Browse Source

wip: more work on diffing machine

Jonathan Kelley 4 years ago
parent
commit
9813f23

+ 1 - 1
Cargo.toml

@@ -55,7 +55,7 @@ members = [
     "packages/core-macro",
     "packages/core-macro",
     "packages/core",
     "packages/core",
     "packages/html-namespace",
     "packages/html-namespace",
-    # "packages/web",
+    "packages/web",
     # "packages/cli",
     # "packages/cli",
     # "packages/atoms",
     # "packages/atoms",
     # "packages/ssr",
     # "packages/ssr",

+ 114 - 96
packages/core/src/diff.rs

@@ -4,15 +4,15 @@
 //! Notice:
 //! Notice:
 //! ------
 //! ------
 //!
 //!
-//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and modified to support Components,
-//! Fragments, Suspense, and additional batching operations.
+//! The inspiration and code for this module was originally taken from Dodrio (@fitzgen) and then modified to support
+//! Components, Fragments, Suspense, and additional batching operations.
 //!
 //!
 //! Implementation Details:
 //! Implementation Details:
 //! -----------------------
 //! -----------------------
 //!
 //!
 //! All nodes are addressed by their IDs. The RealDom provides an imperative interface for making changes to these nodes.
 //! All nodes are addressed by their IDs. The RealDom provides an imperative interface for making changes to these nodes.
-//! We don't necessarily intend for changes to happen exactly during the diffing process, so the implementor may choose
-//! to batch nodes if it is more performant for their application. The u32 should be a no-op to hash,
+//! We don't necessarily require that DOM changes happen instnatly during the diffing process, so the implementor may choose
+//! to batch nodes if it is more performant for their application. We care about an ID size of u32
 //!
 //!
 //!
 //!
 //! Further Reading and Thoughts
 //! Further Reading and Thoughts
@@ -58,6 +58,8 @@ pub trait RealDom {
     fn create_text_node(&mut self, text: &str) -> RealDomNode;
     fn create_text_node(&mut self, text: &str) -> RealDomNode;
     fn create_element(&mut self, tag: &str) -> RealDomNode;
     fn create_element(&mut self, tag: &str) -> RealDomNode;
     fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
     fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
+    // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
+    fn create_placeholder(&mut self) -> RealDomNode;
 
 
     // events
     // events
     fn new_event_listener(
     fn new_event_listener(
@@ -152,7 +154,6 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
                 }
                 }
                 // New node is a text element, need to replace the element with a simple text node
                 // New node is a text element, need to replace the element with a simple text node
                 VNode::Text(_) => {
                 VNode::Text(_) => {
-                    log::debug!("Replacing el with text");
                     self.create(new_node);
                     self.create(new_node);
                     self.dom.replace_with();
                     self.dom.replace_with();
                 }
                 }
@@ -167,10 +168,22 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
                 // New node is actually a sequence of nodes.
                 // New node is actually a sequence of nodes.
                 // We need to replace this one node with a sequence of nodes
                 // We need to replace this one node with a sequence of nodes
                 // Not yet implement because it's kinda hairy
                 // Not yet implement because it's kinda hairy
-                VNode::Fragment(_) => todo!(),
+                VNode::Fragment(new) => {
+                    match new.children.len() {
+                        0 => {
+                            // remove
+                        }
+                        1 => {
+                            // replace
+                        }
+                        _ => {
+                            // remove and mount many
+                        }
+                    }
+                }
 
 
                 // New Node is actually suspended. Todo
                 // New Node is actually suspended. Todo
-                VNode::Suspended => todo!(),
+                VNode::Suspended { real } => todo!(),
             },
             },
 
 
             // Old element was text
             // Old element was text
@@ -200,7 +213,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
                         }
                         }
                     }
                     }
                 }
                 }
-                VNode::Suspended => todo!(),
+                VNode::Suspended { real } => todo!(),
             },
             },
 
 
             // Old element was a component
             // Old element was a component
