wip: back to vnode enum

This commit is contained in:
Jonathan Kelley committed 2021-08-20 10:34:41 -04:00
1 parent 9652ccdcf1
commit 64f289a61c
6 files changed
+213 -226

No files matched your search

+107 -103
View File
@@ -85,7 +85,9 @@ use DomEdit::*;
/// was origially implemented using recursive techniques, but Rust lacks the abilty to call async functions recursively, /// was origially implemented using recursive techniques, but Rust lacks the abilty to call async functions recursively,
/// meaning we could not "pause" the diffing algorithm. /// meaning we could not "pause" the diffing algorithm.
/// ///
/// Instead, we use a traditional stack machine approach to diff and create new nodes. /// Instead, we use a traditional stack machine approach to diff and create new nodes. The diff algorithm periodically
/// calls "yield_now" which allows the machine to pause and return control to the caller. The caller can then wait for
/// the next period of idle time, preventing our diff algorithm from blocking the maint thread.
pub struct DiffMachine<'bump> { pub struct DiffMachine<'bump> {
vdom: &'bump SharedResources, vdom: &'bump SharedResources,
@@ -187,27 +189,27 @@ impl<'bump> DiffMachine<'bump> {
} }
DiffInstruction::Create { node, .. } => { DiffInstruction::Create { node, .. } => {
match &node.kind { match &node {
VNodeKind::Text(text) => { VNode::Text(text) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_text_node(text.text, real_id); self.edit_create_text_node(text.text, real_id);
text.dom_id.set(Some(real_id)); text.dom_id.set(Some(real_id));
*self.nodes_created_stack.last_mut().unwrap() += 1; *self.nodes_created_stack.last_mut().unwrap() += 1;
} }
VNodeKind::Suspended(suspended) => { VNode::Suspended(suspended) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_placeholder(real_id); self.edit_create_placeholder(real_id);
suspended.node.set(Some(real_id)); suspended.node.set(Some(real_id));
*self.nodes_created_stack.last_mut().unwrap() += 1; *self.nodes_created_stack.last_mut().unwrap() += 1;
} }
VNodeKind::Anchor(anchor) => { VNode::Anchor(anchor) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_placeholder(real_id); self.edit_create_placeholder(real_id);
anchor.dom_id.set(Some(real_id)); anchor.dom_id.set(Some(real_id));
*self.nodes_created_stack.last_mut().unwrap() += 1; *self.nodes_created_stack.last_mut().unwrap() += 1;
} }
VNodeKind::Element(el) => { VNode::Element(el) => {
let VElement { let VElement {
tag_name, tag_name,
listeners, listeners,
@@ -218,6 +220,7 @@ impl<'bump> DiffMachine<'bump> {
static_children: _, static_children: _,
static_listeners: _, static_listeners: _,
dom_id, dom_id,
key,
} = el; } = el;
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
@@ -250,13 +253,13 @@ impl<'bump> DiffMachine<'bump> {
} }
} }
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
for node in frag.children { for node in frag.children {
self.node_stack.push(DiffInstruction::Create { node }) self.node_stack.push(DiffInstruction::Create { node })
} }
} }
VNodeKind::Component(_) => { VNode::Component(_) => {
// //
} }
} }
@@ -267,50 +270,52 @@ impl<'bump> DiffMachine<'bump> {
Ok(()) Ok(())
} }
fn create_text_node(&mut self) {}
/// Create the new node, pushing instructions on our instruction stack to create any further children /// Create the new node, pushing instructions on our instruction stack to create any further children
/// ///
/// ///
pub fn create_iterative(&mut self, node: &'bump VNode<'bump>) { pub fn create_iterative(&mut self, node: &'bump VNode<'bump>) {
match &node.kind { match &node {
// singles // singles
// update the parent // update the parent
VNodeKind::Text(text) => { VNode::Text(text) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_text_node(text.text, real_id); self.edit_create_text_node(text.text, real_id);
text.dom_id.set(Some(real_id)); text.dom_id.set(Some(real_id));
*self.nodes_created_stack.last_mut().unwrap() += 1; *self.nodes_created_stack.last_mut().unwrap() += 1;
} }
VNodeKind::Suspended(suspended) => { VNode::Suspended(suspended) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_placeholder(real_id); self.edit_create_placeholder(real_id);
suspended.node.set(Some(real_id)); suspended.node.set(Some(real_id));
*self.nodes_created_stack.last_mut().unwrap() += 1; *self.nodes_created_stack.last_mut().unwrap() += 1;
} }
VNodeKind::Anchor(anchor) => { VNode::Anchor(anchor) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_placeholder(real_id); self.edit_create_placeholder(real_id);
anchor.dom_id.set(Some(real_id)); anchor.dom_id.set(Some(real_id));
*self.nodes_created_stack.last_mut().unwrap() += 1; *self.nodes_created_stack.last_mut().unwrap() += 1;
} }
VNodeKind::Element(el) => { VNode::Element(el) => {
// //
} }
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
// //
} }
VNodeKind::Component(comp) => { VNode::Component(comp) => {
// //
} }
} }
} }
pub fn diff_iterative(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) { pub fn diff_iterative(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
match (&old_node.kind, &new_node.kind) { match (&old_node, &new_node) {
// Handle the "sane" cases first. // Handle the "sane" cases first.
// The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment". // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
// So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable. // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
(VNodeKind::Text(old), VNodeKind::Text(new)) => { (VNode::Text(old), VNode::Text(new)) => {
let root = old_node.direct_id(); let root = old_node.direct_id();
if old.text != new.text { if old.text != new.text {
@@ -322,7 +327,7 @@ impl<'bump> DiffMachine<'bump> {
new.dom_id.set(Some(root)); new.dom_id.set(Some(root));
} }
(VNodeKind::Element(old), VNodeKind::Element(new)) => { (VNode::Element(old), VNode::Element(new)) => {
let root = old_node.direct_id(); let root = old_node.direct_id();
// If the element type is completely different, the element needs to be re-rendered completely // If the element type is completely different, the element needs to be re-rendered completely
@@ -409,7 +414,7 @@ impl<'bump> DiffMachine<'bump> {
self.diff_children(old.children, new.children); self.diff_children(old.children, new.children);
} }
(VNodeKind::Component(old), VNodeKind::Component(new)) => { (VNode::Component(old), VNode::Component(new)) => {
let scope_addr = old.ass_scope.get().unwrap(); let scope_addr = old.ass_scope.get().unwrap();
// Make sure we're dealing with the same component (by function pointer) // Make sure we're dealing with the same component (by function pointer)
@@ -464,7 +469,7 @@ impl<'bump> DiffMachine<'bump> {
} }
} }
(VNodeKind::Fragment(old), VNodeKind::Fragment(new)) => { (VNode::Fragment(old), VNode::Fragment(new)) => {
// This is the case where options or direct vnodes might be used. // This is the case where options or direct vnodes might be used.
// In this case, it's faster to just skip ahead to their diff // In this case, it's faster to just skip ahead to their diff
if old.children.len() == 1 && new.children.len() == 1 { if old.children.len() == 1 && new.children.len() == 1 {
@@ -475,7 +480,7 @@ impl<'bump> DiffMachine<'bump> {
self.diff_children(old.children, new.children); self.diff_children(old.children, new.children);
} }
(VNodeKind::Anchor(old), VNodeKind::Anchor(new)) => { (VNode::Anchor(old), VNode::Anchor(new)) => {
new.dom_id.set(old.dom_id.get()); new.dom_id.set(old.dom_id.get());
} }
@@ -487,27 +492,27 @@ impl<'bump> DiffMachine<'bump> {
// This likely isn't the fastest way to go about replacing one node with a virtual node, but the "insane" cases // This likely isn't the fastest way to go about replacing one node with a virtual node, but the "insane" cases
// are pretty rare. IE replacing a list (component or fragment) with a single node. // are pretty rare. IE replacing a list (component or fragment) with a single node.
( (
VNodeKind::Component(_) VNode::Component(_)
| VNodeKind::Fragment(_) | VNode::Fragment(_)
| VNodeKind::Text(_) | VNode::Text(_)
| VNodeKind::Element(_) | VNode::Element(_)
| VNodeKind::Anchor(_), | VNode::Anchor(_),
VNodeKind::Component(_) VNode::Component(_)
| VNodeKind::Fragment(_) | VNode::Fragment(_)
| VNodeKind::Text(_) | VNode::Text(_)
| VNodeKind::Element(_) | VNode::Element(_)
| VNodeKind::Anchor(_), | VNode::Anchor(_),
) => { ) => {
self.replace_and_create_many_with_many([old_node], [new_node]); self.replace_and_create_many_with_many([old_node], [new_node]);
} }
// TODO // TODO
(VNodeKind::Suspended(old), new) => { (VNode::Suspended(old), new) => {
// //
self.replace_and_create_many_with_many([old_node], [new_node]); self.replace_and_create_many_with_many([old_node], [new_node]);
} }
// a node that was once real is now suspended // a node that was once real is now suspended
(old, VNodeKind::Suspended(_)) => { (old, VNode::Suspended(_)) => {
// //
self.replace_and_create_many_with_many([old_node], [new_node]); self.replace_and_create_many_with_many([old_node], [new_node]);
} }
@@ -521,11 +526,11 @@ impl<'bump> DiffMachine<'bump> {
// //
// each function call assumes the stack is fresh (empty). // each function call assumes the stack is fresh (empty).
pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) { pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) {
match (&old_node.kind, &new_node.kind) { match (&old_node, &new_node) {
// Handle the "sane" cases first. // Handle the "sane" cases first.
// The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment". // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment".
// So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable. // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable.
(VNodeKind::Text(old), VNodeKind::Text(new)) => { (VNode::Text(old), VNode::Text(new)) => {
let root = old_node.direct_id(); let root = old_node.direct_id();
if old.text != new.text { if old.text != new.text {
@@ -537,7 +542,7 @@ impl<'bump> DiffMachine<'bump> {
new.dom_id.set(Some(root)); new.dom_id.set(Some(root));
} }
(VNodeKind::Element(old), VNodeKind::Element(new)) => { (VNode::Element(old), VNode::Element(new)) => {
let root = old_node.direct_id(); let root = old_node.direct_id();
// If the element type is completely different, the element needs to be re-rendered completely // If the element type is completely different, the element needs to be re-rendered completely
@@ -624,7 +629,7 @@ impl<'bump> DiffMachine<'bump> {
self.diff_children(old.children, new.children); self.diff_children(old.children, new.children);
} }
(VNodeKind::Component(old), VNodeKind::Component(new)) => { (VNode::Component(old), VNode::Component(new)) => {
let scope_addr = old.ass_scope.get().unwrap(); let scope_addr = old.ass_scope.get().unwrap();
// Make sure we're dealing with the same component (by function pointer) // Make sure we're dealing with the same component (by function pointer)
@@ -679,7 +684,7 @@ impl<'bump> DiffMachine<'bump> {
} }
} }
(VNodeKind::Fragment(old), VNodeKind::Fragment(new)) => { (VNode::Fragment(old), VNode::Fragment(new)) => {
// This is the case where options or direct vnodes might be used. // This is the case where options or direct vnodes might be used.
// In this case, it's faster to just skip ahead to their diff // In this case, it's faster to just skip ahead to their diff
if old.children.len() == 1 && new.children.len() == 1 { if old.children.len() == 1 && new.children.len() == 1 {
@@ -690,7 +695,7 @@ impl<'bump> DiffMachine<'bump> {
self.diff_children(old.children, new.children); self.diff_children(old.children, new.children);
} }
(VNodeKind::Anchor(old), VNodeKind::Anchor(new)) => { (VNode::Anchor(old), VNode::Anchor(new)) => {
new.dom_id.set(old.dom_id.get()); new.dom_id.set(old.dom_id.get());
} }
@@ -702,27 +707,27 @@ impl<'bump> DiffMachine<'bump> {
// This likely isn't the fastest way to go about replacing one node with a virtual node, but the "insane" cases // This likely isn't the fastest way to go about replacing one node with a virtual node, but the "insane" cases
// are pretty rare. IE replacing a list (component or fragment) with a single node. // are pretty rare. IE replacing a list (component or fragment) with a single node.
( (
VNodeKind::Component(_) VNode::Component(_)
| VNodeKind::Fragment(_) | VNode::Fragment(_)
| VNodeKind::Text(_) | VNode::Text(_)
| VNodeKind::Element(_) | VNode::Element(_)
| VNodeKind::Anchor(_), | VNode::Anchor(_),
VNodeKind::Component(_) VNode::Component(_)
| VNodeKind::Fragment(_) | VNode::Fragment(_)
| VNodeKind::Text(_) | VNode::Text(_)
| VNodeKind::Element(_) | VNode::Element(_)
| VNodeKind::Anchor(_), | VNode::Anchor(_),
) => { ) => {
self.replace_and_create_many_with_many([old_node], [new_node]); self.replace_and_create_many_with_many([old_node], [new_node]);
} }
// TODO // TODO
(VNodeKind::Suspended(old), new) => { (VNode::Suspended(old), new) => {
// //
self.replace_and_create_many_with_many([old_node], [new_node]); self.replace_and_create_many_with_many([old_node], [new_node]);
} }
// a node that was once real is now suspended // a node that was once real is now suspended
(old, VNodeKind::Suspended(_)) => { (old, VNode::Suspended(_)) => {
// //
self.replace_and_create_many_with_many([old_node], [new_node]); self.replace_and_create_many_with_many([old_node], [new_node]);
} }
@@ -739,22 +744,22 @@ impl<'bump> DiffMachine<'bump> {
// //
// [... node] // [... node]
pub fn create_vnode(&mut self, node: &'bump VNode<'bump>) -> CreateMeta { pub fn create_vnode(&mut self, node: &'bump VNode<'bump>) -> CreateMeta {
match &node.kind { match &node {
VNodeKind::Text(text) => { VNode::Text(text) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_text_node(text.text, real_id); self.edit_create_text_node(text.text, real_id);
text.dom_id.set(Some(real_id)); text.dom_id.set(Some(real_id));
CreateMeta::new(text.is_static, 1) CreateMeta::new(text.is_static, 1)
} }
VNodeKind::Anchor(anchor) => { VNode::Anchor(anchor) => {
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
self.edit_create_placeholder(real_id); self.edit_create_placeholder(real_id);
anchor.dom_id.set(Some(real_id)); anchor.dom_id.set(Some(real_id));
CreateMeta::new(false, 1) CreateMeta::new(false, 1)
} }
VNodeKind::Element(el) => { VNode::Element(el) => {
// we have the potential to completely eliminate working on this node in the future(!) // we have the potential to completely eliminate working on this node in the future(!)
// //
// This can only be done if all of the elements properties (attrs, children, listeners, etc) are static // This can only be done if all of the elements properties (attrs, children, listeners, etc) are static
@@ -772,6 +777,7 @@ impl<'bump> DiffMachine<'bump> {
static_children: _, static_children: _,
static_listeners: _, static_listeners: _,
dom_id, dom_id,
key,
} = el; } = el;
let real_id = self.vdom.reserve_node(); let real_id = self.vdom.reserve_node();
@@ -805,7 +811,7 @@ impl<'bump> DiffMachine<'bump> {
// //
// TODO move over // TODO move over
// if children.len() == 1 { // if children.len() == 1 {
// if let VNodeKind::Text(text) = &children[0].kind { // if let VNodeKind::Text(text) = &children[0] {
// self.set_text(text.text); // self.set_text(text.text);
// return CreateMeta::new(is_static, 1); // return CreateMeta::new(is_static, 1);
// } // }
@@ -822,7 +828,7 @@ impl<'bump> DiffMachine<'bump> {
CreateMeta::new(is_static, 1) CreateMeta::new(is_static, 1)
} }
VNodeKind::Component(vcomponent) => { VNode::Component(vcomponent) => {
let caller = vcomponent.caller.clone(); let caller = vcomponent.caller.clone();
let parent_idx = self.scope_stack.last().unwrap().clone(); let parent_idx = self.scope_stack.last().unwrap().clone();
@@ -895,9 +901,9 @@ impl<'bump> DiffMachine<'bump> {
// Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators). // Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators).
// We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them. // We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them.
// Fragments will just put all their nodes onto the stack after creation // Fragments will just put all their nodes onto the stack after creation
VNodeKind::Fragment(frag) => self.create_children(frag.children), VNode::Fragment(frag) => self.create_children(frag.children),
VNodeKind::Suspended(VSuspended { node: real_node }) => { VNode::Suspended(VSuspended { node: real_node }) => {
let id = self.vdom.reserve_node(); let id = self.vdom.reserve_node();
self.edit_create_placeholder(id); self.edit_create_placeholder(id);
real_node.set(Some(id)); real_node.set(Some(id));
@@ -993,14 +999,14 @@ impl<'bump> DiffMachine<'bump> {
let first_old = &old[0]; let first_old = &old[0];
let first_new = &new[0]; let first_new = &new[0];
match (&first_old.kind, &first_new.kind) { match (&first_old, &first_new) {
// Anchors can only appear in empty fragments // Anchors can only appear in empty fragments
(VNodeKind::Anchor(old_anchor), VNodeKind::Anchor(new_anchor)) => { (VNode::Anchor(old_anchor), VNode::Anchor(new_anchor)) => {
old_anchor.dom_id.set(new_anchor.dom_id.get()); old_anchor.dom_id.set(new_anchor.dom_id.get());
} }
// Replace the anchor with whatever new nodes are coming down the pipe // Replace the anchor with whatever new nodes are coming down the pipe
(VNodeKind::Anchor(anchor), _) => { (VNode::Anchor(anchor), _) => {
self.edit_push_root(anchor.dom_id.get().unwrap()); self.edit_push_root(anchor.dom_id.get().unwrap());
let mut added = 0; let mut added = 0;
for el in new { for el in new {
@@ -1011,21 +1017,21 @@ impl<'bump> DiffMachine<'bump> {
} }
// Replace whatever nodes are sitting there with the anchor // Replace whatever nodes are sitting there with the anchor
(_, VNodeKind::Anchor(anchor)) => { (_, VNode::Anchor(anchor)) => {
self.replace_and_create_many_with_many(old, [first_new]); self.replace_and_create_many_with_many(old, [first_new]);
} }
// Use the complex diff algorithm to diff the nodes // Use the complex diff algorithm to diff the nodes
_ => { _ => {
let new_is_keyed = new[0].key.is_some(); let new_is_keyed = new[0].key().is_some();
let old_is_keyed = old[0].key.is_some(); let old_is_keyed = old[0].key().is_some();
debug_assert!( debug_assert!(
new.iter().all(|n| n.key.is_some() == new_is_keyed), new.iter().all(|n| n.key().is_some() == new_is_keyed),
"all siblings must be keyed or all siblings must be non-keyed" "all siblings must be keyed or all siblings must be non-keyed"
); );
debug_assert!( debug_assert!(
old.iter().all(|o| o.key.is_some() == old_is_keyed), old.iter().all(|o| o.key().is_some() == old_is_keyed),
"all siblings must be keyed or all siblings must be non-keyed" "all siblings must be keyed or all siblings must be non-keyed"
); );
@@ -1062,7 +1068,7 @@ impl<'bump> DiffMachine<'bump> {
let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| { let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| {
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"
@@ -1102,7 +1108,7 @@ impl<'bump> DiffMachine<'bump> {
.iter() .iter()
.rev() .rev()
.zip(new[shared_prefix_count..].iter().rev()) .zip(new[shared_prefix_count..].iter().rev())
.take_while(|&(old, new)| old.key == new.key) .take_while(|&(old, new)| old.key() == new.key())
.count(); .count();
let old_shared_suffix_start = old.len() - shared_suffix_count; let old_shared_suffix_start = old.len() - shared_suffix_count;
@@ -1543,17 +1549,16 @@ impl<'bump> DiffMachine<'bump> {
loop { loop {
let node = search_node.take().unwrap(); let node = search_node.take().unwrap();
match &node.kind { match &node {
// the ones that have a direct id // the ones that have a direct id
VNodeKind::Text(_) VNode::Text(_) | VNode::Element(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
| VNodeKind::Element(_) break node
| VNodeKind::Anchor(_) }
| VNodeKind::Suspended(_) => break node,
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
search_node = frag.children.last(); search_node = frag.children.last();
} }
VNodeKind::Component(el) => { VNode::Component(el) => {
let scope_id = el.ass_scope.get().unwrap(); let scope_id = el.ass_scope.get().unwrap();
let scope = self.get_scope(&scope_id).unwrap(); let scope = self.get_scope(&scope_id).unwrap();
search_node = Some(scope.root()); search_node = Some(scope.root());
@@ -1567,17 +1572,16 @@ impl<'bump> DiffMachine<'bump> {
loop { loop {
let node = search_node.take().unwrap(); let node = search_node.take().unwrap();
match &node.kind { match &node {
// the ones that have a direct id // the ones that have a direct id
VNodeKind::Text(_) VNode::Text(_) | VNode::Element(_) | VNode::Anchor(_) | VNode::Suspended(_) => {
| VNodeKind::Element(_) break node
| VNodeKind::Anchor(_) }
| VNodeKind::Suspended(_) => break node,
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
search_node = Some(&frag.children[0]); search_node = Some(&frag.children[0]);
} }
VNodeKind::Component(el) => { VNode::Component(el) => {
let scope_id = el.ass_scope.get().unwrap(); let scope_id = el.ass_scope.get().unwrap();
let scope = self.get_scope(&scope_id).unwrap(); let scope = self.get_scope(&scope_id).unwrap();
search_node = Some(scope.root()); search_node = Some(scope.root());
@@ -1602,15 +1606,15 @@ impl<'bump> DiffMachine<'bump> {
let mut nodes_to_search = old_nodes.into_iter().collect::<Vec<_>>(); let mut nodes_to_search = old_nodes.into_iter().collect::<Vec<_>>();
let mut scopes_obliterated = Vec::new(); let mut scopes_obliterated = Vec::new();
while let Some(node) = nodes_to_search.pop() { while let Some(node) = nodes_to_search.pop() {
match &node.kind { match &node {
// the ones that have a direct id return immediately // the ones that have a direct id return immediately
VNodeKind::Text(el) => nodes_to_replace.push(el.dom_id.get().unwrap()), VNode::Text(el) => nodes_to_replace.push(el.dom_id.get().unwrap()),
VNodeKind::Element(el) => nodes_to_replace.push(el.dom_id.get().unwrap()), VNode::Element(el) => nodes_to_replace.push(el.dom_id.get().unwrap()),
VNodeKind::Anchor(el) => nodes_to_replace.push(el.dom_id.get().unwrap()), VNode::Anchor(el) => nodes_to_replace.push(el.dom_id.get().unwrap()),
VNodeKind::Suspended(el) => nodes_to_replace.push(el.node.get().unwrap()), VNode::Suspended(el) => nodes_to_replace.push(el.node.get().unwrap()),
// Fragments will either have a single anchor or a list of children // Fragments will either have a single anchor or a list of children
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
for child in frag.children { for child in frag.children {
nodes_to_search.push(child); nodes_to_search.push(child);
} }
@@ -1618,7 +1622,7 @@ impl<'bump> DiffMachine<'bump> {
// Components can be any of the nodes above // Components can be any of the nodes above
// However, we do need to track which components need to be removed // However, we do need to track which components need to be removed
VNodeKind::Component(el) => { VNode::Component(el) => {
let scope_id = el.ass_scope.get().unwrap(); let scope_id = el.ass_scope.get().unwrap();
let scope = self.get_scope(&scope_id).unwrap(); let scope = self.get_scope(&scope_id).unwrap();
let root = scope.root(); let root = scope.root();
@@ -1680,27 +1684,27 @@ impl<'bump> DiffMachine<'bump> {
} }
fn remove_vnode(&mut self, node: &'bump VNode<'bump>) { fn remove_vnode(&mut self, node: &'bump VNode<'bump>) {
match &node.kind { match &node {
VNodeKind::Text(el) => self.immediately_dispose_garabage(node.direct_id()), VNode::Text(el) => self.immediately_dispose_garabage(node.direct_id()),
VNodeKind::Element(el) => { VNode::Element(el) => {
self.immediately_dispose_garabage(node.direct_id()); self.immediately_dispose_garabage(node.direct_id());
for child in el.children { for child in el.children {
self.remove_vnode(&child); self.remove_vnode(&child);
} }
} }
VNodeKind::Anchor(a) => { VNode::Anchor(a) => {
// //
} }
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
for child in frag.children { for child in frag.children {
self.remove_vnode(&child); self.remove_vnode(&child);
} }
} }
VNodeKind::Component(el) => { VNode::Component(el) => {
// //
// self.destroy_scopes(old_scope) // self.destroy_scopes(old_scope)
} }
VNodeKind::Suspended(_) => todo!(), VNode::Suspended(_) => todo!(),
} }
} }
@@ -1941,10 +1945,10 @@ impl<'a> Iterator for RealChildIterator<'a> {
while returned_node.is_none() { while returned_node.is_none() {
if let Some((count, node)) = self.stack.last_mut() { if let Some((count, node)) = self.stack.last_mut() {
match &node.kind { match &node {
// We can only exit our looping when we get "real" nodes // We can only exit our looping when we get "real" nodes
// This includes fragments and components when they're empty (have a single root) // This includes fragments and components when they're empty (have a single root)
VNodeKind::Element(_) | VNodeKind::Text(_) => { VNode::Element(_) | VNode::Text(_) => {
// We've recursed INTO an element/text // We've recursed INTO an element/text
// We need to recurse *out* of it and move forward to the next // We need to recurse *out* of it and move forward to the next
should_pop = true; should_pop = true;
@@ -1952,7 +1956,7 @@ impl<'a> Iterator for RealChildIterator<'a> {
} }
// If we get a fragment we push the next child // If we get a fragment we push the next child
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
let subcount = *count as usize; let subcount = *count as usize;
if frag.children.len() == 0 { if frag.children.len() == 0 {
@@ -1983,17 +1987,17 @@ impl<'a> Iterator for RealChildIterator<'a> {
// } // }
// Immediately abort suspended nodes - can't do anything with them yet // Immediately abort suspended nodes - can't do anything with them yet
VNodeKind::Suspended(node) => { VNode::Suspended(node) => {
// VNodeKind::Suspended => should_pop = true, // VNodeKind::Suspended => should_pop = true,
todo!() todo!()
} }
VNodeKind::Anchor(a) => { VNode::Anchor(a) => {
todo!() todo!()
} }
// For components, we load their root and push them onto the stack // For components, we load their root and push them onto the stack
VNodeKind::Component(sc) => { VNode::Component(sc) => {
let scope = let scope =
unsafe { self.scopes.get_scope(sc.ass_scope.get().unwrap()) }.unwrap(); unsafe { self.scopes.get_scope(sc.ass_scope.get().unwrap()) }.unwrap();
// let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap(); // let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
+3 -6
View File
@@ -191,12 +191,9 @@ where
} }
None => { None => {
// //
Some(VNode { Some(VNode::Suspended(VSuspended {
key: None, node: domnode.clone(),
kind: VNodeKind::Suspended(VSuspended { }))
node: domnode.clone(),
}),
})
} }
} }
}); });
+2 -2
View File
@@ -12,8 +12,8 @@
pub use crate::innerlude::{ pub use crate::innerlude::{
format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority, format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority,
EventTrigger, LazyNodes, NodeFactory, Properties, ScopeId, SuspendedContext, VNode, VNodeKind, EventTrigger, LazyNodes, NodeFactory, Properties, ScopeId, SuspendedContext, VNode, VirtualDom,
VirtualDom, VirtualEvent, FC, VirtualEvent, FC,
}; };
pub mod prelude { pub mod prelude {
+88 -102
View File
@@ -16,37 +16,12 @@ use std::{
rc::Rc, rc::Rc,
}; };
pub struct VNode<'src> {
pub kind: VNodeKind<'src>,
pub(crate) key: Option<&'src str>,
}
impl<'src> VNode<'src> {
pub fn key(&self) -> Option<&'src str> {
self.key
}
pub fn direct_id(&self) -> ElementId {
self.try_direct_id().unwrap()
}
pub fn try_direct_id(&self) -> Option<ElementId> {
match &self.kind {
VNodeKind::Text(el) => el.dom_id.get(),
VNodeKind::Element(el) => el.dom_id.get(),
VNodeKind::Anchor(el) => el.dom_id.get(),
VNodeKind::Fragment(_) => None,
VNodeKind::Component(_) => None,
VNodeKind::Suspended(_) => None,
}
}
}
/// Tools for the base unit of the virtual dom - the VNode /// Tools for the base unit of the virtual dom - the VNode
/// VNodes are intended to be quickly-allocated, lightweight enum values. /// VNodes are intended to be quickly-allocated, lightweight enum values.
/// ///
/// Components will be generating a lot of these very quickly, so we want to /// Components will be generating a lot of these very quickly, so we want to
/// limit the amount of heap allocations / overly large enum sizes. /// limit the amount of heap allocations / overly large enum sizes.
pub enum VNodeKind<'src> { pub enum VNode<'src> {
Text(VText<'src>), Text(VText<'src>),
Element(&'src VElement<'src>), Element(&'src VElement<'src>),
@@ -60,6 +35,32 @@ pub enum VNodeKind<'src> {
Anchor(VAnchor), Anchor(VAnchor),
} }
impl<'src> VNode<'src> {
pub fn key(&self) -> Option<&'src str> {
match &self {
VNode::Text(_) => todo!(),
VNode::Element(_) => todo!(),
VNode::Fragment(_) => todo!(),
VNode::Component(_) => todo!(),
VNode::Suspended(_) => todo!(),
VNode::Anchor(_) => todo!(),
}
}
pub fn direct_id(&self) -> ElementId {
self.try_direct_id().unwrap()
}
pub fn try_direct_id(&self) -> Option<ElementId> {
match &self {
VNode::Text(el) => el.dom_id.get(),
VNode::Element(el) => el.dom_id.get(),
VNode::Anchor(el) => el.dom_id.get(),
VNode::Fragment(_) => None,
VNode::Component(_) => None,
VNode::Suspended(_) => None,
}
}
}
pub struct VAnchor { pub struct VAnchor {
pub dom_id: Cell<Option<ElementId>>, pub dom_id: Cell<Option<ElementId>>,
} }
@@ -72,6 +73,7 @@ pub struct VText<'src> {
pub struct VFragment<'src> { pub struct VFragment<'src> {
pub children: &'src [VNode<'src>], pub children: &'src [VNode<'src>],
pub key: Option<&'src str>,
pub is_static: bool, pub is_static: bool,
} }
@@ -89,6 +91,8 @@ pub trait DioxusElement {
} }
pub struct VElement<'a> { pub struct VElement<'a> {
pub key: Option<&'a str>,
// tag is always static // tag is always static
pub tag_name: &'static str, pub tag_name: &'static str,
pub namespace: Option<&'static str>, pub namespace: Option<&'static str>,
@@ -144,6 +148,8 @@ impl Listener<'_> {
/// Virtual Components for custom user-defined components /// Virtual Components for custom user-defined components
/// Only supports the functional syntax /// Only supports the functional syntax
pub struct VComponent<'src> { pub struct VComponent<'src> {
pub key: Option<&'src str>,
pub ass_scope: Cell<Option<ScopeId>>, pub ass_scope: Cell<Option<ScopeId>>,
pub(crate) caller: Rc<dyn Fn(&Scope) -> DomTree>, pub(crate) caller: Rc<dyn Fn(&Scope) -> DomTree>,
@@ -196,26 +202,20 @@ impl<'a> NodeFactory<'a> {
} }
pub fn unstable_place_holder() -> VNode<'static> { pub fn unstable_place_holder() -> VNode<'static> {
VNode { VNode::Text(VText {
key: None, text: "",
kind: VNodeKind::Text(VText { dom_id: empty_cell(),
text: "", is_static: true,
dom_id: empty_cell(), })
is_static: true,
}),
}
} }
/// Used in a place or two to make it easier to build vnodes from dummy text /// Used in a place or two to make it easier to build vnodes from dummy text
pub fn static_text(&self, text: &'static str) -> VNode<'a> { pub fn static_text(&self, text: &'static str) -> VNode<'a> {
VNode { VNode::Text(VText {
key: None, dom_id: empty_cell(),
kind: VNodeKind::Text(VText { text,
dom_id: empty_cell(), is_static: true,
text, })
is_static: true,
}),
}
} }
/// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
@@ -237,14 +237,12 @@ impl<'a> NodeFactory<'a> {
/// ///
pub fn text(&self, args: Arguments) -> VNode<'a> { pub fn text(&self, args: Arguments) -> VNode<'a> {
let (text, is_static) = self.raw_text(args); let (text, is_static) = self.raw_text(args);
VNode {
key: None, VNode::Text(VText {
kind: VNodeKind::Text(VText { text,
text, is_static,
is_static, dom_id: empty_cell(),
dom_id: empty_cell(), })
}),
}
} }
pub fn element<L, A, V>( pub fn element<L, A, V>(
@@ -295,31 +293,26 @@ impl<'a> NodeFactory<'a> {
let key = key.map(|f| self.raw_text(f).0); let key = key.map(|f| self.raw_text(f).0);
VNode { VNode::Element(self.bump().alloc(VElement {
tag_name: tag,
key, key,
kind: VNodeKind::Element(self.bump().alloc(VElement { namespace,
tag_name: tag, listeners,
namespace, attributes,
listeners, children,
attributes, dom_id: empty_cell(),
children,
dom_id: empty_cell(),
// todo: wire up more constization // todo: wire up more constization
static_listeners: false, static_listeners: false,
static_attrs: false, static_attrs: false,
static_children: false, static_children: false,
})), }))
}
} }
pub fn suspended() -> VNode<'static> { pub fn suspended() -> VNode<'static> {
VNode { VNode::Suspended(VSuspended {
key: None, node: Rc::new(empty_cell()),
kind: VNodeKind::Suspended(VSuspended { })
node: Rc::new(empty_cell()),
}),
}
} }
pub fn attr( pub fn attr(
@@ -422,20 +415,18 @@ impl<'a> NodeFactory<'a> {
let key = key.map(|f| self.raw_text(f).0); let key = key.map(|f| self.raw_text(f).0);
VNode { VNode::Component(bump.alloc_with(|| VComponent {
user_fc,
comparator,
raw_props,
children,
caller: NodeFactory::create_component_caller(component, raw_props),
is_static,
drop_props: RefCell::new(Some(drop_props)),
can_memoize: P::IS_STATIC,
ass_scope: Cell::new(None),
key, key,
kind: VNodeKind::Component(bump.alloc_with(|| VComponent { }))
user_fc,
comparator,
raw_props,
children,
caller: NodeFactory::create_component_caller(component, raw_props),
is_static,
drop_props: RefCell::new(Some(drop_props)),
can_memoize: P::IS_STATIC,
ass_scope: Cell::new(None),
})),
}
} }
pub fn create_component_caller<'g, P: 'g>( pub fn create_component_caller<'g, P: 'g>(
@@ -463,13 +454,11 @@ impl<'a> NodeFactory<'a> {
pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> { pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
let children = node_iter.into_vnode_list(self); let children = node_iter.into_vnode_list(self);
VNode { VNode::Fragment(VFragment {
children,
key: None, key: None,
kind: VNodeKind::Fragment(VFragment { is_static: false,
children, })
is_static: false,
}),
}
} }
} }
@@ -525,12 +514,9 @@ where
} }
if nodes.len() == 0 { if nodes.len() == 0 {
nodes.push(VNode { nodes.push(VNode::Anchor(VAnchor {
kind: VNodeKind::Anchor(VAnchor { dom_id: empty_cell(),
dom_id: empty_cell(), }));
}),
key: None,
});
} }
nodes.into_bump_slice() nodes.into_bump_slice()
@@ -666,14 +652,14 @@ impl Debug for NodeFactory<'_> {
impl Debug for VNode<'_> { impl Debug for VNode<'_> {
fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match &self.kind { match &self {
VNodeKind::Element(el) => write!(s, "VElement {{ name: {} }}", el.tag_name), VNode::Element(el) => write!(s, "VElement {{ name: {} }}", el.tag_name),
VNodeKind::Text(t) => write!(s, "VText {{ text: {} }}", t.text), VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
VNodeKind::Anchor(a) => write!(s, "VAnchor"), VNode::Anchor(a) => write!(s, "VAnchor"),
VNodeKind::Fragment(_) => write!(s, "fragment"), VNode::Fragment(_) => write!(s, "fragment"),
VNodeKind::Suspended { .. } => write!(s, "suspended"), VNode::Suspended { .. } => write!(s, "suspended"),
VNodeKind::Component(_) => write!(s, "component"), VNode::Component(_) => write!(s, "component"),
} }
} }
} }
+7 -7
View File
@@ -354,31 +354,31 @@ impl Scheduler {
} }
while let Some(node) = garbage_list.pop() { while let Some(node) = garbage_list.pop() {
match &node.kind { match &node {
VNodeKind::Text(_) => { VNode::Text(_) => {
self.shared.collect_garbage(node.direct_id()); self.shared.collect_garbage(node.direct_id());
} }
VNodeKind::Anchor(_) => { VNode::Anchor(_) => {
self.shared.collect_garbage(node.direct_id()); self.shared.collect_garbage(node.direct_id());
} }
VNodeKind::Suspended(_) => { VNode::Suspended(_) => {
self.shared.collect_garbage(node.direct_id()); self.shared.collect_garbage(node.direct_id());
} }
VNodeKind::Element(el) => { VNode::Element(el) => {
self.shared.collect_garbage(node.direct_id()); self.shared.collect_garbage(node.direct_id());
for child in el.children { for child in el.children {
garbage_list.push(child); garbage_list.push(child);
} }
} }
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
for child in frag.children { for child in frag.children {
garbage_list.push(child); garbage_list.push(child);
} }
} }
VNodeKind::Component(comp) => { VNode::Component(comp) => {
// TODO: run the hook destructors and then even delete the scope // TODO: run the hook destructors and then even delete the scope
let scope_id = comp.ass_scope.get().unwrap(); let scope_id = comp.ass_scope.get().unwrap();
+6 -6
View File
@@ -75,7 +75,7 @@ impl<'a> TextRenderer<'a> {
fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result { fn html_render(&self, node: &VNode, f: &mut std::fmt::Formatter, il: u16) -> std::fmt::Result {
match &node.kind { match &node.kind {
VNodeKind::Text(text) => { VNode::Text(text) => {
if self.cfg.indent { if self.cfg.indent {
for _ in 0..il { for _ in 0..il {
write!(f, " ")?; write!(f, " ")?;
@@ -83,7 +83,7 @@ impl<'a> TextRenderer<'a> {
} }
write!(f, "{}", text.text)? write!(f, "{}", text.text)?
} }
VNodeKind::Anchor(anchor) => { VNode::Anchor(anchor) => {
// //
if self.cfg.indent { if self.cfg.indent {
for _ in 0..il { for _ in 0..il {
@@ -92,7 +92,7 @@ impl<'a> TextRenderer<'a> {
} }
write!(f, "<!-- -->")?; write!(f, "<!-- -->")?;
} }
VNodeKind::Element(el) => { VNode::Element(el) => {
if self.cfg.indent { if self.cfg.indent {
for _ in 0..il { for _ in 0..il {
write!(f, " ")?; write!(f, " ")?;
@@ -163,12 +163,12 @@ impl<'a> TextRenderer<'a> {
write!(f, "\n")?; write!(f, "\n")?;
} }
} }
VNodeKind::Fragment(frag) => { VNode::Fragment(frag) => {
for child in frag.children { for child in frag.children {
self.html_render(child, f, il + 1)?; self.html_render(child, f, il + 1)?;
} }
} }
VNodeKind::Component(vcomp) => { VNode::Component(vcomp) => {
let idx = vcomp.ass_scope.get().unwrap(); let idx = vcomp.ass_scope.get().unwrap();
match (self.vdom, self.cfg.skip_components) { match (self.vdom, self.cfg.skip_components) {
(Some(vdom), false) => { (Some(vdom), false) => {
@@ -180,7 +180,7 @@ impl<'a> TextRenderer<'a> {
} }
} }
} }
VNodeKind::Suspended { .. } => { VNode::Suspended { .. } => {
// we can't do anything with suspended nodes // we can't do anything with suspended nodes
} }
} }