Forráskód Böngészése

wip: moving to imperative method of dom

Jonathan Kelley 4 éve
szülő
commit
45ee803

+ 4 - 0
packages/core/.vscode/spellright.dict

@@ -1,2 +1,6 @@
 Dodrio
 VDoms
+dom
+virtualdom
+ns
+nohasher

+ 4 - 4
packages/core/Cargo.toml

@@ -11,13 +11,13 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
 
 [dependencies]
 # todo: use wast for faster load/compile
-dioxus-core-macro = { path = "../core-macro", version = "0.1.1" }
+dioxus-core-macro = { path="../core-macro", version="0.1.1" }
 
 # Backs scopes and graphs between parent and children
-generational-arena = { version = "0.2.8" }
+generational-arena = { version="0.2.8" }
 
 # Bumpalo backs the VNode creation
-bumpalo = { version = "3.6.0", features = ["collections", "boxed"] }
+bumpalo = { version="3.6.0", features=["collections", "boxed"] }
 
 # custom error type
 thiserror = "1"
@@ -32,7 +32,7 @@ longest-increasing-subsequence = "0.1.0"
 log = "0.4"
 
 # Serialize the Edits for use in Webview/Liveview instances
-serde = { version = "1", features = ["derive"], optional = true }
+serde = { version="1", features=["derive"], optional=true }
 
 [features]
 default = []

+ 41 - 7
packages/core/architecture.md

@@ -1,23 +1,57 @@
 # This module includes all life-cycle related mechanics, including the virtual DOM, scopes, properties, and lifecycles.
+
 ---
+
 The VirtualDom is designed as so:
 
 VDOM contains:
+
 - An arena of component scopes.
-    - A scope contains
-        - lifecycle data
-        - hook data
+  - A scope contains
+    - lifecycle data
+    - hook data
 - Event queue
-    - An event
+  - An event
 
 A VDOM is
+
 - constructed from anything that implements "component"
 
 A "Component" is anything (normally functions) that can be ran with a context to produce VNodes
+
 - Must implement properties-builder trait which produces a properties builder
 
 A Context
+
 - Is a consumable struct
-    - Made of references to properties
-    - Holds a reference (lockable) to the underlying scope
-    - Is partially thread-safe
+  - Made of references to properties
+  - Holds a reference (lockable) to the underlying scope
+  - Is partially thread-safe
+
+# How to interact with the real dom?
+
+## idea: use only u32
+
+pros:
+
+- allows for 4,294,967,295 nodes (enough)
+- u32 is relatively small
+- doesn't add type noise
+- allows virtualdom to stay completely generic
+
+cons:
+
+- cost of querying individual nodes (about 7ns per node query for all sizes w/ nohasher)
+- old IDs need to be manually freed when subtrees are destroyed
+  - can be collected as garbage after every render
+- loss of ids between renders........................
+  - each new render doesn't know which node the old one was connected to unless it is visited
+  - When are nodes _not_ visited during diffing?
+    - They are predetermined to be removed (a parent was probed)
+    - something with keys?
+    - I think all nodes must be visited between diffs
+  -
+
+## idea: leak raw nodes and then reclaim them on drop
+
+## idea: bind

+ 217 - 159
packages/core/src/diff.rs

@@ -9,15 +9,10 @@
 //! Implementation Details:
 //! -----------------------
 //!
-//! Diff the `old` node with the `new` node. Emits instructions to modify a
-//! physical DOM node that reflects `old` into something that reflects `new`.
+//! 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,
 //!
-//! Upon entry to this function, the physical DOM node must be on the top of the
-//! change list stack:
-//!
-//!     [... node]
-//!
-//! The change list stack is in the same state when this function exits.
 //!
 //! Further Reading and Thoughts
 //! ----------------------------
@@ -30,11 +25,58 @@ use crate::{arena::ScopeArena, innerlude::*};
 use fxhash::{FxHashMap, FxHashSet};
 
 use std::{
+    any::Any,
+    cell::Cell,
     cmp::Ordering,
     rc::{Rc, Weak},
-    sync::atomic::AtomicU32,
 };
 
+/// The accompanying "real dom" exposes an imperative API for controlling the UI layout
+///
+/// Instead of having handles directly over nodes, Dioxus uses simple u32s as node IDs.
+/// This allows layouts with up to 4,294,967,295 nodes. If we
+pub trait RealDom {
+    fn delete_root(&mut self, root: RealDomNode);
+
+    // ===========
+    //  Create
+    // ===========
+    /// Create a new text node and push it on to the top of the stack
+    fn create_text_node(&mut self, text: &str) -> RealDomNode;
+
+    /// Create a new text node and push it on to the top of the stack
+    fn create_element(&mut self, tag: &str) -> RealDomNode;
+
+    /// Create a new namespaced element and push it on to the top of the stack
+    fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode;
+
+    fn append_node(&self, child: RealDomNode, parent: RealDomNode);
+
+    // ===========
+    //  Remove
+    // ===========
+    fn remove_node(&mut self, node: RealDomNode);
+
+    fn remove_all_children(&mut self, node: RealDomNode);
+
+    // ===========
+    //  Replace
+    // ===========
+    fn replace_node_with(&mut self, old: RealDomNode, new: RealDomNode);
+
+    fn new_event_listener(&mut self, node: RealDomNode, event: &str);
+
+    fn set_inner_text(&mut self, node: RealDomNode, text: &str);
+
+    fn set_class(&mut self, node: RealDomNode);
+
+    fn set_attr(&mut self, node: RealDomNode, name: &str, value: &str);
+
+    fn remove_attr(&mut self, node: RealDomNode);
+
+    fn raw_node_as_any_mut(&mut self) -> &mut dyn Any;
+}
+
 /// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
 /// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
 ///