@@ -214,75 +227,74 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
 
 
                     // It's also a component
                     // It's also a component
                     VNode::Component(new) => {
                     VNode::Component(new) => {
-                        match old.user_fc == new.user_fc {
+                        if old.user_fc == new.user_fc {
                             // Make sure we're dealing with the same component (by function pointer)
                             // Make sure we're dealing with the same component (by function pointer)
-                            true => {
-                                // Make sure the new component vnode is referencing the right scope id
-                                let scope_id = old.ass_scope.borrow().clone();
-                                *new.ass_scope.borrow_mut() = scope_id;
 
 
-                                // make sure the component's caller function is up to date
+                            // Make sure the new component vnode is referencing the right scope id
+                            let scope_id = old.ass_scope.borrow().clone();
+                            *new.ass_scope.borrow_mut() = scope_id;
+
+                            // make sure the component's caller function is up to date
+                            self.components
+                                .with_scope(scope_id.unwrap(), |scope| {
+                                    scope.caller = Rc::downgrade(&new.caller)
+                                })
+                                .unwrap();
+
+                            // React doesn't automatically memoize, but we do.
+                            // The cost is low enough to make it worth checking
+                            let should_render = match old.comparator {
+                                Some(comparator) => comparator(new),
+                                None => true,
+                            };
+
+                            if should_render {
+                                // // self.dom.commit_traversal();
                                 self.components
                                 self.components
-                                    .with_scope(scope_id.unwrap(), |scope| {
-                                        scope.caller = Rc::downgrade(&new.caller)
+                                    .with_scope(scope_id.unwrap(), |f| {
+                                        f.run_scope().unwrap();
                                     })
                                     })
                                     .unwrap();
                                     .unwrap();
-
-                                // React doesn't automatically memoize, but we do.
-                                // The cost is low enough to make it worth checking
-                                let should_render = match old.comparator {
-                                    Some(comparator) => comparator(new),
-                                    None => true,
-                                };
-
-                                if should_render {
-                                    // // self.dom.commit_traversal();
-                                    self.components
-                                        .with_scope(scope_id.unwrap(), |f| {
-                                            f.run_scope().unwrap();
-                                        })
-                                        .unwrap();
-                                    // diff_machine.change_list.load_known_root(root_id);
-                                    // run the scope
-                                    //
-                                } else {
-                                    // Component has memoized itself and doesn't need to be re-rendered.
-                                    // We still need to make sure the child's props are up-to-date.
-                                    // Don't commit traversal
-                                }
+                                // diff_machine.change_list.load_known_root(root_id);
+                                // run the scope
+                                //
+                            } else {
+                                // Component has memoized itself and doesn't need to be re-rendered.
+                                // We still need to make sure the child's props are up-to-date.
+                                // Don't commit traversal
                             }
                             }
+                        } else {
                             // It's an entirely different component
                             // It's an entirely different component
-                            false => {
-                                // A new component has shown up! We need to destroy the old node
-
-                                // Wipe the old one and plant the new one
-                                // self.dom.commit_traversal();
-                                // self.dom.replace_node_with(old.dom_id, new.dom_id);
-                                // self.create(new_node);
-                                // self.dom.replace_with();
-                                self.create(new_node);
-                                // self.create_and_repalce(new_node, old.mounted_root.get());
-
-                                // Now we need to remove the old scope and all of its descendents
-                                let old_scope = old.ass_scope.borrow().as_ref().unwrap().clone();
-                                self.destroy_scopes(old_scope);
-                            }
+
+                            // A new component has shown up! We need to destroy the old node
+
+                            // Wipe the old one and plant the new one
+                            // self.dom.commit_traversal();
+                            // self.dom.replace_node_with(old.dom_id, new.dom_id);
+                            // self.create(new_node);
+                            // self.dom.replace_with();
+                            self.create(new_node);
+                            // self.create_and_repalce(new_node, old.mounted_root.get());
+
+                            // Now we need to remove the old scope and all of its descendents
+                            let old_scope = old.ass_scope.borrow().as_ref().unwrap().clone();
+                            self.destroy_scopes(old_scope);
                         }
                         }
                     }
                     }
                     VNode::Fragment(_) => todo!(),
                     VNode::Fragment(_) => todo!(),
-                    VNode::Suspended => todo!(),
+                    VNode::Suspended { real } => todo!(),
                 }
                 }
             }
             }
 
 
             VNode::Fragment(old) => {
             VNode::Fragment(old) => {
                 //
                 //
                 match new_node {
                 match new_node {
-                    VNode::Fragment(_) => todo!(),
+                    VNode::Fragment(new) => todo!(),
 
 
                     // going from fragment to element means we're going from many (or potentially none) to one
                     // going from fragment to element means we're going from many (or potentially none) to one
                     VNode::Element(new) => {}
                     VNode::Element(new) => {}
                     VNode::Text(_) => todo!(),
                     VNode::Text(_) => todo!(),
-                    VNode::Suspended => todo!(),
+                    VNode::Suspended { real } => todo!(),
                     VNode::Component(_) => todo!(),
                     VNode::Component(_) => todo!(),
                 }
                 }
             }
             }
@@ -290,10 +302,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
             // a suspended node will perform a mem-copy of the previous elements until it is ready
             // a suspended node will perform a mem-copy of the previous elements until it is ready
             // this means that event listeners will need to be disabled and removed
             // this means that event listeners will need to be disabled and removed
             // it also means that props will need to disabled - IE if the node "came out of hibernation" any props should be considered outdated
             // it also means that props will need to disabled - IE if the node "came out of hibernation" any props should be considered outdated
