mirror of
https://github.com/DioxusLabs/dioxus
synced 2025-02-18 14:48:26 +00:00
wip: error boundary
This commit is contained in:
parent
112c954e00
commit
0e5a59f9ed
17 changed files with 343 additions and 316 deletions
|
@ -33,11 +33,10 @@ indexmap = "1.7"
|
||||||
|
|
||||||
# Serialize the Edits for use in Webview/Liveview instances
|
# Serialize the Edits for use in Webview/Liveview instances
|
||||||
serde = { version = "1", features = ["derive"], optional = true }
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
anyhow = "1.0.66"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "*", features = ["full"] }
|
tokio = { version = "*", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
dioxus = { path = "../dioxus" }
|
dioxus = { path = "../dioxus" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::cell::Cell;
|
|
||||||
|
|
||||||
use crate::factory::RenderReturn;
|
use crate::factory::RenderReturn;
|
||||||
use crate::innerlude::{Mutations, VComponent, VFragment, VText};
|
use crate::innerlude::{Mutations, VComponent, VFragment, VText};
|
||||||
use crate::mutations::Mutation;
|
use crate::mutations::Mutation;
|
||||||
|
@ -7,7 +5,7 @@ use crate::mutations::Mutation::*;
|
||||||
use crate::nodes::VNode;
|
use crate::nodes::VNode;
|
||||||
use crate::nodes::{DynamicNode, TemplateNode};
|
use crate::nodes::{DynamicNode, TemplateNode};
|
||||||
use crate::virtual_dom::VirtualDom;
|
use crate::virtual_dom::VirtualDom;
|
||||||
use crate::{AttributeValue, ElementId, ScopeId, SuspenseContext, TemplateAttribute};
|
use crate::{AttributeValue, ScopeId, SuspenseContext, TemplateAttribute};
|
||||||
|
|
||||||
impl VirtualDom {
|
impl VirtualDom {
|
||||||
/// Create a new template [`VNode`] and write it to the [`Mutations`] buffer.
|
/// Create a new template [`VNode`] and write it to the [`Mutations`] buffer.
|
||||||
|
@ -71,12 +69,6 @@ impl VirtualDom {
|
||||||
mutations.push(CreateTextNode { value, id });
|
mutations.push(CreateTextNode { value, id });
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
DynamicNode::Placeholder(slot) => {
|
|
||||||
let id = self.next_element(template, template.template.node_paths[*id]);
|
|
||||||
slot.set(id);
|
|
||||||
mutations.push(CreatePlaceholder { id });
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -247,8 +239,7 @@ impl VirtualDom {
|
||||||
use DynamicNode::*;
|
use DynamicNode::*;
|
||||||
match node {
|
match node {
|
||||||
Text(text) => self.create_dynamic_text(mutations, template, text, idx),
|
Text(text) => self.create_dynamic_text(mutations, template, text, idx),
|
||||||
Placeholder(slot) => self.create_placeholder(template, idx, slot, mutations),
|
Fragment(frag) => self.create_fragment(frag, template, idx, mutations),
|
||||||
Fragment(frag) => self.create_fragment(frag, mutations),
|
|
||||||
Component(component) => self.create_component_node(mutations, template, component, idx),
|
Component(component) => self.create_component_node(mutations, template, component, idx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,37 +268,35 @@ impl VirtualDom {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_placeholder(
|
pub(crate) fn create_fragment<'a>(
|
||||||
&mut self,
|
|
||||||
template: &VNode,
|
|
||||||
idx: usize,
|
|
||||||
slot: &Cell<ElementId>,
|
|
||||||
mutations: &mut Mutations,
|
|
||||||
) -> usize {
|
|
||||||
// Allocate a dynamic element reference for this text node
|
|
||||||
let id = self.next_element(template, template.template.node_paths[idx]);
|
|
||||||
|
|
||||||
// Make sure the text node is assigned to the correct element
|
|
||||||
slot.set(id);
|
|
||||||
|
|
||||||
// Assign the ID to the existing node in the template
|
|
||||||
mutations.push(AssignId {
|
|
||||||
path: &template.template.node_paths[idx][1..],
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Since the placeholder is already in the DOM, we don't create any new nodes
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_fragment<'a>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
frag: &'a VFragment<'a>,
|
frag: &'a VFragment<'a>,
|
||||||
|
template: &'a VNode<'a>,
|
||||||
|
idx: usize,
|
||||||
mutations: &mut Mutations<'a>,
|
mutations: &mut Mutations<'a>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
frag.nodes
|
match frag {
|
||||||
.iter()
|
VFragment::NonEmpty(nodes) => nodes
|
||||||
.fold(0, |acc, child| acc + self.create(mutations, child))
|
.iter()
|
||||||
|
.fold(0, |acc, child| acc + self.create(mutations, child)),
|
||||||
|
|
||||||
|
VFragment::Empty(slot) => {
|
||||||
|
// Allocate a dynamic element reference for this text node
|
||||||
|
let id = self.next_element(template, template.template.node_paths[idx]);
|
||||||
|
|
||||||
|
// Make sure the text node is assigned to the correct element
|
||||||
|
slot.set(id);
|
||||||
|
|
||||||
|
// Assign the ID to the existing node in the template
|
||||||
|
mutations.push(AssignId {
|
||||||
|
path: &template.template.node_paths[idx][1..],
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Since the placeholder is already in the DOM, we don't create any new nodes
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_component_node<'a>(
|
fn create_component_node<'a>(
|
||||||
|
@ -330,10 +319,9 @@ impl VirtualDom {
|
||||||
use RenderReturn::*;
|
use RenderReturn::*;
|
||||||
|
|
||||||
match return_nodes {
|
match return_nodes {
|
||||||
Sync(Some(t)) => self.mount_component(mutations, scope, t, idx),
|
Sync(Ok(t)) => self.mount_component(mutations, scope, t, idx),
|
||||||
Sync(None) | Async(_) => {
|
Sync(Err(_e)) => todo!("Propogate error upwards"),
|
||||||
self.mount_component_placeholder(template, idx, scope, mutations)
|
Async(_) => self.mount_component_placeholder(template, idx, scope, mutations),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,24 +21,6 @@ use crate::{
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use slab::Slab;
|
use slab::Slab;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct DirtyScope {
|
|
||||||
pub height: u32,
|
|
||||||
pub id: ScopeId,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for DirtyScope {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
Some(self.height.cmp(&other.height))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for DirtyScope {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.height.cmp(&other.height)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'b> VirtualDom {
|
impl<'b> VirtualDom {
|
||||||
pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
|
pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) {
|
||||||
let scope_state = &mut self.scopes[scope.0];
|
let scope_state = &mut self.scopes[scope.0];
|
||||||
|
@ -78,25 +60,26 @@ impl<'b> VirtualDom {
|
||||||
use RenderReturn::{Async, Sync};
|
use RenderReturn::{Async, Sync};
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
// diff
|
// diff
|
||||||
(Sync(Some(l)), Sync(Some(r))) => self.diff_vnode(m, l, r),
|
(Sync(Ok(l)), Sync(Ok(r))) => self.diff_vnode(m, l, r),
|
||||||
|
|
||||||
// remove old with placeholder
|
_ => todo!("handle diffing nonstandard nodes"),
|
||||||
(Sync(Some(l)), Sync(None)) | (Sync(Some(l)), Async(_)) => {
|
// // remove old with placeholder
|
||||||
//
|
// (Sync(Ok(l)), Sync(None)) | (Sync(Ok(l)), Async(_)) => {
|
||||||
let id = self.next_element(l, &[]); // todo!
|
// //
|
||||||
m.push(Mutation::CreatePlaceholder { id });
|
// let id = self.next_element(l, &[]); // todo!
|
||||||
self.drop_template(m, l, true);
|
// m.push(Mutation::CreatePlaceholder { id });
|
||||||
}
|
// self.drop_template(m, l, true);
|
||||||
|
// }
|
||||||
|
|
||||||
// remove placeholder with nodes
|
// // remove placeholder with nodes
|
||||||
(Sync(None), Sync(Some(_))) => {}
|
// (Sync(None), Sync(Ok(_))) => {}
|
||||||
(Async(_), Sync(Some(v))) => {}
|
// (Async(_), Sync(Ok(v))) => {}
|
||||||
|
|
||||||
// nothing... just transfer the placeholders over
|
// // nothing... just transfer the placeholders over
|
||||||
(Async(_), Async(_))
|
// (Async(_), Async(_))
|
||||||
| (Sync(None), Sync(None))
|
// | (Sync(None), Sync(None))
|
||||||
| (Sync(None), Async(_))
|
// | (Sync(None), Async(_))
|
||||||
| (Async(_), Sync(None)) => {}
|
// | (Async(_), Sync(None)) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,15 +103,19 @@ impl<'b> VirtualDom {
|
||||||
.mounted_element
|
.mounted_element
|
||||||
.set(left_attr.mounted_element.get());
|
.set(left_attr.mounted_element.get());
|
||||||
|
|
||||||
if left_attr.value != right_attr.value {
|
if left_attr.value != right_attr.value || left_attr.volatile {
|
||||||
// todo: add more types of attribute values
|
// todo: add more types of attribute values
|
||||||
if let AttributeValue::Text(text) = right_attr.value {
|
match right_attr.value {
|
||||||
muts.push(Mutation::SetAttribute {
|
AttributeValue::Text(text) => {
|
||||||
id: left_attr.mounted_element.get(),
|
muts.push(Mutation::SetAttribute {
|
||||||
name: left_attr.name,
|
id: left_attr.mounted_element.get(),
|
||||||
value: text,
|
name: left_attr.name,
|
||||||
ns: right_attr.namespace,
|
value: text,
|
||||||
});
|
ns: right_attr.namespace,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// todo: more types of attribute values
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,10 +126,9 @@ impl<'b> VirtualDom {
|
||||||
.zip(right_template.dynamic_nodes.iter())
|
.zip(right_template.dynamic_nodes.iter())
|
||||||
{
|
{
|
||||||
match (left_node, right_node) {
|
match (left_node, right_node) {
|
||||||
(Component(left), Component(right)) => self.diff_vcomponent(muts, left, right),
|
|
||||||
(Text(left), Text(right)) => self.diff_vtext(muts, left, right),
|
(Text(left), Text(right)) => self.diff_vtext(muts, left, right),
|
||||||
(Fragment(left), Fragment(right)) => self.diff_vfragment(muts, left, right),
|
(Fragment(left), Fragment(right)) => self.diff_vfragment(muts, left, right),
|
||||||
(Placeholder(left), Placeholder(right)) => right.set(left.get()),
|
(Component(left), Component(right)) => self.diff_vcomponent(muts, left, right),
|
||||||
_ => self.replace(muts, left_template, right_template, left_node, right_node),
|
_ => self.replace(muts, left_template, right_template, left_node, right_node),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -168,17 +154,6 @@ impl<'b> VirtualDom {
|
||||||
// this codepath is through "light_diff", but we check there that the pointers are the same
|
// this codepath is through "light_diff", but we check there that the pointers are the same
|
||||||
assert_eq!(left.render_fn, right.render_fn);
|
assert_eq!(left.render_fn, right.render_fn);
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let left = rsx!{ Component {} }
|
|
||||||
let right = rsx!{ Component {} }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Make sure the new vcomponent has the right scopeid associated to it
|
// Make sure the new vcomponent has the right scopeid associated to it
|
||||||
let scope_id = left.scope.get().unwrap();
|
let scope_id = left.scope.get().unwrap();
|
||||||
right.scope.set(Some(scope_id));
|
right.scope.set(Some(scope_id));
|
||||||
|
@ -276,36 +251,63 @@ impl<'b> VirtualDom {
|
||||||
left: &'b VFragment<'b>,
|
left: &'b VFragment<'b>,
|
||||||
right: &'b VFragment<'b>,
|
right: &'b VFragment<'b>,
|
||||||
) {
|
) {
|
||||||
// match (left.nodes, right.nodes) {
|
use VFragment::*;
|
||||||
// ([], []) => rp.set(lp.get()),
|
match (left, right) {
|
||||||
// ([], _) => {
|
(Empty(l), Empty(r)) => r.set(l.get()),
|
||||||
// //
|
(Empty(l), NonEmpty(r)) => self.replace_placeholder_with_nodes(muts, l, r),
|
||||||
// todo!()
|
(NonEmpty(l), Empty(r)) => self.replace_nodes_with_placeholder(muts, l, r),
|
||||||
// }
|
(NonEmpty(old), NonEmpty(new)) => self.diff_non_empty_fragment(new, old, muts),
|
||||||
// (_, []) => {
|
}
|
||||||
// // if this fragment is the only child of its parent, then we can use the "RemoveAllChildren" mutation
|
}
|
||||||
// todo!()
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// let new_is_keyed = new[0].key.is_some();
|
|
||||||
// let old_is_keyed = old[0].key.is_some();
|
|
||||||
|
|
||||||
// debug_assert!(
|
fn replace_placeholder_with_nodes(
|
||||||
// new.iter().all(|n| n.key.is_some() == new_is_keyed),
|
&mut self,
|
||||||
// "all siblings must be keyed or all siblings must be non-keyed"
|
muts: &mut Mutations<'b>,
|
||||||
// );
|
l: &'b std::cell::Cell<ElementId>,
|
||||||
// debug_assert!(
|
r: &'b [VNode<'b>],
|
||||||
// old.iter().all(|o| o.key.is_some() == old_is_keyed),
|
) {
|
||||||
// "all siblings must be keyed or all siblings must be non-keyed"
|
let created = r
|
||||||
// );
|
.iter()
|
||||||
|
.fold(0, |acc, child| acc + self.create(muts, child));
|
||||||
|
muts.push(Mutation::ReplaceWith {
|
||||||
|
id: l.get(),
|
||||||
|
m: created,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// if new_is_keyed && old_is_keyed {
|
fn replace_nodes_with_placeholder(
|
||||||
// self.diff_keyed_children(muts, old, new);
|
&mut self,
|
||||||
// } else {
|
muts: &mut Mutations<'b>,
|
||||||
// self.diff_non_keyed_children(muts, old, new);
|
l: &'b [VNode<'b>],
|
||||||
// }
|
r: &'b std::cell::Cell<ElementId>,
|
||||||
// }
|
) {
|
||||||
// }
|
//
|
||||||
|
|
||||||
|
// Remove the old nodes, except for one
|
||||||
|
self.remove_nodes(muts, &l[1..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff_non_empty_fragment(
|
||||||
|
&mut self,
|
||||||
|
new: &'b [VNode<'b>],
|
||||||
|
old: &'b [VNode<'b>],
|
||||||
|
muts: &mut Mutations<'b>,
|
||||||
|
) {
|
||||||
|
let new_is_keyed = new[0].key.is_some();
|
||||||
|
let old_is_keyed = old[0].key.is_some();
|
||||||
|
debug_assert!(
|
||||||
|
new.iter().all(|n| n.key.is_some() == new_is_keyed),
|
||||||
|
"all siblings must be keyed or all siblings must be non-keyed"
|
||||||
|
);
|
||||||
|
debug_assert!(
|
||||||
|
old.iter().all(|o| o.key.is_some() == old_is_keyed),
|
||||||
|
"all siblings must be keyed or all siblings must be non-keyed"
|
||||||
|
);
|
||||||
|
if new_is_keyed && old_is_keyed {
|
||||||
|
// self.diff_keyed_children(muts, old, new);
|
||||||
|
} else {
|
||||||
|
self.diff_non_keyed_children(muts, old, new);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diff children that are not keyed.
|
// Diff children that are not keyed.
|
||||||
|
@ -330,8 +332,9 @@ impl<'b> VirtualDom {
|
||||||
|
|
||||||
match old.len().cmp(&new.len()) {
|
match old.len().cmp(&new.len()) {
|
||||||
Ordering::Greater => self.remove_nodes(muts, &old[new.len()..]),
|
Ordering::Greater => self.remove_nodes(muts, &old[new.len()..]),
|
||||||
Ordering::Less => todo!(),
|
Ordering::Less => {
|
||||||
// Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()),
|
self.create_and_insert_after(muts, &new[old.len()..], old.last().unwrap())
|
||||||
|
}
|
||||||
Ordering::Equal => {}
|
Ordering::Equal => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,95 +343,95 @@ impl<'b> VirtualDom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diffing "keyed" children.
|
// // Diffing "keyed" children.
|
||||||
//
|
// //
|
||||||
// With keyed children, we care about whether we delete, move, or create nodes
|
// // With keyed children, we care about whether we delete, move, or create nodes
|
||||||
// versus mutate existing nodes in place. Presumably there is some sort of CSS
|
// // versus mutate existing nodes in place. Presumably there is some sort of CSS
|
||||||
// transition animation that makes the virtual DOM diffing algorithm
|
// // transition animation that makes the virtual DOM diffing algorithm
|
||||||
// observable. By specifying keys for nodes, we know which virtual DOM nodes
|
// // observable. By specifying keys for nodes, we know which virtual DOM nodes
|
||||||
// must reuse (or not reuse) the same physical DOM nodes.
|
// // must reuse (or not reuse) the same physical DOM nodes.
|
||||||
//
|
// //
|
||||||
// This is loosely based on Inferno's keyed patching implementation. However, we
|
// // This is loosely based on Inferno's keyed patching implementation. However, we
|
||||||
// have to modify the algorithm since we are compiling the diff down into change
|
// // have to modify the algorithm since we are compiling the diff down into change
|
||||||
// list instructions that will be executed later, rather than applying the
|
// // list instructions that will be executed later, rather than applying the
|
||||||
// changes to the DOM directly as we compare virtual DOMs.
|
// // changes to the DOM directly as we compare virtual DOMs.
|
||||||
//
|
// //
|
||||||
// https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
|
// // https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
|
||||||
//
|
// //
|
||||||
// The stack is empty upon entry.
|
// // The stack is empty upon entry.
|
||||||
fn diff_keyed_children(
|
// fn diff_keyed_children(
|
||||||
&mut self,
|
// &mut self,
|
||||||
muts: &mut Mutations<'b>,
|
// muts: &mut Mutations<'b>,
|
||||||
old: &'b [VNode<'b>],
|
// old: &'b [VNode<'b>],
|
||||||
new: &'b [VNode<'b>],
|
// new: &'b [VNode<'b>],
|
||||||
) {
|
// ) {
|
||||||
// if cfg!(debug_assertions) {
|
// if cfg!(debug_assertions) {
|
||||||
// let mut keys = fxhash::FxHashSet::default();
|
// let mut keys = fxhash::FxHashSet::default();
|
||||||
// let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
|
// let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
|
||||||
// keys.clear();
|
// keys.clear();
|
||||||
// for child in children {
|
// for child in children {
|
||||||
// let key = child.key;
|
// let key = child.key;
|
||||||
// debug_assert!(
|
// debug_assert!(
|
||||||
// key.is_some(),
|
// key.is_some(),
|
||||||
// "if any sibling is keyed, all siblings must be keyed"
|
// "if any sibling is keyed, all siblings must be keyed"
|
||||||
// );
|
// );
|
||||||
// keys.insert(key);
|
// keys.insert(key);
|
||||||
// }
|
// }
|
||||||
// debug_assert_eq!(
|
// debug_assert_eq!(
|
||||||
// children.len(),
|
// children.len(),
|
||||||
// keys.len(),
|
// keys.len(),
|
||||||
// "keyed siblings must each have a unique key"
|
// "keyed siblings must each have a unique key"
|
||||||
// );
|
// );
|
||||||
// };
|
// };
|
||||||
// assert_unique_keys(old);
|
// assert_unique_keys(old);
|
||||||
// assert_unique_keys(new);
|
// assert_unique_keys(new);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // First up, we diff all the nodes with the same key at the beginning of the
|
// // First up, we diff all the nodes with the same key at the beginning of the
|
||||||
// // children.
|
// // children.
|
||||||
// //
|
// //
|
||||||
// // `shared_prefix_count` is the count of how many nodes at the start of
|
// // `shared_prefix_count` is the count of how many nodes at the start of
|
||||||
// // `new` and `old` share the same keys.
|
// // `new` and `old` share the same keys.
|
||||||
// let (left_offset, right_offset) = match self.diff_keyed_ends(muts, old, new) {
|
// let (left_offset, right_offset) = match self.diff_keyed_ends(muts, old, new) {
|
||||||
// Some(count) => count,
|
// Some(count) => count,
|
||||||
// None => return,
|
// None => return,
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// // Ok, we now hopefully have a smaller range of children in the middle
|
// // Ok, we now hopefully have a smaller range of children in the middle
|
||||||
// // within which to re-order nodes with the same keys, remove old nodes with
|
// // within which to re-order nodes with the same keys, remove old nodes with
|
||||||
// // now-unused keys, and create new nodes with fresh keys.
|
// // now-unused keys, and create new nodes with fresh keys.
|
||||||
|
|
||||||
// let old_middle = &old[left_offset..(old.len() - right_offset)];
|
// let old_middle = &old[left_offset..(old.len() - right_offset)];
|
||||||
// let new_middle = &new[left_offset..(new.len() - right_offset)];
|
// let new_middle = &new[left_offset..(new.len() - right_offset)];
|
||||||
|
|
||||||
// debug_assert!(
|
// debug_assert!(
|
||||||
// !((old_middle.len() == new_middle.len()) && old_middle.is_empty()),
|
// !((old_middle.len() == new_middle.len()) && old_middle.is_empty()),
|
||||||
// "keyed children must have the same number of children"
|
// "keyed children must have the same number of children"
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// if new_middle.is_empty() {
|
// if new_middle.is_empty() {
|
||||||
// // remove the old elements
|
// // remove the old elements
|
||||||
// self.remove_nodes(muts, old_middle);
|
// self.remove_nodes(muts, old_middle);
|
||||||
// } else if old_middle.is_empty() {
|
// } else if old_middle.is_empty() {
|
||||||
// // there were no old elements, so just create the new elements
|
// // there were no old elements, so just create the new elements
|
||||||
// // we need to find the right "foothold" though - we shouldn't use the "append" at all
|
// // we need to find the right "foothold" though - we shouldn't use the "append" at all
|
||||||
// if left_offset == 0 {
|
// if left_offset == 0 {
|
||||||
// // insert at the beginning of the old list
|
// // insert at the beginning of the old list
|
||||||
// let foothold = &old[old.len() - right_offset];
|
// let foothold = &old[old.len() - right_offset];
|
||||||
// self.create_and_insert_before(new_middle, foothold);
|
// self.create_and_insert_before(muts, new_middle, foothold);
|
||||||
// } else if right_offset == 0 {
|
// } else if right_offset == 0 {
|
||||||
// // insert at the end the old list
|
// // insert at the end the old list
|
||||||
// let foothold = old.last().unwrap();
|
// let foothold = old.last().unwrap();
|
||||||
// self.create_and_insert_after(new_middle, foothold);
|
// self.create_and_insert_after(muts, new_middle, foothold);
|
||||||
// } else {
|
// } else {
|
||||||
// // inserting in the middle
|
// // inserting in the middle
|
||||||
// let foothold = &old[left_offset - 1];
|
// let foothold = &old[left_offset - 1];
|
||||||
// self.create_and_insert_after(new_middle, foothold);
|
// self.create_and_insert_after(muts, new_middle, foothold);
|
||||||
// }
|
// }
|
||||||
// } else {
|
// } else {
|
||||||
// self.diff_keyed_middle(muts, old_middle, new_middle);
|
// self.diff_keyed_middle(muts, old_middle, new_middle);
|
||||||
// }
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// /// Diff both ends of the children that share keys.
|
// /// Diff both ends of the children that share keys.
|
||||||
// ///
|
// ///
|
||||||
|
@ -437,7 +440,7 @@ impl<'b> VirtualDom {
|
||||||
// /// If there is no offset, then this function returns None and the diffing is complete.
|
// /// If there is no offset, then this function returns None and the diffing is complete.
|
||||||
// fn diff_keyed_ends(
|
// fn diff_keyed_ends(
|
||||||
// &mut self,
|
// &mut self,
|
||||||
// muts: &mut Renderer<'b>,
|
// muts: &mut Mutations<'b>,
|
||||||
// old: &'b [VNode<'b>],
|
// old: &'b [VNode<'b>],
|
||||||
// new: &'b [VNode<'b>],
|
// new: &'b [VNode<'b>],
|
||||||
// ) -> Option<(usize, usize)> {
|
// ) -> Option<(usize, usize)> {
|
||||||
|
@ -496,7 +499,7 @@ impl<'b> VirtualDom {
|
||||||
// #[allow(clippy::too_many_lines)]
|
// #[allow(clippy::too_many_lines)]
|
||||||
// fn diff_keyed_middle(
|
// fn diff_keyed_middle(
|
||||||
// &mut self,
|
// &mut self,
|
||||||
// muts: &mut Renderer<'b>,
|
// muts: &mut Mutations<'b>,
|
||||||
// old: &'b [VNode<'b>],
|
// old: &'b [VNode<'b>],
|
||||||
// new: &'b [VNode<'b>],
|
// new: &'b [VNode<'b>],
|
||||||
// ) {
|
// ) {
|
||||||
|
@ -677,6 +680,37 @@ impl<'b> VirtualDom {
|
||||||
fn remove_nodes(&mut self, muts: &mut Mutations<'b>, nodes: &'b [VNode<'b>]) {
|
fn remove_nodes(&mut self, muts: &mut Mutations<'b>, nodes: &'b [VNode<'b>]) {
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push all the real nodes on the stack
|
||||||
|
fn push_elements_onto_stack(&mut self, node: &VNode) -> usize {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_and_insert_before(
|
||||||
|
&self,
|
||||||
|
mutations: &mut Mutations<'b>,
|
||||||
|
new: &[VNode],
|
||||||
|
after: &VNode,
|
||||||
|
) {
|
||||||
|
let id = self.get_last_real_node(after);
|
||||||
|
}
|
||||||
|
pub(crate) fn create_and_insert_after(
|
||||||
|
&self,
|
||||||
|
mutations: &mut Mutations<'b>,
|
||||||
|
new: &[VNode],
|
||||||
|
after: &VNode,
|
||||||
|
) {
|
||||||
|
let id = self.get_last_real_node(after);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_last_real_node(&self, node: &VNode) -> ElementId {
|
||||||
|
match node.template.roots.last().unwrap() {
|
||||||
|
TemplateNode::Element { .. } => todo!(),
|
||||||
|
TemplateNode::Text(t) => todo!(),
|
||||||
|
TemplateNode::Dynamic(_) => todo!(),
|
||||||
|
TemplateNode::DynamicText(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matching_components<'a>(
|
fn matching_components<'a>(
|
||||||
|
|
19
packages/core/src/dirty_scope.rs
Normal file
19
packages/core/src/dirty_scope.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::ScopeId;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct DirtyScope {
|
||||||
|
pub height: u32,
|
||||||
|
pub id: ScopeId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for DirtyScope {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.height.cmp(&other.height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for DirtyScope {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.height.cmp(&other.height)
|
||||||
|
}
|
||||||
|
}
|
29
packages/core/src/error_boundary.rs
Normal file
29
packages/core/src/error_boundary.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{ScopeId, ScopeState};
|
||||||
|
|
||||||
|
pub struct ErrorContext {
|
||||||
|
error: RefCell<Option<(anyhow::Error, ScopeId)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Catch all errors from the children and bubble them up to this component
|
||||||
|
///
|
||||||
|
/// Returns the error and scope that caused the error
|
||||||
|
pub fn use_catch_error(cx: &ScopeState) -> Option<&(anyhow::Error, ScopeId)> {
|
||||||
|
let err_ctx = use_error_context(cx);
|
||||||
|
|
||||||
|
let out = cx.use_hook(|| None);
|
||||||
|
|
||||||
|
if let Some(error) = err_ctx.error.take() {
|
||||||
|
*out = Some(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new error context at this component.
|
||||||
|
///
|
||||||
|
/// This component will start to catch any errors that occur in its children.
|
||||||
|
pub fn use_error_context(cx: &ScopeState) -> &ErrorContext {
|
||||||
|
cx.use_hook(|| cx.provide_context(Rc::new(ErrorContext { error: None.into() })))
|
||||||
|
}
|
|
@ -150,8 +150,7 @@ pub trait IntoDynNode<'a, A = ()> {
|
||||||
|
|
||||||
impl<'a, 'b> IntoDynNode<'a> for () {
|
impl<'a, 'b> IntoDynNode<'a> for () {
|
||||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||||
todo!()
|
DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0))))
|
||||||
// self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a, 'b> IntoDynNode<'a> for VNode<'a> {
|
impl<'a, 'b> IntoDynNode<'a> for VNode<'a> {
|
||||||
|
@ -165,23 +164,14 @@ impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for Option<T> {
|
||||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||||
match self {
|
match self {
|
||||||
Some(val) => val.into_vnode(_cx),
|
Some(val) => val.into_vnode(_cx),
|
||||||
None => DynamicNode::Placeholder(Default::default()),
|
None => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, T: IntoDynNode<'a>> IntoDynNode<'a> for &Option<T> {
|
|
||||||
fn into_vnode(self, _cx: &'a ScopeState) -> DynamicNode<'a> {
|
|
||||||
// DynamicNode::Fragment { nodes: cx., inner: () }
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
|
impl<'a, 'b> IntoDynNode<'a> for LazyNodes<'a, 'b> {
|
||||||
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
fn into_vnode(self, cx: &'a ScopeState) -> DynamicNode<'a> {
|
||||||
DynamicNode::Fragment(VFragment {
|
DynamicNode::Fragment(VFragment::NonEmpty(cx.bump().alloc([self.call(cx)])))
|
||||||
nodes: cx.bump().alloc([self.call(cx)]),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,8 +239,8 @@ where
|
||||||
let children = nodes.into_bump_slice();
|
let children = nodes.into_bump_slice();
|
||||||
|
|
||||||
match children.len() {
|
match children.len() {
|
||||||
0 => DynamicNode::Placeholder(Cell::new(ElementId(0))),
|
0 => DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
||||||
_ => DynamicNode::Fragment(VFragment { nodes: children }),
|
_ => DynamicNode::Fragment(VFragment::NonEmpty(children)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ use crate::innerlude::*;
|
||||||
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
|
||||||
#[allow(non_upper_case_globals, non_snake_case)]
|
#[allow(non_upper_case_globals, non_snake_case)]
|
||||||
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
pub fn Fragment<'a>(cx: Scope<'a, FragmentProps<'a>>) -> Element {
|
||||||
let children = cx.props.0.as_ref()?;
|
let children = cx.props.0.as_ref().unwrap();
|
||||||
Some(VNode {
|
Ok(VNode {
|
||||||
node_id: children.node_id.clone(),
|
node_id: children.node_id.clone(),
|
||||||
key: children.key.clone(),
|
key: children.key.clone(),
|
||||||
parent: children.parent.clone(),
|
parent: children.parent.clone(),
|
||||||
|
@ -96,7 +96,7 @@ impl<'a> Properties for FragmentProps<'a> {
|
||||||
type Builder = FragmentBuilder<'a, false>;
|
type Builder = FragmentBuilder<'a, false>;
|
||||||
const IS_STATIC: bool = false;
|
const IS_STATIC: bool = false;
|
||||||
fn builder() -> Self::Builder {
|
fn builder() -> Self::Builder {
|
||||||
FragmentBuilder(None)
|
todo!()
|
||||||
}
|
}
|
||||||
unsafe fn memoize(&self, _other: &Self) -> bool {
|
unsafe fn memoize(&self, _other: &Self) -> bool {
|
||||||
false
|
false
|
||||||
|
|
|
@ -3,6 +3,8 @@ mod arena;
|
||||||
mod bump_frame;
|
mod bump_frame;
|
||||||
mod create;
|
mod create;
|
||||||
mod diff;
|
mod diff;
|
||||||
|
mod dirty_scope;
|
||||||
|
mod error_boundary;
|
||||||
mod events;
|
mod events;
|
||||||
mod factory;
|
mod factory;
|
||||||
mod fragment;
|
mod fragment;
|
||||||
|
@ -15,9 +17,9 @@ mod scheduler;
|
||||||
mod scope_arena;
|
mod scope_arena;
|
||||||
mod scopes;
|
mod scopes;
|
||||||
mod virtual_dom;
|
mod virtual_dom;
|
||||||
|
|
||||||
pub(crate) mod innerlude {
|
pub(crate) mod innerlude {
|
||||||
pub use crate::arena::*;
|
pub use crate::arena::*;
|
||||||
|
pub use crate::dirty_scope::*;
|
||||||
pub use crate::events::*;
|
pub use crate::events::*;
|
||||||
pub use crate::fragment::*;
|
pub use crate::fragment::*;
|
||||||
pub use crate::lazynodes::*;
|
pub use crate::lazynodes::*;
|
||||||
|
@ -28,10 +30,10 @@ pub(crate) mod innerlude {
|
||||||
pub use crate::scopes::*;
|
pub use crate::scopes::*;
|
||||||
pub use crate::virtual_dom::*;
|
pub use crate::virtual_dom::*;
|
||||||
|
|
||||||
/// An [`Element`] is a possibly-none [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
|
/// An [`Element`] is a possibly-errored [`VNode`] created by calling `render` on [`Scope`] or [`ScopeState`].
|
||||||
///
|
///
|
||||||
/// Any [`None`] [`Element`] will automatically be coerced into a placeholder [`VNode`] with the [`VNode::Placeholder`] variant.
|
/// An Errored [`Element`] will propagate the error to the nearest error boundary.
|
||||||
pub type Element<'a> = Option<VNode<'a>>;
|
pub type Element<'a> = anyhow::Result<VNode<'a>>;
|
||||||
|
|
||||||
/// A [`Component`] is a function that takes a [`Scope`] and returns an [`Element`].
|
/// A [`Component`] is a function that takes a [`Scope`] and returns an [`Element`].
|
||||||
///
|
///
|
||||||
|
|
|
@ -2,7 +2,6 @@ use crate::{any_props::AnyProps, arena::ElementId, ScopeId, ScopeState, UiEvent}
|
||||||
use std::{
|
use std::{
|
||||||
any::{Any, TypeId},
|
any::{Any, TypeId},
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
hash::Hasher,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type TemplateId = &'static str;
|
pub type TemplateId = &'static str;
|
||||||
|
@ -42,7 +41,7 @@ impl<'a> VNode<'a> {
|
||||||
pub fn placeholder_template(cx: &'a ScopeState) -> Self {
|
pub fn placeholder_template(cx: &'a ScopeState) -> Self {
|
||||||
Self::template_from_dynamic_node(
|
Self::template_from_dynamic_node(
|
||||||
cx,
|
cx,
|
||||||
DynamicNode::Placeholder(Cell::new(ElementId(0))),
|
DynamicNode::Fragment(VFragment::Empty(Cell::new(ElementId(0)))),
|
||||||
"dioxus-placeholder",
|
"dioxus-placeholder",
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -52,8 +51,8 @@ impl<'a> VNode<'a> {
|
||||||
cx: &'a ScopeState,
|
cx: &'a ScopeState,
|
||||||
node: DynamicNode<'a>,
|
node: DynamicNode<'a>,
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
) -> Option<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
Some(VNode {
|
Ok(VNode {
|
||||||
node_id: Cell::new(ElementId(0)),
|
node_id: Cell::new(ElementId(0)),
|
||||||
key: None,
|
key: None,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
@ -73,8 +72,8 @@ impl<'a> VNode<'a> {
|
||||||
_cx: &'a ScopeState,
|
_cx: &'a ScopeState,
|
||||||
text: &'static [TemplateNode<'static>],
|
text: &'static [TemplateNode<'static>],
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
) -> Option<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
Some(VNode {
|
Ok(VNode {
|
||||||
node_id: Cell::new(ElementId(0)),
|
node_id: Cell::new(ElementId(0)),
|
||||||
key: None,
|
key: None,
|
||||||
parent: None,
|
parent: None,
|
||||||
|
@ -99,18 +98,6 @@ pub struct Template<'a> {
|
||||||
pub attr_paths: &'a [&'a [u8]],
|
pub attr_paths: &'a [&'a [u8]],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::hash::Hash for Template<'a> {
|
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
||||||
self.id.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Eq for Template<'_> {}
|
|
||||||
impl PartialEq for Template<'_> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A weird-ish variant of VNodes with way more limited types
|
/// A weird-ish variant of VNodes with way more limited types
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum TemplateNode<'a> {
|
pub enum TemplateNode<'a> {
|
||||||
|
@ -131,7 +118,6 @@ pub enum DynamicNode<'a> {
|
||||||
Component(VComponent<'a>),
|
Component(VComponent<'a>),
|
||||||
Text(VText<'a>),
|
Text(VText<'a>),
|
||||||
Fragment(VFragment<'a>),
|
Fragment(VFragment<'a>),
|
||||||
Placeholder(Cell<ElementId>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DynamicNode<'a> {
|
impl<'a> DynamicNode<'a> {
|
||||||
|
@ -165,8 +151,9 @@ pub struct VText<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VFragment<'a> {
|
pub enum VFragment<'a> {
|
||||||
pub nodes: &'a [VNode<'a>],
|
Empty(Cell<ElementId>),
|
||||||
|
NonEmpty(&'a [VNode<'a>]),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -72,7 +72,7 @@ impl VirtualDom {
|
||||||
|
|
||||||
fiber.waiting_on.borrow_mut().remove(&id);
|
fiber.waiting_on.borrow_mut().remove(&id);
|
||||||
|
|
||||||
if let RenderReturn::Sync(Some(template)) = ret {
|
if let RenderReturn::Sync(Ok(template)) = ret {
|
||||||
let mutations_ref = &mut fiber.mutations.borrow_mut();
|
let mutations_ref = &mut fiber.mutations.borrow_mut();
|
||||||
let mutations = &mut **mutations_ref;
|
let mutations = &mut **mutations_ref;
|
||||||
let template: &VNode = unsafe { std::mem::transmute(template) };
|
let template: &VNode = unsafe { std::mem::transmute(template) };
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
any_props::AnyProps,
|
any_props::AnyProps,
|
||||||
bump_frame::BumpFrame,
|
bump_frame::BumpFrame,
|
||||||
diff::DirtyScope,
|
|
||||||
factory::RenderReturn,
|
factory::RenderReturn,
|
||||||
|
innerlude::DirtyScope,
|
||||||
innerlude::{SuspenseId, SuspenseLeaf},
|
innerlude::{SuspenseId, SuspenseLeaf},
|
||||||
scheduler::RcWake,
|
scheduler::RcWake,
|
||||||
scopes::{ScopeId, ScopeState},
|
scopes::{ScopeId, ScopeState},
|
||||||
|
@ -48,13 +48,6 @@ impl VirtualDom {
|
||||||
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
|
.and_then(|id| self.scopes.get_mut(id.0).map(|f| f as *mut ScopeState))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn run_scope_extend<'a>(
|
|
||||||
&mut self,
|
|
||||||
scope_id: ScopeId,
|
|
||||||
) -> &'a RenderReturn<'a> {
|
|
||||||
unsafe { self.run_scope(scope_id).extend_lifetime_ref() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
|
pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn {
|
||||||
let mut new_nodes = unsafe {
|
let mut new_nodes = unsafe {
|
||||||
let scope = &mut self.scopes[scope_id.0];
|
let scope = &mut self.scopes[scope_id.0];
|
||||||
|
|
|
@ -5,8 +5,7 @@ use crate::{
|
||||||
factory::RenderReturn,
|
factory::RenderReturn,
|
||||||
innerlude::{Scheduler, SchedulerMsg},
|
innerlude::{Scheduler, SchedulerMsg},
|
||||||
lazynodes::LazyNodes,
|
lazynodes::LazyNodes,
|
||||||
nodes::VNode,
|
Element, TaskId,
|
||||||
TaskId,
|
|
||||||
};
|
};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
@ -118,7 +117,7 @@ impl ScopeState {
|
||||||
///
|
///
|
||||||
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
/// This is useful for traversing the tree outside of the VirtualDom, such as in a custom renderer or in SSR.
|
||||||
pub fn root_node<'a>(&'a self) -> &'a RenderReturn<'a> {
|
pub fn root_node<'a>(&'a self) -> &'a RenderReturn<'a> {
|
||||||
let r = unsafe { &*self.current_frame().node.get() };
|
let r: &RenderReturn = unsafe { &*self.current_frame().node.get() };
|
||||||
unsafe { std::mem::transmute(r) }
|
unsafe { std::mem::transmute(r) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,8 +324,8 @@ impl ScopeState {
|
||||||
/// cx.render(lazy_tree)
|
/// cx.render(lazy_tree)
|
||||||
/// }
|
/// }
|
||||||
///```
|
///```
|
||||||
pub fn render<'src>(&'src self, rsx: LazyNodes<'src, '_>) -> Option<VNode<'src>> {
|
pub fn render<'src>(&'src self, rsx: LazyNodes<'src, '_>) -> Element<'src> {
|
||||||
Some(rsx.call(self))
|
Ok(rsx.call(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store a value between renders. The foundational hook for all other hooks.
|
/// Store a value between renders. The foundational hook for all other hooks.
|
||||||
|
|
|
@ -28,31 +28,3 @@ pub struct Subtree {
|
||||||
root: ScopeId,
|
root: ScopeId,
|
||||||
elements: Slab<ElementPath>,
|
elements: Slab<ElementPath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn app(cx: Scope) -> Element {
|
|
||||||
// // whenever a user connects, they get a new connection
|
|
||||||
// // this requires the virtualdom to be Send + Sync
|
|
||||||
// rsx! {
|
|
||||||
// ClientForEach(|req| rsx!{
|
|
||||||
// Route {}
|
|
||||||
// Route {}
|
|
||||||
// Route {}
|
|
||||||
// Route {}
|
|
||||||
// Route {}
|
|
||||||
// Route {}
|
|
||||||
// })
|
|
||||||
|
|
||||||
// // windows.map(|w| {
|
|
||||||
// // WebviewWindow {}
|
|
||||||
// // WebviewWindow {}
|
|
||||||
// // WebviewWindow {}
|
|
||||||
// // WebviewWindow {}
|
|
||||||
// // })
|
|
||||||
|
|
||||||
// // if show_settings {
|
|
||||||
// // WebviewWindow {
|
|
||||||
// // Settings {}
|
|
||||||
// // }
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -4,11 +4,9 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any_props::VProps,
|
any_props::VProps,
|
||||||
arena::ElementId,
|
arena::{ElementId, ElementRef},
|
||||||
arena::ElementRef,
|
|
||||||
diff::DirtyScope,
|
|
||||||
factory::RenderReturn,
|
factory::RenderReturn,
|
||||||
innerlude::{Mutations, Scheduler, SchedulerMsg},
|
innerlude::{DirtyScope, Mutations, Scheduler, SchedulerMsg},
|
||||||
mutations::Mutation,
|
mutations::Mutation,
|
||||||
nodes::{Template, TemplateId},
|
nodes::{Template, TemplateId},
|
||||||
scheduler::{SuspenseBoundary, SuspenseId},
|
scheduler::{SuspenseBoundary, SuspenseId},
|
||||||
|
@ -469,20 +467,17 @@ impl VirtualDom {
|
||||||
///
|
///
|
||||||
/// apply_edits(edits);
|
/// apply_edits(edits);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn rebuild<'a>(&'a mut self) -> Mutations<'a> {
|
pub fn rebuild(&mut self) -> Mutations {
|
||||||
let mut mutations = Mutations::new(0);
|
let mut mutations = Mutations::new(0);
|
||||||
|
|
||||||
match unsafe { self.run_scope_extend(ScopeId(0)) } {
|
match unsafe { self.run_scope(ScopeId(0)).extend_lifetime_ref() } {
|
||||||
// Rebuilding implies we append the created elements to the root
|
// Rebuilding implies we append the created elements to the root
|
||||||
RenderReturn::Sync(Some(node)) => {
|
RenderReturn::Sync(Ok(node)) => {
|
||||||
let m = self.create_scope(ScopeId(0), &mut mutations, node);
|
let m = self.create_scope(ScopeId(0), &mut mutations, node);
|
||||||
mutations.push(Mutation::AppendChildren { m });
|
mutations.push(Mutation::AppendChildren { m });
|
||||||
}
|
}
|
||||||
// If nothing was rendered, then insert a placeholder element instead
|
// If an error occurs, we should try to render the default error component and context where the error occured
|
||||||
RenderReturn::Sync(None) => {
|
RenderReturn::Sync(Err(e)) => panic!("Cannot catch errors during rebuild {:?}", e),
|
||||||
mutations.push(Mutation::CreatePlaceholder { id: ElementId(1) });
|
|
||||||
mutations.push(Mutation::AppendChildren { m: 1 });
|
|
||||||
}
|
|
||||||
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
|
RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
packages/core/tests/element.rs
Normal file
20
packages/core/tests/element.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_core::SuspenseContext;
|
||||||
|
|
||||||
|
/// Ensure no issues with not building the virtualdom before
|
||||||
|
#[test]
|
||||||
|
fn root_node_isnt_null() {
|
||||||
|
let dom = VirtualDom::new(|cx| render!("Hello world!"));
|
||||||
|
|
||||||
|
let scope = dom.base_scope();
|
||||||
|
|
||||||
|
// The root should be a valid pointer
|
||||||
|
assert_ne!(scope.root_node() as *const _, std::ptr::null_mut());
|
||||||
|
|
||||||
|
// The height should be 0
|
||||||
|
assert_eq!(scope.height(), 0);
|
||||||
|
|
||||||
|
// There should be a default suspense context
|
||||||
|
// todo: there should also be a default error boundary
|
||||||
|
assert!(scope.has_context::<SuspenseContext>().is_some());
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
//! Verify that tasks get polled by the virtualdom properly, and that we escape wait_for_work safely
|
//! Verify that tasks get polled by the virtualdom properly, and that we escape wait_for_work safely
|
||||||
|
|
||||||
use dioxus_core::*;
|
use dioxus::prelude::*;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
@ -11,7 +11,7 @@ async fn it_works() {
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = dom.wait_for_work() => {}
|
_ = dom.wait_for_work() => {}
|
||||||
_ = tokio::time::sleep(Duration::from_millis(1000)) => {}
|
_ = tokio::time::sleep(Duration::from_millis(600)) => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,5 +32,5 @@ fn app(cx: Scope) -> Element {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
None
|
cx.render(rsx!(()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl ToTokens for CallBody {
|
||||||
|
|
||||||
if self.inline_cx {
|
if self.inline_cx {
|
||||||
out_tokens.append_all(quote! {
|
out_tokens.append_all(quote! {
|
||||||
Some({
|
Ok({
|
||||||
let __cx = cx;
|
let __cx = cx;
|
||||||
#body
|
#body
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Reference in a new issue