@@ -47,39 +89,39 @@ use std::{
 /// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
 /// that were modified by the eventtrigger. This prevents doubly evaluating components if they were both updated via
 /// subscriptions and props changes.
-pub struct DiffMachine<'a> {
-    pub create_diffs: bool,
+pub struct DiffMachine {
     pub cur_idx: ScopeIdx,
-    pub change_list: EditMachine<'a>,
     pub diffed: FxHashSet<ScopeIdx>,
     pub components: ScopeArena,
     pub event_queue: EventQueue,
     pub seen_nodes: FxHashSet<ScopeIdx>,
 }
 
-static COUNTER: AtomicU32 = AtomicU32::new(1);
+// todo: see if unsafe works better
+static COUNTER: Cell<u32> = Cell::new(1);
 fn next_id() -> u32 {
-    COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
+    let out = COUNTER.get();
+    COUNTER.set(out + 1);
+    out
 }
 
-impl<'a> DiffMachine<'a> {
+impl DiffMachine {
     pub fn new(components: ScopeArena, cur_idx: ScopeIdx, event_queue: EventQueue) -> Self {
         Self {
             components,
             cur_idx,
             event_queue,
-            create_diffs: true,
-            change_list: EditMachine::new(),
             diffed: FxHashSet::default(),
             seen_nodes: FxHashSet::default(),
         }
     }
 
-    pub fn consume(self) -> EditList<'a> {
-        self.change_list.emitter
-    }
-
-    pub fn diff_node(&mut self, old_node: &VNode<'a>, new_node: &VNode<'a>) {
+    pub fn diff_node<'a, Dom: RealDom>(
+        &mut self,
+        dom: &mut Dom,
+        old_node: &mut VNode<'a>,
+        new_node: &mut VNode<'a>,
+    ) {
         // pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
         /*
         For each valid case, we "commit traversal", meaning we save this current position in the tree.
@@ -87,61 +129,64 @@ impl<'a> DiffMachine<'a> {
         When re-entering, we reuse the EditList in DiffState
         */
         match (old_node, new_node) {
-            (VNode::Text(old_text), VNode::Text(new_text)) => {
-                if old_text != new_text {
-                    self.change_list.commit_traversal();
-                    self.change_list.set_text(new_text);
+            (VNode::Text(old), VNode::Text(new)) => {
+                new.dom_id = old.dom_id;
+                if old.text != new.text {
+                    self.realdom.set_inner_text(new.dom_id, new.text);
                 }
             }
 
-            (VNode::Text(_), VNode::Element(_)) => {
-                self.change_list.commit_traversal();
+            (VNode::Text(old), VNode::Element(new)) => {
+                // // self.realdom.commit_traversal();
                 self.create(new_node);
-                self.change_list.replace_with();
+                self.realdom.replace_node_with(old.dom_id, old.dom_id);
+                // self.realdom.replace_with();
             }
 
-            (VNode::Element(_), VNode::Text(_)) => {
-                self.change_list.commit_traversal();
+            (VNode::Element(old), VNode::Text(new)) => {
+                // // self.realdom.commit_traversal();
                 self.create(new_node);
-                self.change_list.replace_with();
+                self.realdom.replace_node_with(old.dom_id, new.dom_id);
+                // self.realdom.replace_with();
             }
 
-            (VNode::Element(eold), VNode::Element(enew)) => {
+            (VNode::Element(old), VNode::Element(new)) => {
                 // If the element type is completely different, the element needs to be re-rendered completely
-                if enew.tag_name != eold.tag_name || enew.namespace != eold.namespace {
-                    self.change_list.commit_traversal();
-                    self.change_list.replace_with();
+                if new.tag_name != old.tag_name || new.namespace != old.namespace {
+                    // // self.realdom.commit_traversal();
+                    // self.realdom.replace_with();
+                    self.realdom.replace_node_with(old.dom_id, new.dom_id);
                     return;
                 }
 
-                self.diff_listeners(eold.listeners, enew.listeners);
-                self.diff_attr(eold.attributes, enew.attributes, enew.namespace.is_some());
-                self.diff_children(eold.children, enew.children);
+                self.diff_listeners(old.listeners, new.listeners);
+                self.diff_attr(old.attributes, new.attributes, new.namespace.is_some());
+                self.diff_children(old.children, new.children);
             }
 
-            (VNode::Component(cold), VNode::Component(cnew)) => {
+            (VNode::Component(old), VNode::Component(new)) => {
                 // Make sure we're dealing with the same component (by function pointer)
-                if cold.user_fc == cnew.user_fc {
+                if old.user_fc == new.user_fc {
                     // Make sure the new component vnode is referencing the right scope id
-                    let scope_id = cold.ass_scope.borrow().clone();
-                    *cnew.ass_scope.borrow_mut() = 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(&cnew.caller)
+                            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 cold.comparator {
-                        Some(comparator) => comparator(cnew),
+                    let should_render = match old.comparator {
+                        Some(comparator) => comparator(new),
                         None => true,
                     };
 
                     if should_render {
-                        self.change_list.commit_traversal();
+                        // // self.realdom.commit_traversal();
                         self.components
                             .with_scope(scope_id.unwrap(), |f| {
                                 f.run_scope().unwrap();
@@ -159,28 +204,29 @@ impl<'a> DiffMachine<'a> {
                     // A new component has shown up! We need to destroy the old node
 
                     // Wipe the old one and plant the new one
-                    self.change_list.commit_traversal();
+                    // self.realdom.commit_traversal();
                     self.create(new_node);
-                    self.change_list.replace_with();
+                    // self.realdom.replace_node_with(old.dom_id, new.dom_id);
+                    self.realdom.replace_with();
 
                     // Now we need to remove the old scope and all of its descendents
-                    let old_scope = cold.ass_scope.borrow().as_ref().unwrap().clone();
+                    let old_scope = old.ass_scope.borrow().as_ref().unwrap().clone();
                     self.destroy_scopes(old_scope);
                 }
             }
 
             // todo: knock out any listeners
             (_, VNode::Component(_)) => {
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 self.create(new_node);
-                self.change_list.replace_with();
+                self.realdom.replace_with();
             }
 
             // A component is being torn down in favor of a non-component node
             (VNode::Component(_old), _) => {
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 self.create(new_node);
-                self.change_list.replace_with();
+                self.realdom.replace_with();
 
                 // Destroy the original scope and any of its children
                 self.destroy_scopes(_old.ass_scope.borrow().unwrap());
@@ -223,35 +269,46 @@ impl<'a> DiffMachine<'a> {
     // When this function returns, the new node is on top of the change list stack:
     //
     //     [... node]
-    fn create(&mut self, node: &VNode<'a>) {
-        debug_assert!(self.change_list.traversal_is_committed());
+    fn create<'a, Dom: RealDom>(
+        &mut self,
+        dom: &mut Dom,
+        node: &mut VNode<'a>,
+        parent: RealDomNode,
+    ) {
+        // debug_assert!(self.realdom.traversal_is_committed());
         match node {
             VNode::Text(text) => {
-                self.change_list.create_text_node(text);
+                let real_id = self.realdom.create_text_node(text.text);
+                text.dom_id = real_id;
             }
-            VNode::Element(&VElement {
-                key,
-                tag_name,
-                listeners,
-                attributes,
-                children,
-                namespace,
-            }) => {
+            VNode::Element(&el) => {
+                let VElement {
+                    key,
+                    tag_name,
+                    listeners,
+                    attributes,
+                    children,
+                    namespace,
+                    dom_id,
+                } = el;
                 // log::info!("Creating {:#?}", node);
-                if let Some(namespace) = namespace {
-                    self.change_list.create_element_ns(tag_name, namespace);
+                let real_id = if let Some(namespace) = namespace {
+                    self.realdom.create_element_ns(tag_name, namespace)
                 } else {
-                    self.change_list.create_element(tag_name);
-                }
+                    self.realdom.create_element(tag_name)
+                };
+                el.dom_id = real_id;
 
                 listeners.iter().enumerate().for_each(|(_id, listener)| {
-                    self.change_list
-                        .new_event_listener(listener.event, listener.scope, listener.id)
+                    todo!()
+                    // self.realdom
+                    //     .new_event_listener(listener.event, listener.scope, listener.id)
                 });
 
                 for attr in attributes {
-                    self.change_list
-                        .set_attribute(&attr.name, &attr.value, namespace.is_some());
+                    todo!()
+                    // self.realdom
+                    //     .set_attribute(&attr.name, &attr.value, namespace.is_some());
                 }
 
                 // Fast path: if there is a single text child, it is faster to
@@ -262,28 +319,27 @@ impl<'a> DiffMachine<'a> {
                 // parent.
                 if children.len() == 1 {
                     if let VNode::Text(text) = children[0] {
-                        self.change_list.set_text(text);
+                        self.realdom.set_inner_text(real_id, text.text);
                         return;
                     }
                 }
 
                 for child in children {
-                    self.create(child);
+                    self.create(child, real_id);
                     if let VNode::Fragment(_) = child {
                         // do nothing
                         // fragments append themselves
                     } else {
-                        self.change_list.append_child();
+                        self.realdom.append_child();
                     }
                 }
             }
 
             VNode::Component(component) => {
-                self.change_list
-                    .create_text_node("placeholder for vcomponent");
+                self.realdom.create_text_node("placeholder for vcomponent");
 
-                let root_id = next_id();
-                self.change_list.save_known_root(root_id);
+                // let root_id = next_id();
+                // self.realdom.save_known_root(root_id);
 
                 log::debug!("Mounting a new component");
                 let caller: Weak<OpaqueComponent> = Rc::downgrade(&component.caller);
@@ -333,7 +389,8 @@ impl<'a> DiffMachine<'a> {
                 new_component.run_scope().unwrap();
 
                 // And then run the diff algorithm
-                self.diff_node(new_component.old_frame(), new_component.next_frame());
+                todo!();
+                // self.diff_node(new_component.old_frame(), new_component.next_frame());
 
                 // Finally, insert this node as a seen node.
                 self.seen_nodes.insert(idx);
@@ -343,8 +400,9 @@ impl<'a> DiffMachine<'a> {
             VNode::Fragment(frag) => {
                 // create the children directly in the space
                 for child in frag.children {
-                    self.create(child);
-                    self.change_list.append_child();
+                    todo!()
+                    // self.create(child);
+                    // self.realdom.append_child();
                 }
             }
 
@@ -395,7 +453,7 @@ impl<'a> DiffMachine<'a> {
     // The change list stack is left unchanged.
     fn diff_listeners(&mut self, old: &[Listener<'_>], new: &[Listener<'_>]) {
         if !old.is_empty() || !new.is_empty() {
-            self.change_list.commit_traversal();
+            // self.realdom.commit_traversal();
         }
 
         'outer1: for (_l_idx, new_l) in new.iter().enumerate() {
@@ -410,8 +468,8 @@ impl<'a> DiffMachine<'a> {
             for old_l in old {
                 if new_l.event == old_l.event {
                     if new_l.id != old_l.id {
-                        self.change_list.remove_event_listener(event_type);
-                        self.change_list
+                        self.realdom.remove_event_listener(event_type);
+                        self.realdom
                             .update_event_listener(event_type, new_l.scope, new_l.id)
                     }
 
@@ -419,7 +477,7 @@ impl<'a> DiffMachine<'a> {
                 }
             }
 
-            self.change_list
+            self.realdom
                 .new_event_listener(event_type, new_l.scope, new_l.id);
         }
 
@@ -429,7 +487,7 @@ impl<'a> DiffMachine<'a> {
                     continue 'outer2;
                 }
             }
-            self.change_list.remove_event_listener(old_l.event);
+            self.realdom.remove_event_listener(old_l.event);
         }
     }
 
@@ -440,7 +498,7 @@ impl<'a> DiffMachine<'a> {
     //     [... node]
     //
     // The change list stack is left unchanged.
-    fn diff_attr(
+    fn diff_attr<'a>(
         &mut self,
         old: &'a [Attribute<'a>],
         new: &'a [Attribute<'a>],
@@ -453,15 +511,15 @@ impl<'a> DiffMachine<'a> {
         // With the Rsx and Html macros, this will almost always be the case
         'outer: for new_attr in new {
             if new_attr.is_volatile() {
-                self.change_list.commit_traversal();
-                self.change_list
+                // self.realdom.commit_traversal();
+                self.realdom
                     .set_attribute(new_attr.name, new_attr.value, is_namespaced);
             } else {
                 for old_attr in old {
                     if old_attr.name == new_attr.name {
                         if old_attr.value != new_attr.value {
-                            self.change_list.commit_traversal();
-                            self.change_list.set_attribute(
+                            // self.realdom.commit_traversal();
+                            self.realdom.set_attribute(
                                 new_attr.name,
                                 new_attr.value,
                                 is_namespaced,
@@ -473,8 +531,8 @@ impl<'a> DiffMachine<'a> {
                     }
                 }
 
-                self.change_list.commit_traversal();
-                self.change_list
+                // self.realdom.commit_traversal();
+                self.realdom
                     .set_attribute(new_attr.name, new_attr.value, is_namespaced);
             }
         }
@@ -486,8 +544,8 @@ impl<'a> DiffMachine<'a> {
                 }
             }
 
-            self.change_list.commit_traversal();
-            self.change_list.remove_attribute(old_attr.name);
+            // self.realdom.commit_traversal();
+            self.realdom.remove_attribute(old_attr.name);
         }
     }
 
@@ -499,10 +557,10 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // the change list stack is in the same state when this function returns.
-    fn diff_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
+    fn diff_children<'a>(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
         if new.is_empty() {
             if !old.is_empty() {
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 self.remove_all_children(old);
             }
             return;
@@ -515,8 +573,8 @@ impl<'a> DiffMachine<'a> {
                 }
 
                 (_, &VNode::Text(text)) => {
-                    self.change_list.commit_traversal();
-                    self.change_list.set_text(text);
+                    // self.realdom.commit_traversal();
+                    self.realdom.set_text(text);
                     return;
                 }
 
@@ -527,7 +585,7 @@ impl<'a> DiffMachine<'a> {
 
         if old.is_empty() {
             if !new.is_empty() {
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 self.create_and_append_children(new);
             }
             return;
@@ -546,9 +604,9 @@ impl<'a> DiffMachine<'a> {
         );
 
         if new_is_keyed && old_is_keyed {
-            let t = self.change_list.next_temporary();
+            let t = self.realdom.next_temporary();
             self.diff_keyed_children(old, new);
-            self.change_list.set_next_temporary(t);
+            self.realdom.set_next_temporary(t);
         } else {
             self.diff_non_keyed_children(old, new);
         }
@@ -575,7 +633,7 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // Upon exiting, the change list stack is in the same state.
-    fn diff_keyed_children(&mut self, old: &[VNode<'a>], new: &[VNode<'a>]) {
+    fn diff_keyed_children<'a>(&mut self, old: &[VNode<'a>], new: &[VNode<'a>]) {
         // if cfg!(debug_assertions) {
         //     let mut keys = fxhash::FxHashSet::default();
         //     let mut assert_unique_keys = |children: &[VNode]| {
@@ -658,8 +716,8 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // Upon exit, the change list stack is the same.
-    fn diff_keyed_prefix(&mut self, old: &[VNode<'a>], new: &[VNode<'a>]) -> KeyedPrefixResult {
-        self.change_list.go_down();
+    fn diff_keyed_prefix<'a>(&mut self, old: &[VNode<'a>], new: &[VNode<'a>]) -> KeyedPrefixResult {
+        // self.realdom.go_down();
         let mut shared_prefix_count = 0;
 
         for (i, (old, new)) in old.iter().zip(new.iter()).enumerate() {
@@ -667,7 +725,7 @@ impl<'a> DiffMachine<'a> {
                 break;
             }
 
-            self.change_list.go_to_sibling(i);
+            self.realdom.go_to_sibling(i);
 
             self.diff_node(old, new);
 
@@ -677,8 +735,8 @@ impl<'a> DiffMachine<'a> {
         // If that was all of the old children, then create and append the remaining
         // new children and we're finished.
         if shared_prefix_count == old.len() {
-            self.change_list.go_up();
-            self.change_list.commit_traversal();
+            self.realdom.go_up();
+            // self.realdom.commit_traversal();
             self.create_and_append_children(&new[shared_prefix_count..]);
             return KeyedPrefixResult::Finished;
         }
@@ -686,13 +744,13 @@ impl<'a> DiffMachine<'a> {
         // And if that was all of the new children, then remove all of the remaining
         // old children and we're finished.
         if shared_prefix_count == new.len() {
-            self.change_list.go_to_sibling(shared_prefix_count);
-            self.change_list.commit_traversal();
+            self.realdom.go_to_sibling(shared_prefix_count);
+            // self.realdom.commit_traversal();
             self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
             return KeyedPrefixResult::Finished;
         }
 
-        self.change_list.go_up();
+        self.realdom.go_up();
         KeyedPrefixResult::MoreWorkToDo(shared_prefix_count)
     }
 
@@ -709,7 +767,7 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // Upon exit from this function, it will be restored to that same state.
-    fn diff_keyed_middle(
+    fn diff_keyed_middle<'a>(
         &mut self,
         old: &[VNode<'a>],
         mut new: &[VNode<'a>],
@@ -753,11 +811,11 @@ impl<'a> DiffMachine<'a> {
         // afresh.
         if shared_suffix_count == 0 && shared_keys.is_empty() {
             if shared_prefix_count == 0 {
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 self.remove_all_children(old);
             } else {
-                self.change_list.go_down_to_child(shared_prefix_count);
-                self.change_list.commit_traversal();
+                self.realdom.go_down_to_child(shared_prefix_count);
+                // self.realdom.commit_traversal();
                 self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
             }
 
@@ -779,8 +837,8 @@ impl<'a> DiffMachine<'a> {
                 .unwrap_or(old.len());
 
             if end - start > 0 {
-                self.change_list.commit_traversal();
-                let mut t = self.change_list.save_children_to_temporaries(
+                // self.realdom.commit_traversal();
+                let mut t = self.realdom.save_children_to_temporaries(
                     shared_prefix_count + start,
                     shared_prefix_count + end,
                 );
@@ -805,8 +863,8 @@ impl<'a> DiffMachine<'a> {
             if !shared_keys.contains(&old_child.key()) {
                 // registry.remove_subtree(old_child);
                 // todo
-                self.change_list.commit_traversal();
-                self.change_list.remove_child(i + shared_prefix_count);
+                // self.realdom.commit_traversal();
+                self.realdom.remove_child(i + shared_prefix_count);
                 removed_count += 1;
             }
         }
@@ -849,7 +907,7 @@ impl<'a> DiffMachine<'a> {
             // shared suffix to the change list stack.
             //
             // [... parent]
-            self.change_list
+            self.realdom
                 .go_down_to_child(old_shared_suffix_start - removed_count);
         // [... parent first_child_of_shared_suffix]
         } else {
@@ -865,29 +923,29 @@ impl<'a> DiffMachine<'a> {
                 let old_index = new_index_to_old_index[last_index];
                 let temp = old_index_to_temp[old_index];
                 // [... parent]
-                self.change_list.go_down_to_temp_child(temp);
+                self.realdom.go_down_to_temp_child(temp);
                 // [... parent last]
                 self.diff_node(&old[old_index], last);
 
                 if new_index_is_in_lis.contains(&last_index) {
                     // Don't move it, since it is already where it needs to be.
                 } else {
-                    self.change_list.commit_traversal();
+                    // self.realdom.commit_traversal();
                     // [... parent last]
-                    self.change_list.append_child();
+                    self.realdom.append_child();
                     // [... parent]
-                    self.change_list.go_down_to_temp_child(temp);
+                    self.realdom.go_down_to_temp_child(temp);
                     // [... parent last]
                 }
             } else {
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 // [... parent]
                 self.create(last);
 
                 // [... parent last]
-                self.change_list.append_child();
+                self.realdom.append_child();
                 // [... parent]
-                self.change_list.go_down_to_reverse_child(0);
+                self.realdom.go_down_to_reverse_child(0);
                 // [... parent last]
             }
         }
@@ -896,11 +954,11 @@ impl<'a> DiffMachine<'a> {
             let old_index = new_index_to_old_index[new_index];
             if old_index == u32::MAX as usize {
                 debug_assert!(!shared_keys.contains(&new_child.key()));
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 // [... parent successor]
                 self.create(new_child);
                 // [... parent successor new_child]
-                self.change_list.insert_before();
+                self.realdom.insert_before();
             // [... parent new_child]
             } else {
                 debug_assert!(shared_keys.contains(&new_child.key()));
@@ -909,14 +967,14 @@ impl<'a> DiffMachine<'a> {
 
                 if new_index_is_in_lis.contains(&new_index) {
                     // [... parent successor]
-                    self.change_list.go_to_temp_sibling(temp);
+                    self.realdom.go_to_temp_sibling(temp);
                 // [... parent new_child]
                 } else {
-                    self.change_list.commit_traversal();
+                    // self.realdom.commit_traversal();
                     // [... parent successor]
-                    self.change_list.push_temporary(temp);
+                    self.realdom.push_temporary(temp);
                     // [... parent successor new_child]
-                    self.change_list.insert_before();
+                    self.realdom.insert_before();
                     // [... parent new_child]
                 }
 
@@ -925,7 +983,7 @@ impl<'a> DiffMachine<'a> {
         }
 
         // [... parent child]
-        self.change_list.go_up();
+        self.realdom.go_up();
         // [... parent]
     }
 
@@ -936,7 +994,7 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // When this function exits, the change list stack remains the same.
-    fn diff_keyed_suffix(
+    fn diff_keyed_suffix<'a>(
         &mut self,
         old: &[VNode<'a>],
         new: &[VNode<'a>],
@@ -946,16 +1004,16 @@ impl<'a> DiffMachine<'a> {
         debug_assert!(!old.is_empty());
 
         // [... parent]
-        self.change_list.go_down();
+        self.realdom.go_down();
         // [... parent new_child]
 
         for (i, (old_child, new_child)) in old.iter().zip(new.iter()).enumerate() {
-            self.change_list.go_to_sibling(new_shared_suffix_start + i);
+            self.realdom.go_to_sibling(new_shared_suffix_start + i);
             self.diff_node(old_child, new_child);
         }
 
         // [... parent]
-        self.change_list.go_up();
+        self.realdom.go_up();
     }
 
     // Diff children that are not keyed.
@@ -966,18 +1024,18 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // the change list stack is in the same state when this function returns.
-    fn diff_non_keyed_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
+    fn diff_non_keyed_children<'a>(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
         // Handled these cases in `diff_children` before calling this function.
         debug_assert!(!new.is_empty());
         debug_assert!(!old.is_empty());
 
         //     [... parent]
-        self.change_list.go_down();
+        self.realdom.go_down();
         //     [... parent child]
 
         for (i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
             // [... parent prev_child]
-            self.change_list.go_to_sibling(i);
+            self.realdom.go_to_sibling(i);
             // [... parent this_child]
             self.diff_node(old_child, new_child);
         }
@@ -986,9 +1044,9 @@ impl<'a> DiffMachine<'a> {
             // old.len > new.len -> removing some nodes
             Ordering::Greater => {
                 // [... parent prev_child]
-                self.change_list.go_to_sibling(new.len());
+                self.realdom.go_to_sibling(new.len());
                 // [... parent first_child_to_remove]
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 // support::remove_self_and_next_siblings(state, &old[new.len()..]);
                 self.remove_self_and_next_siblings(&old[new.len()..]);
                 // [... parent]
@@ -996,15 +1054,15 @@ impl<'a> DiffMachine<'a> {
             // old.len < new.len -> adding some nodes
             Ordering::Less => {
                 // [... parent last_child]
-                self.change_list.go_up();
+                self.realdom.go_up();
                 // [... parent]
-                self.change_list.commit_traversal();
+                // self.realdom.commit_traversal();
                 self.create_and_append_children(&new[old.len()..]);
             }
             // old.len == new.len -> no nodes added/removed, but πerhaps changed
             Ordering::Equal => {
                 // [... parent child]
-                self.change_list.go_up();
+                self.realdom.go_up();
                 // [... parent]
             }
         }
@@ -1021,15 +1079,15 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // When this function returns, the change list stack is in the same state.
-    pub fn remove_all_children(&mut self, old: &[VNode<'a>]) {
-        debug_assert!(self.change_list.traversal_is_committed());
+    pub fn remove_all_children<'a>(&mut self, old: &[VNode<'a>]) {
+        // debug_assert!(self.realdom.traversal_is_committed());
         log::debug!("REMOVING CHILDREN");
         for _child in old {
             // registry.remove_subtree(child);
         }
         // Fast way to remove all children: set the node's textContent to an empty
         // string.
-        self.change_list.set_text("");
+        self.realdom.set_text("");
     }
 
     // Create the given children and append them to the parent node.
@@ -1039,11 +1097,11 @@ impl<'a> DiffMachine<'a> {
     //     [... parent]
     //
     // When this function returns, the change list stack is in the same state.
-    pub fn create_and_append_children(&mut self, new: &[VNode<'a>]) {
-        debug_assert!(self.change_list.traversal_is_committed());
+    pub fn create_and_append_children<'a>(&mut self, new: &[VNode<'a>]) {
+        // debug_assert!(self.realdom.traversal_is_committed());
         for child in new {
             self.create(child);
-            self.change_list.append_child();
+            self.realdom.append_child();
         }
     }
 
@@ -1056,11 +1114,11 @@ impl<'a> DiffMachine<'a> {
     // After the function returns, the child is no longer on the change list stack:
     //
     //     [... parent]
-    pub fn remove_self_and_next_siblings(&mut self, old: &[VNode<'a>]) {
-        debug_assert!(self.change_list.traversal_is_committed());
+    pub fn remove_self_and_next_siblings<'a>(&mut self, old: &[VNode<'a>]) {
+        // debug_assert!(self.realdom.traversal_is_committed());
         for child in old {
             if let VNode::Component(vcomp) = child {
-                // self.change_list
+                // self.realdom
                 //     .create_text_node("placeholder for vcomponent");
 
                 todo!()
@@ -1071,7 +1129,7 @@ impl<'a> DiffMachine<'a> {
                 // })
                 // let id = get_id();
                 // *component.stable_addr.as_ref().borrow_mut() = Some(id);
-                // self.change_list.save_known_root(id);
+                // self.realdom.save_known_root(id);
                 // let scope = Rc::downgrade(&component.ass_scope);
                 // self.lifecycle_events.push_back(LifeCycleEvent::Mount {
                 //     caller: Rc::downgrade(&component.caller),
@@ -1082,7 +1140,7 @@ impl<'a> DiffMachine<'a> {
 
             // registry.remove_subtree(child);
         }
-        self.change_list.remove_self_and_next_siblings();
+        self.realdom.remove_self_and_next_siblings();
     }
 }
 

+ 2 - 3
packages/core/src/lib.rs

@@ -11,7 +11,7 @@
 pub mod arena;
 pub mod component; // Logic for extending FC
 
-pub mod debug_renderer;
+// pub mod debug_renderer;
 pub mod diff;
 pub mod patch; // An "edit phase" described by transitions and edit operations // Test harness for validating that lifecycles and diffs work appropriately
                // the diffing algorithm that builds the ChangeList
@@ -30,7 +30,6 @@ pub mod builder {
 pub(crate) mod innerlude {
     pub use crate::component::*;
 
-    pub use crate::debug_renderer::*;
     pub use crate::diff::*;
     pub use crate::error::*;
     pub use crate::events::*;
@@ -77,6 +76,6 @@ pub mod prelude {
     pub use crate::diff::DiffMachine;
     pub use crate::virtual_dom::ScopeIdx;
 
-    pub use crate::debug_renderer::DebugRenderer;
+    // pub use crate::debug_renderer::DebugRenderer;
     pub use crate::hooks::*;
 }

+ 19 - 9
packages/core/src/nodes.rs

@@ -7,13 +7,12 @@ use crate::{
     events::VirtualEvent,
     innerlude::{Context, Properties, Scope, ScopeIdx, FC},
     nodebuilder::text3,
-    virtual_dom::NodeCtx,
-    // support::NodeCtx,
+    virtual_dom::{NodeCtx, RealDomNode},
 };
 use bumpalo::Bump;
 use std::{
     any::Any,
-    cell::RefCell,
+    cell::{Cell, RefCell},
     fmt::{Arguments, Debug},
     marker::PhantomData,
     rc::Rc,
@@ -29,7 +28,7 @@ pub enum VNode<'src> {
     Element(&'src VElement<'src>),
 
     /// A text node (node type `TEXT_NODE`).
-    Text(&'src str),
+    Text(VText<'src>),
 
     /// A fragment is a "virtual position" in the DOM
     /// Fragments may have children and keys
@@ -49,7 +48,7 @@ impl<'a> Clone for VNode<'a> {
     fn clone(&self) -> Self {
         match self {
             VNode::Element(element) => VNode::Element(element),
-            VNode::Text(text) => VNode::Text(text),
+            VNode::Text(old) => VNode::Text(old.clone()),
             VNode::Fragment(fragment) => VNode::Fragment(fragment),
             VNode::Component(component) => VNode::Component(component),
             VNode::Suspended => VNode::Suspended,
@@ -81,6 +80,7 @@ impl<'a> VNode<'a> {
             attributes,
             children,
             namespace,
+            dom_id: Cell::new(RealDomNode::empty()),
         });
         VNode::Element(element)
     }
@@ -88,7 +88,10 @@ impl<'a> VNode<'a> {
     /// Construct a new text node with the given text.
     #[inline]
     pub fn text(text: &'a str) -> VNode<'a> {
-        VNode::Text(text)
+        VNode::Text(VText {
+            text,
+            dom_id: Cell::new(RealDomNode::empty()),
+        })
     }
 
     pub fn text_args(bump: &'a Bump, args: Arguments) -> VNode<'a> {
@@ -98,7 +101,7 @@ impl<'a> VNode<'a> {
     #[inline]
     pub(crate) fn key(&self) -> NodeKey {
         match &self {
-            VNode::Text(_) => NodeKey::NONE,
+            VNode::Text { .. } => NodeKey::NONE,
             VNode::Element(e) => e.key,
             VNode::Fragment(frag) => frag.key,
             VNode::Component(c) => c.key,
@@ -109,6 +112,12 @@ impl<'a> VNode<'a> {
     }
 }
 
+#[derive(Clone)]
+pub struct VText<'src> {
+    pub text: &'src str,
+    pub dom_id: Cell<RealDomNode>,
+}
+
 // ========================================================
 //   VElement (div, h1, etc), attrs, keys, listener handle
 // ========================================================
@@ -120,6 +129,7 @@ pub struct VElement<'a> {
     pub attributes: &'a [Attribute<'a>],
     pub children: &'a [VNode<'a>],
     pub namespace: Option<&'a str>,
+    pub dom_id: Cell<RealDomNode>,
 }
 
 /// An attribute on a DOM node, such as `id="my-thing"` or
@@ -224,7 +234,7 @@ pub type VCompAssociatedScope = Option<ScopeIdx>;
 pub struct VComponent<'src> {
     pub key: NodeKey<'src>,
 
-    pub stable_addr: RefCell<StableScopeAddres>,
+    pub mounted_root: RealDomNode,
     pub ass_scope: RefCell<VCompAssociatedScope>,
 
     // pub comparator: Rc<dyn Fn(&VComponent) -> bool + 'src>,
@@ -325,7 +335,7 @@ impl<'a> VComponent<'a> {
             raw_props,
             children,
             caller,
-            stable_addr: RefCell::new(None),
+            mounted_root: RealDomNode::empty(),
         }
     }
 }

+ 34 - 15
packages/core/src/virtual_dom.rs

@@ -63,6 +63,14 @@ pub struct VirtualDom {
     _root_prop_type: std::any::TypeId,
 }
 
+#[derive(Clone, Copy)]
+pub struct RealDomNode(u32);
+impl RealDomNode {
+    pub fn empty() -> Self {
+        Self(u32::MIN)
+    }
+}
+
 // ======================================
 // Public Methods for the VirtualDom
 // ======================================
@@ -174,10 +182,15 @@ impl VirtualDom {
             _root_prop_type: TypeId::of::<P>(),
         }
     }
+}
 
+// ======================================
+// Private Methods for the VirtualDom
+// ======================================
+impl VirtualDom {
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
     /// Currently this doesn't do what we want it to do
-    pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
+    pub fn rebuild<'s, Dom: RealDom>(&'s mut self, realdom: &mut Dom) -> Result<()> {
         let mut diff_machine = DiffMachine::new(
             self.components.clone(),
             self.base_scope,
@@ -193,16 +206,10 @@ impl VirtualDom {
         let update = &base.event_channel;
         update();
 
-        self.progress_completely(&mut diff_machine)?;
+        self.progress_completely(realdom, &mut diff_machine)?;
 
-        Ok(diff_machine.consume())
+        Ok(())
     }
-}
-
-// ======================================
-// Private Methods for the VirtualDom
-// ======================================
-impl VirtualDom {
     /// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
     ///  
     /// Given a synthetic event, the component that triggered the event, and the index of the callback, this runs the virtual
@@ -246,7 +253,11 @@ impl VirtualDom {
     // but the guarantees provide a safe, fast, and efficient abstraction for the VirtualDOM updating framework.
     //
     // A good project would be to remove all unsafe from this crate and move the unsafety into safer abstractions.
-    pub fn progress_with_event(&mut self, event: EventTrigger) -> Result<EditList> {
+    pub fn progress_with_event<Dom: RealDom>(
+        &mut self,
+        realdom: &mut Dom,
+        event: EventTrigger,
+    ) -> Result<()> {
         let id = event.component_id.clone();
 
         self.components.try_get_mut(id)?.call_listener(event)?;
@@ -254,18 +265,19 @@ impl VirtualDom {
         let mut diff_machine =
             DiffMachine::new(self.components.clone(), id, self.event_queue.clone());
 
-        self.progress_completely(&mut diff_machine)?;
+        self.progress_completely(realdom, &mut diff_machine)?;
 
-        Ok(diff_machine.consume())
+        Ok(())
     }
 
     /// Consume the event queue, descending depth-first.
     /// Only ever run each component once.
     ///
     /// The DiffMachine logs its progress as it goes which might be useful for certain types of renderers.
-    pub(crate) fn progress_completely<'s>(
+    pub(crate) fn progress_completely<'s, Dom: RealDom>(
         &'s mut self,
-        diff_machine: &'_ mut DiffMachine<'s>,
+        realdom: &mut Dom,
+        diff_machine: &'_ mut DiffMachine,
     ) -> Result<()> {
         // Add this component to the list of components that need to be difed
         // #[allow(unused_assignments)]
@@ -302,7 +314,8 @@ impl VirtualDom {
             cur_component.run_scope()?;
             // diff_machine.change_list.load_known_root(1);
 
-            diff_machine.diff_node(cur_component.old_frame(), cur_component.next_frame());
+            let (old, new) = cur_component.get_frames_mut();
+            diff_machine.diff_node(realdom, old, new);
 
             // cur_height = cur_component.height;
 
@@ -530,6 +543,12 @@ impl Scope {
         Ok(())
     }
 
+    fn get_frames_mut<'bump>(
+        &'bump mut self,
+    ) -> (&'bump mut VNode<'bump>, &'bump mut VNode<'bump>) {
+        todo!()
+    }
+
     pub fn next_frame<'bump>(&'bump self) -> &'bump VNode<'bump> {
         self.frames.current_head_node()
     }