-            VNode::Suspended => {
+            VNode::Suspended { real: old_real } => {
                 //
                 //
                 match new_node {
                 match new_node {
-                    VNode::Suspended => todo!(),
+                    VNode::Suspended { real: new_real } => {
+                        //
+                    }
                     VNode::Element(_) => todo!(),
                     VNode::Element(_) => todo!(),
                     VNode::Text(_) => todo!(),
                     VNode::Text(_) => todo!(),
                     VNode::Fragment(_) => todo!(),
                     VNode::Fragment(_) => todo!(),
@@ -354,6 +368,10 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
                 // to emit three instructions to (1) create a text node, (2) set its
                 // to emit three instructions to (1) create a text node, (2) set its
                 // text content, and finally (3) append the text node to this
                 // text content, and finally (3) append the text node to this
                 // parent.
                 // parent.
+                //
+                // Notice: this is a web-specific optimization and may be changed in the future
+                //
+                // TODO move over
                 // if children.len() == 1 {
                 // if children.len() == 1 {
                 //     if let VNode::Text(text) = &children[0] {
                 //     if let VNode::Text(text) = &children[0] {
                 //         self.dom.set_text(text.text);
                 //         self.dom.set_text(text.text);
@@ -373,7 +391,7 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
             }
             }
 
 
             VNode::Component(component) => {
             VNode::Component(component) => {
-                self.dom.create_text_node("placeholder for vcomponent");
+                let real_id = self.dom.create_placeholder();
 
 
                 // let root_id = next_id();
                 // let root_id = next_id();
                 // self.dom.save_known_root(root_id);
                 // self.dom.save_known_root(root_id);
@@ -426,7 +444,6 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
                 new_component.run_scope().unwrap();
                 new_component.run_scope().unwrap();
 
 
                 // And then run the diff algorithm
                 // And then run the diff algorithm
-                // todo!();
                 self.diff_node(new_component.old_frame(), new_component.next_frame());
                 self.diff_node(new_component.old_frame(), new_component.next_frame());
 
 
                 // Finally, insert this node as a seen node.
                 // Finally, insert this node as a seen node.
@@ -437,21 +454,17 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
             VNode::Fragment(frag) => {
             VNode::Fragment(frag) => {
                 // create the children directly in the space
                 // create the children directly in the space
                 for child in frag.children {
                 for child in frag.children {
-                    todo!()
-                    // self.create(child);
-                    // self.dom.append_child();
+                    self.create(child);
+                    self.dom.append_child();
                 }
                 }
             }
             }
 
 
-            VNode::Suspended => {
-                todo!("Creation of VNode::Suspended not yet supported")
+            VNode::Suspended { real } => {
+                let id = self.dom.create_placeholder();
+                real.set(id);
             }
             }
         }
         }
     }
     }
-
-    fn iter_children(&self, node: &VNode<'a>) -> ChildIterator<'a> {
-        todo!()
-    }
 }
 }
 
 
 impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
 impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
@@ -680,29 +693,29 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
     //     [... parent]
     //     [... parent]
     //
     //
     // Upon exiting, the change list stack is in the same state.
     // Upon exiting, the change list stack is in the same state.
-    fn diff_keyed_children(&self, old: &[VNode<'a>], new: &[VNode<'a>]) {
-        todo!();
-        // if cfg!(debug_assertions) {
-        //     let mut keys = fxhash::FxHashSet::default();
-        //     let mut assert_unique_keys = |children: &[VNode]| {
-        //         keys.clear();
-        //         for child in children {
-        //             let key = child.key();
-        //             debug_assert!(
-        //                 key.is_some(),
-        //                 "if any sibling is keyed, all siblings must be keyed"
-        //             );
-        //             keys.insert(key);
-        //         }
-        //         debug_assert_eq!(
-        //             children.len(),
-        //             keys.len(),
-        //             "keyed siblings must each have a unique key"
-        //         );
-        //     };
-        //     assert_unique_keys(old);
-        //     assert_unique_keys(new);
-        // }
+    fn diff_keyed_children(&self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
+        // todo!();
+        if cfg!(debug_assertions) {
+            let mut keys = fxhash::FxHashSet::default();
+            let mut assert_unique_keys = |children: &'a [VNode<'a>]| {
+                keys.clear();
+                for child in children {
+                    let key = child.key();
+                    debug_assert!(
+                        key.is_some(),
+                        "if any sibling is keyed, all siblings must be keyed"
+                    );
+                    keys.insert(key);
+                }
+                debug_assert_eq!(
+                    children.len(),
+                    keys.len(),
+                    "keyed siblings must each have a unique key"
+                );
+            };
+            assert_unique_keys(old);
+            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.
@@ -1090,7 +1103,12 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
             // [... parent prev_child]
             // [... parent prev_child]
             // self.dom.go_to_sibling(i);
             // self.dom.go_to_sibling(i);
             // [... parent this_child]
             // [... parent this_child]
-            self.dom.push_root(old_child.get_mounted_id().unwrap());
+
+            let did = old_child.get_mounted_id().unwrap();
+            if did.0 == 0 {
+                log::debug!("Root is bad {:#?}", old_child);
+            }
+            self.dom.push_root(did);
             self.diff_node(old_child, new_child);
             self.diff_node(old_child, new_child);
 
 
             let old_id = old_child.get_mounted_id().unwrap();
             let old_id = old_child.get_mounted_id().unwrap();
@@ -1270,7 +1288,7 @@ impl<'a> Iterator for ChildIterator<'a> {
 
 
                     // Immediately abort suspended nodes - can't do anything with them yet
                     // Immediately abort suspended nodes - can't do anything with them yet
                     // VNode::Suspended => should_pop = true,
                     // VNode::Suspended => should_pop = true,
-                    VNode::Suspended => todo!(),
+                    VNode::Suspended { real } => 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
                     VNode::Component(sc) => {
                     VNode::Component(sc) => {
@@ -1370,7 +1388,7 @@ mod tests {
 
 
                 // These would represent failing cases.
                 // These would represent failing cases.
                 VNode::Fragment(_) => panic!("Found: Fragment"),
                 VNode::Fragment(_) => panic!("Found: Fragment"),
-                VNode::Suspended => panic!("Found: Suspended"),
+                VNode::Suspended { real } => panic!("Found: Suspended"),
                 VNode::Component(_) => panic!("Found: Component"),
                 VNode::Component(_) => panic!("Found: Component"),
             }
             }
         }
         }

+ 6 - 2
packages/core/src/nodebuilder.rs

@@ -611,14 +611,18 @@ where
 impl<'a> IntoVNode<'a> for () {
 impl<'a> IntoVNode<'a> for () {
     fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
     fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
         todo!();
         todo!();
-        VNode::Suspended
+        VNode::Suspended {
+            real: Cell::new(RealDomNode::empty()),
+        }
     }
     }
 }
 }
 
 
 impl<'a> IntoVNode<'a> for Option<()> {
 impl<'a> IntoVNode<'a> for Option<()> {
     fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
     fn into_vnode(self, cx: &NodeCtx<'a>) -> VNode<'a> {
         todo!();
         todo!();
-        VNode::Suspended
+        VNode::Suspended {
+            real: Cell::new(RealDomNode::empty()),
+        }
     }
     }
 }
 }
 
 

+ 7 - 167
packages/core/src/nodes.rs

@@ -35,7 +35,7 @@ pub enum VNode<'src> {
     /// A "suspended component"
     /// A "suspended component"
     /// This is a masqeurade over an underlying future that needs to complete
     /// This is a masqeurade over an underlying future that needs to complete
     /// When the future is completed, the VNode will then trigger a render
     /// When the future is completed, the VNode will then trigger a render
-    Suspended,
+    Suspended { real: Cell<RealDomNode> },
 
 
     /// A User-defined componen node (node type COMPONENT_NODE)
     /// A User-defined componen node (node type COMPONENT_NODE)
     Component(&'src VComponent<'src>),
     Component(&'src VComponent<'src>),
@@ -49,7 +49,7 @@ impl<'a> Clone for VNode<'a> {
             VNode::Text(old) => VNode::Text(old.clone()),
             VNode::Text(old) => VNode::Text(old.clone()),
             VNode::Fragment(fragment) => VNode::Fragment(fragment),
             VNode::Fragment(fragment) => VNode::Fragment(fragment),
             VNode::Component(component) => VNode::Component(component),
             VNode::Component(component) => VNode::Component(component),
-            VNode::Suspended => VNode::Suspended,
+            VNode::Suspended { real } => VNode::Suspended { real: real.clone() },
         }
         }
     }
     }
 }
 }
@@ -85,7 +85,7 @@ impl<'old, 'new> VNode<'old> {
             }
             }
             VNode::Text(_) => todo!(),
             VNode::Text(_) => todo!(),
             VNode::Fragment(_) => todo!(),
             VNode::Fragment(_) => todo!(),
-            VNode::Suspended => todo!(),
+            VNode::Suspended { real } => todo!(),
             VNode::Component(_) => todo!(),
             VNode::Component(_) => todo!(),
         }
         }
     }
     }
@@ -142,7 +142,7 @@ impl<'a> VNode<'a> {
             VNode::Component(c) => c.key,
             VNode::Component(c) => c.key,
 
 
             // todo suspend should be allowed to have keys
             // todo suspend should be allowed to have keys
-            VNode::Suspended => NodeKey::NONE,
+            VNode::Suspended { .. } => NodeKey::NONE,
         }
         }
     }
     }
 
 
@@ -155,7 +155,7 @@ impl<'a> VNode<'a> {
             VNode::Element(_) => true,
             VNode::Element(_) => true,
             VNode::Text(_) => true,
             VNode::Text(_) => true,
             VNode::Fragment(_) => false,
             VNode::Fragment(_) => false,
-            VNode::Suspended => false,
+            VNode::Suspended { .. } => false,
             VNode::Component(_) => false,
             VNode::Component(_) => false,
         }
         }
     }
     }
@@ -165,7 +165,7 @@ impl<'a> VNode<'a> {
             VNode::Element(el) => Some(el.dom_id.get()),
             VNode::Element(el) => Some(el.dom_id.get()),
             VNode::Text(te) => Some(te.dom_id.get()),
             VNode::Text(te) => Some(te.dom_id.get()),
             VNode::Fragment(_) => todo!(),
             VNode::Fragment(_) => todo!(),
-            VNode::Suspended => todo!(),
+            VNode::Suspended { .. } => todo!(),
             VNode::Component(_) => todo!(),
             VNode::Component(_) => todo!(),
         }
         }
     }
     }
@@ -177,7 +177,7 @@ impl Debug for VNode<'_> {
             VNode::Element(el) => write!(s, "element, {}", el.tag_name),
             VNode::Element(el) => write!(s, "element, {}", el.tag_name),
             VNode::Text(t) => write!(s, "text, {}", t.text),
             VNode::Text(t) => write!(s, "text, {}", t.text),
             VNode::Fragment(_) => write!(s, "fragment"),
             VNode::Fragment(_) => write!(s, "fragment"),
-            VNode::Suspended => write!(s, "suspended"),
+            VNode::Suspended { .. } => write!(s, "suspended"),
             VNode::Component(_) => write!(s, "component"),
             VNode::Component(_) => write!(s, "component"),
         }
         }
     }
     }
@@ -436,163 +436,3 @@ impl<'a> VFragment<'a> {
         Self { key, children }
         Self { key, children }
     }
     }
 }
 }
-
-// /// This method converts a list of nested real/virtual nodes into a stream of nodes that are definitely associated
-// /// with the real dom. The only types of nodes that may be returned are text, elemets, and components.
-// ///
-// /// Components *are* considered virtual, but this iterator can't necessarily handle them without the scope arena.
-// ///
-// /// Why?
-// /// ---
-// /// Fragments are seen as virtual nodes but are actually a list of possibly-real nodes.
-// /// JS implementations normalize their node lists when fragments are present. Here, we just create a new iterator
-// /// that iterates through the recursive nesting of fragments.
-// ///
-// /// Fragments are stupid and I wish we didn't need to support them.
-// ///
-// /// This iterator only supports 3 levels of nested fragments
-// ///
-// pub fn iterate_real_nodes<'a>(nodes: &'a [VNode<'a>]) -> RealNodeIterator<'a> {
-//     RealNodeIterator::new(nodes)
-// }
-
-// pub struct RealNodeIterator<'a> {
-//     nodes: &'a [VNode<'a>],
-
-//     // this node is always a "real" node
-//     // the index is "what sibling # is it"
-//     // IE in a list of children on a fragment, the node will be a text node that's the 5th sibling
-//     node_stack: Vec<(&'a VNode<'a>, u32)>,
-// }
-
-// impl<'a> RealNodeIterator<'a> {
-//     // We immediately descend to the first real node we can find
-//     fn new(nodes: &'a [VNode<'a>]) -> Self {
-//         let mut node_stack = Vec::new();
-//         if nodes.len() > 0 {
-//             let mut cur_node = nodes.get(0).unwrap();
-//             loop {
-//                 node_stack.push((cur_node, 0_u32));
-//                 if !cur_node.is_real() {
-//                     cur_node = cur_node.get_child(0).unwrap();
-//                 } else {
-//                     break;
-//                 }
-//             }
-//         }
-
-//         Self { nodes, node_stack }
-//     }
-
-//     // // advances the cursor to the next element, panicing if we're on the 3rd level and still finding fragments
-//     // fn advance_cursor(&mut self) {
-//     //     let (mut cur_node, mut cur_id) = self.node_stack.last().unwrap();
-
-//     //     while !cur_node.is_real() {
-//     //         match cur_node {
-//     //             VNode::Element(_) | VNode::Text(_) => todo!(),
-//     //             VNode::Suspended => todo!(),
-//     //             VNode::Component(_) => todo!(),
-//     //             VNode::Fragment(frag) => {
-//     //                 let p = frag.children;
-//     //             }
-//     //         }
-//     //     }
-//     // }
-
-//     fn next_node(&mut self) -> bool {
-//         let (mut cur_node, cur_id) = self.node_stack.last_mut().unwrap();
-
-//         match cur_node {
-//             VNode::Fragment(frag) => {
-//                 //
-//                 if *cur_id + 1 > frag.children.len() as u32 {
-//                     self.node_stack.pop();
-//                     let next = self.node_stack.last_mut();
-//                     return false;
-//                 }
-//                 *cur_id += 1;
-//                 true
-//             }
-
-//             VNode::Element(_) => todo!(),
-//             VNode::Text(_) => todo!(),
-//             VNode::Suspended => todo!(),
-//             VNode::Component(_) => todo!(),
-//         }
-//     }
-
-//     fn get_current_node(&self) -> Option<&VNode<'a>> {
-//         self.node_stack.last().map(|(node, id)| match node {
-//             VNode::Element(_) => todo!(),
-//             VNode::Text(_) => todo!(),
-//             VNode::Fragment(_) => todo!(),
-//             VNode::Suspended => todo!(),
-//             VNode::Component(_) => todo!(),
-//         })
-//     }
-// }
-
-// impl<'a> Iterator for RealNodeIterator<'a> {
-//     type Item = &'a VNode<'a>;
-
-//     fn next(&mut self) -> Option<Self::Item> {
-//         todo!()
-//         // let top_idx = self.nesting_idxs.get_mut(0).unwrap();
-//         // let node = &self.nodes.get_mut(*top_idx as usize);
-
-//         // if node.is_none() {
-//         //     return None;
-//         // }
-//         // let node = node.unwrap();
-
-//         // match node {
-//         //     VNode::Element(_) | VNode::Text(_) => {
-//         //         *top_idx += 1;
-//         //         return Some(node);
-//         //     }
-//         //     VNode::Suspended => todo!(),
-//         //     // we need access over the scope map
-//         //     VNode::Component(_) => todo!(),
-
-//         //     VNode::Fragment(frag) => {
-//         //         let nest_idx = self.nesting_idxs.get_mut(1).unwrap();
-//         //         let node = &frag.children.get_mut(*nest_idx as usize);
-//         //         match node {
-//         //             VNode::Element(_) | VNode::Text(_) => {
-//         //                 *nest_idx += 1;
-//         //                 return Some(node);
-//         //             }
-//         //             VNode::Fragment(_) => todo!(),
-//         //             VNode::Suspended => todo!(),
-//         //             VNode::Component(_) => todo!(),
-//         //         }
-//         //     }
-//         // }
-//     }
-// }
-
-mod tests {
-    use crate::debug_renderer::DebugRenderer;
-    use crate::nodebuilder::LazyNodes;
-
-    use crate as dioxus;
-    use dioxus::prelude::*;
-    #[test]
-    fn iterate_nodes() {
-        let rs = rsx! {
-            Fragment {
-                Fragment {
-                    Fragment {
-                        Fragment {
-                            h1 {"abc1"}
-                        }
-                        h2 {"abc2"}
-                    }
-                    h3 {"abc3"}
-                }
-                h4 {"abc4"}
-            }
-        };
-    }
-}

+ 5 - 1
packages/core/src/util.rs

@@ -1,7 +1,7 @@
 use crate::innerlude::*;
 use crate::innerlude::*;
 
 
 pub struct DebugDom {
 pub struct DebugDom {
-    counter: u32,
+    counter: u64,
 }
 }
 impl DebugDom {
 impl DebugDom {
     pub fn new() -> Self {
     pub fn new() -> Self {
@@ -33,6 +33,10 @@ impl RealDom for DebugDom {
         self.counter += 1;
         self.counter += 1;
         RealDomNode::new(self.counter)
         RealDomNode::new(self.counter)
     }
     }
+    fn create_placeholder(&mut self) -> RealDomNode {
+        self.counter += 1;
+        RealDomNode::new(self.counter)
+    }
 
 
     fn new_event_listener(
     fn new_event_listener(
         &mut self,
         &mut self,

+ 12 - 5
packages/core/src/virtual_dom.rs

@@ -64,14 +64,19 @@ pub struct VirtualDom {
     _root_prop_type: std::any::TypeId,
     _root_prop_type: std::any::TypeId,
 }
 }
 
 
+/// The `RealDomNode` is an ID handle that corresponds to a foreign DOM node.
+///
+/// "u64" was chosen for two reasons
+/// - 0 cost hashing
+/// - use with slotmap and other versioned slot arenas
 #[derive(Clone, Copy, Debug, PartialEq)]
 #[derive(Clone, Copy, Debug, PartialEq)]
-pub struct RealDomNode(pub u32);
+pub struct RealDomNode(pub u64);
 impl RealDomNode {
 impl RealDomNode {
-    pub fn new(id: u32) -> Self {
+    pub fn new(id: u64) -> Self {
         Self(id)
         Self(id)
     }
     }
     pub fn empty() -> Self {
     pub fn empty() -> Self {
-        Self(u32::MIN)
+        Self(u64::MIN)
     }
     }
 }
 }
 
 
@@ -795,7 +800,7 @@ Any function prefixed with "use" should not be called conditionally.
     }
     }
 
 
     /// There are hooks going on here!
     /// There are hooks going on here!
-    fn use_context<T: 'static>(&self) -> &'src T {
+    fn use_context<T: 'static>(&self) -> &'src Rc<T> {
         self.try_use_context().unwrap()
         self.try_use_context().unwrap()
     }
     }
 
 
@@ -871,7 +876,9 @@ Any function prefixed with "use" should not be called conditionally.
             }
             }
             None => {
             None => {
                 // we need to register this task
                 // we need to register this task
-                VNode::Suspended
+                VNode::Suspended {
+                    real: Cell::new(RealDomNode::empty()),
+                }
             }
             }
         }
         }
     }
     }

+ 1 - 0
packages/web/Cargo.toml

@@ -26,6 +26,7 @@ atoms = { path="../atoms" }
 async-channel = "1.6.1"
 async-channel = "1.6.1"
 nohash-hasher = "0.2.0"
 nohash-hasher = "0.2.0"
 anyhow = "1.0.41"
 anyhow = "1.0.41"
+slotmap = "1.0.3"
 
 
 [dependencies.web-sys]
 [dependencies.web-sys]
 version = "0.3.50"
 version = "0.3.50"

+ 5 - 5
packages/web/examples/jackjill.rs

@@ -36,11 +36,11 @@ static Example: FC<()> = |cx| {
                                     "Jack!"
                                     "Jack!"
                                 </button>
                                 </button>
 
 
-                                <button
-                                    class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
-                                    onclick={move |_| set_name("jill")}>
-                                    "Jill!"
-                                </button>
+                            <button
+                                class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-900 font-semibold rounded shadow"
+                                onclick={move |_| set_name("jill")}>
+                                "Jill!"
+                            </button>
                         </div>
                         </div>
                     </div>
                     </div>
                 </section>
                 </section>

+ 23 - 24
packages/web/src/new.rs

@@ -7,6 +7,7 @@ use dioxus_core::{
 };
 };
 use fxhash::FxHashMap;
 use fxhash::FxHashMap;
 use nohash_hasher::IntMap;
 use nohash_hasher::IntMap;
+use slotmap::{DefaultKey, Key, KeyData};
 use wasm_bindgen::{closure::Closure, JsCast};
 use wasm_bindgen::{closure::Closure, JsCast};
 use web_sys::{
 use web_sys::{
     window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
     window, Document, Element, Event, HtmlElement, HtmlInputElement, HtmlOptionElement, Node,
@@ -14,7 +15,7 @@ use web_sys::{
 
 
 pub struct WebsysDom {
 pub struct WebsysDom {
     pub stack: Stack,
     pub stack: Stack,
-    nodes: IntMap<u32, Node>,
+    nodes: slotmap::SlotMap<DefaultKey, Node>,
     document: Document,
     document: Document,
     root: Element,
     root: Element,
 
 
@@ -39,8 +40,6 @@ pub struct WebsysDom {
     // `ptns` = Percy text node separator
     // `ptns` = Percy text node separator
     // TODO
     // TODO
     last_node_was_text: bool,
     last_node_was_text: bool,
-
-    node_counter: Counter,
 }
 }
 impl WebsysDom {
 impl WebsysDom {
     pub fn new(root: Element) -> Self {
     pub fn new(root: Element) -> Self {
@@ -52,16 +51,18 @@ impl WebsysDom {
         let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
         let (sender, mut receiver) = async_channel::unbounded::<EventTrigger>();
 
 
         let sender_callback = Arc::new(move |ev| {
         let sender_callback = Arc::new(move |ev| {
-            let mut c = sender.clone();
+            let c = sender.clone();
             wasm_bindgen_futures::spawn_local(async move {
             wasm_bindgen_futures::spawn_local(async move {
                 c.send(ev).await.unwrap();
                 c.send(ev).await.unwrap();
             });
             });
         });
         });
 
 
-        let mut nodes =
-            HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
+        let mut nodes = slotmap::SlotMap::new();
+        // HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
+        // let mut nodes =
+        //     HashMap::with_capacity_and_hasher(1000, nohash_hasher::BuildNoHashHasher::default());
 
 
-        nodes.insert(0_u32, root.clone().dyn_into::<Node>().unwrap());
+        let root_id = nodes.insert(root.clone().dyn_into::<Node>().unwrap());
         Self {
         Self {
             stack: Stack::with_capacity(10),
             stack: Stack::with_capacity(10),
             nodes,
             nodes,
@@ -74,7 +75,6 @@ impl WebsysDom {
             trigger: sender_callback,
             trigger: sender_callback,
             root,
             root,
             last_node_was_text: false,
             last_node_was_text: false,
-            node_counter: Counter(0),
         }
         }
     }
     }
 
 
@@ -84,17 +84,11 @@ impl WebsysDom {
     }
     }
 }
 }
 
 
-struct Counter(u32);
-impl Counter {
-    fn next(&mut self) -> u32 {
-        self.0 += 1;
-        self.0
-    }
-}
 impl dioxus_core::diff::RealDom for WebsysDom {
 impl dioxus_core::diff::RealDom for WebsysDom {
     fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
     fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) {
-        log::debug!("Called `[`push_root] {:?}", root);
-        let domnode = self.nodes.get(&root.0).expect("Failed to pop know root");
+        log::debug!("Called [push_root] {:?}", root);
+        let key: DefaultKey = KeyData::from_ffi(root.0).into();
+        let domnode = self.nodes.get(key).expect("Failed to pop know root");
         self.stack.push(domnode.clone());
         self.stack.push(domnode.clone());
     }
     }
 
 
@@ -166,15 +160,19 @@ impl dioxus_core::diff::RealDom for WebsysDom {
         todo!()
         todo!()
     }
     }
 
 
+    fn create_placeholder(&mut self) -> RealDomNode {
+        self.create_element("pre")
+    }
     fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode {
     fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode {
-        let nid = self.node_counter.next();
+        // let nid = self.node_counter.next();
         let textnode = self
         let textnode = self
             .document
             .document
             .create_text_node(text)
             .create_text_node(text)
             .dyn_into::<Node>()
             .dyn_into::<Node>()
             .unwrap();
             .unwrap();
         self.stack.push(textnode.clone());
         self.stack.push(textnode.clone());
-        self.nodes.insert(nid, textnode);
+        let nid = self.nodes.insert(textnode);
+        let nid = nid.data().as_ffi();
 
 
         log::debug!("Called [`create_text_node`]: {}, {}", text, nid);
         log::debug!("Called [`create_text_node`]: {}, {}", text, nid);
 
 
@@ -190,8 +188,8 @@ impl dioxus_core::diff::RealDom for WebsysDom {
             .unwrap();
             .unwrap();
 
 
         self.stack.push(el.clone());
         self.stack.push(el.clone());
-        let nid = self.node_counter.next();
-        self.nodes.insert(nid, el);
+        // let nid = self.node_counter.?next();
+        let nid = self.nodes.insert(el).data().as_ffi();
         log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
         log::debug!("Called [`create_element`]: {}, {:?}", tag, nid);
         RealDomNode::new(nid)
         RealDomNode::new(nid)
     }
     }
@@ -209,8 +207,9 @@ impl dioxus_core::diff::RealDom for WebsysDom {
             .unwrap();
             .unwrap();
 
 
         self.stack.push(el.clone());
         self.stack.push(el.clone());
-        let nid = self.node_counter.next();
-        self.nodes.insert(nid, el);
+        let nid = self.nodes.insert(el).data().as_ffi();
+        // let nid = self.node_counter.next();
+        // self.nodes.insert(nid, el);
         log::debug!("Called [`create_element_ns`]: {:}", nid);
         log::debug!("Called [`create_element_ns`]: {:}", nid);
         RealDomNode::new(nid)
         RealDomNode::new(nid)
     }
     }
@@ -287,7 +286,7 @@ impl dioxus_core::diff::RealDom for WebsysDom {
                         .context("")?;
                         .context("")?;
                     let real_id = fields
                     let real_id = fields
                         .next()
                         .next()
-                        .and_then(|f| f.parse::<u32>().ok().map(RealDomNode::new))
+                        .and_then(|f| f.parse::<u64>().ok().map(RealDomNode::new))
                         .context("")?;
                         .context("")?;
 
 
                     // Call the trigger
                     // Call the trigger