Explorar el Código

Feat: major overhaul to diffing, using a "diffing machine" now

Jonathan Kelley hace 4 años
padre
commit
4dfdf91

+ 6 - 6
README.md

@@ -15,19 +15,19 @@ static Example: FC<()> = |ctx| {
 
     ctx.view(html! {
         <div>
-            <button onclick={move |_| set_value("world!")}> "world" </button>
-            <button onclick={move |_| set_value("dioxus 🎉")}> "dioxus" </button>
+            <button onclick={move |_| set_value("world!")}> "?" </button>
+            <button onclick={move |_| set_value("Dioxus 🎉")}> "?" </button>
             <div>
-                <p> "Hello, {val1}" </p>
+                <h1> "Hello, {val1}" </h1>
             </div>
         </div>
     })
 };
 ```
-Dioxus can be used to serve webapps, desktop apps, static pages, LiveView apps, Android apps, iOS Apps, and more. At its core,
-Dioxus is entirely renderer agnostic and has great documentation for creating new renderers for any platform.
 
-Dioxus is supported by Dioxus Labs, a company providing end-to-end services for building, testing, deploying, and managing Dioxus apps on all supported platforms, designed especially for your next startup. 
+Dioxus can be used to deliver webapps, desktop apps, static pages, liveview apps, Android apps, iOS Apps, and more. At its core, Dioxus is entirely renderer agnostic and has great documentation for creating new renderers for any platform.
+
+Dioxus is supported by Dioxus Labs, a company providing end-to-end services for building, testing, deploying, and managing Dioxus apps on all supported platforms, designed especially for your next startup.
 
 ### Get Started with...
 <table style="width:100%" align="center">

+ 4 - 0
examples/Cargo.toml

@@ -45,6 +45,10 @@ path = "common.rs"
 path = "example_app.rs"
 name = "example_app"
 
+[[example]]
+path = "website.rs"
+name = "website"
+
 # [[example]]
 # path = "hello_web.rs"
 # name = "hello_web"

+ 45 - 0
examples/website.rs

@@ -0,0 +1,45 @@
+//! DioxusLabs Webiste
+//! ------------------
+//!
+//! This is the example powering the DioxusLabs website :)
+//! It's always good to dogfood your code, right?
+
+use dioxus::prelude::*;
+
+fn main() {}
+
+mod state {
+    pub struct AppState {
+        cur_page: Route,
+    }
+
+    pub enum Route {
+        Homepage,
+        Docs,
+    }
+}
+
+static APP: FC<()> = |ctx, props| {
+    ctx.view(html! {
+        <div>
+
+        <div>
+    })
+};
+
+/// Draw the navbar on top of the screen
+static Navbar: FC<state::Route> = |ctx, props| {
+    ctx.view(html! {
+        <div>
+
+        <div>
+    })
+};
+
+static Homepage: FC<()> = |ctx, props| {
+    ctx.view(html! {
+        <div>
+
+        <div>
+    })
+};

+ 2 - 0
packages/core/Cargo.toml

@@ -29,3 +29,5 @@ typed-arena = "2.0.1"
 toolshed = "0.8.1"
 id-arena = "2.2.1"
 thiserror = "1.0.23"
+fxhash = "0.2.1"
+longest-increasing-subsequence = "0.1.0"

+ 116 - 93
packages/core/src/changelist.rs

@@ -21,50 +21,50 @@
 
 use crate::innerlude::{Listener, VirtualDom};
 
-/// Renderers need to implement the interpreter trait
+/// The `Edit` represents a single modifcation of the renderer tree.
 ///
 ///
 ///
 ///
 ///
-trait Inrerpreter {
-    fn set_text(&mut self, text: &str);
-    fn remove_self_and_next_siblings(&mut self);
-    fn replace_with(&mut self);
-    fn set_attribute(&mut self, name: &str, value: &str);
-    fn remove_attribute(&mut self, name: &str);
-    fn push_reverse_child(&mut self, n: u32);
-    fn pop_push_child(&mut self, n: u32);
-    fn pop(&mut self);
-    fn append_child(&mut self);
-    fn create_text_node(&mut self, text: &str);
-    fn create_element(&mut self, tag_name: &str);
-    fn new_event_listener(&mut self, event_type: &str, a: u32, b: u32);
-    fn update_event_listener(&mut self, event_type: &str, a: u32, b: u32);
-    fn remove_event_listener(&mut self, event_type: &str);
-    fn create_element_ns(&mut self, tag_name: &str, ns: &str);
-    fn save_children_to_temporaries(&mut self, temp: u32, start: u32, end: u32);
-    // fn save_children_to_temporaries(&mut self, mut temp: u32, start: u32, end: u32);
-    fn push_child(&mut self, n: u32);
-    fn push_temporary(&mut self, temp: u32);
-    fn insert_before(&mut self);
-    fn pop_push_reverse_child(&mut self, n: u32);
-    fn remove_child(&mut self, n: u32);
-    fn set_class(&mut self, class_name: &str);
-    // fn save_template(&mut self, id: CacheId);
-    // fn push_template(&mut self, id: CacheId);
+///
+///
+///
+///
+pub enum Edit<'d> {
+    SetText { text: &'d str },
+    RemoveSelfAndNextSiblings {},
+    ReplaceWith,
+    SetAttribute { name: &'d str, value: &'d str },
+    RemoveAttribute { name: &'d str },
+    PushReverseChild { n: u32 },
+    PopPushChild { n: u32 },
+    Pop,
+    AppendChild,
+    CreateTextNode { text: &'d str },
+    CreateElement { tag_name: &'d str },
+    NewEventListener { event_type: &'d str, a: u32, b: u32 },
+    UpdateEventListener { event_type: &'d str, a: u32, b: u32 },
+    RemoveEventListener { event_type: &'d str },
+    CreateElementNs { tag_name: &'d str, ns: &'d str },
+    SaveChildrenToTemporaries { temp: u32, start: u32, end: u32 },
+    PushChild { n: u32 },
+    PushTemporary { temp: u32 },
+    InsertBefore,
+    PopPushReverseChild { n: u32 },
+    RemoveChild { n: u32 },
+    SetClass { class_name: &'d str },
 }
 
-pub struct ChangeList<'src> {
+pub struct EditList<'src> {
     traversal: Traversal,
     next_temporary: u32,
     forcing_new_listeners: bool,
-    emitter: &'src mut dyn Inrerpreter,
-    domlock: &'src VirtualDom,
+    emitter: Vec<Edit<'src>>,
 }
 
 /// Traversal methods.
-impl ChangeList<'_> {
+impl EditList<'_> {
     pub fn go_down(&mut self) {
         self.traversal.down();
     }
@@ -102,32 +102,36 @@ impl ChangeList<'_> {
         }
 
         for mv in self.traversal.commit() {
-            // match mv {
-            //     MoveTo::Parent => {
-            //         // debug!("emit: pop");
-            //         self.emitter.pop();
-            //     }
-            //     MoveTo::Child(n) => {
-            //         // debug!("emit: push_child({})", n);
-            //         self.emitter.push_child(n);
-            //     }
-            //     MoveTo::ReverseChild(n) => {
-            //         // debug!("emit: push_reverse_child({})", n);
-            //         self.emitter.push_reverse_child(n);
-            //     }
-            //     MoveTo::Sibling(n) => {
-            //         // debug!("emit: pop_push_child({})", n);
-            //         self.emitter.pop_push_child(n);
-            //     }
-            //     MoveTo::ReverseSibling(n) => {
-            //         // debug!("emit: pop_push_reverse_child({})", n);
-            //         self.emitter.pop_push_reverse_child(n);
-            //     }
-            //     MoveTo::TempChild(temp) => {
-            //         // debug!("emit: push_temporary({})", temp);
-            //         self.emitter.push_temporary(temp);
-            //     }
-            // }
+            match mv {
+                MoveTo::Parent => {
+                    // debug!("emit: pop");
+                    self.emitter.push(Edit::Pop {});
+                    // self.emitter.pop();
+                }
+                MoveTo::Child(n) => {
+                    // debug!("emit: push_child({})", n);
+                    self.emitter.push(Edit::PushChild { n });
+                }
+                MoveTo::ReverseChild(n) => {
+                    // debug!("emit: push_reverse_child({})", n);
+                    self.emitter.push(Edit::PushReverseChild { n });
+                    // self.emitter.push_reverse_child(n);
+                }
+                MoveTo::Sibling(n) => {
+                    // debug!("emit: pop_push_child({})", n);
+                    self.emitter.push(Edit::PopPushChild { n });
+                    // self.emitter.pop_push_child(n);
+                }
+                MoveTo::ReverseSibling(n) => {
+                    // debug!("emit: pop_push_reverse_child({})", n);
+                    self.emitter.push(Edit::PopPushReverseChild { n });
+                }
+                MoveTo::TempChild(temp) => {
+                    // debug!("emit: push_temporary({})", temp);
+                    self.emitter.push(Edit::PushTemporary { temp });
+                    // self.emitter.push_temporary(temp);
+                }
+            }
         }
     }
 
@@ -136,7 +140,7 @@ impl ChangeList<'_> {
     }
 }
 
-impl ChangeList<'_> {
+impl<'a> EditList<'a> {
     pub fn next_temporary(&self) -> u32 {
         self.next_temporary
     }
@@ -154,27 +158,33 @@ impl ChangeList<'_> {
         //     temp_base, start, end
         // );
         self.next_temporary = temp_base + (end - start) as u32;
-        self.emitter
-            .save_children_to_temporaries(temp_base, start as u32, end as u32);
+        self.emitter.push(Edit::SaveChildrenToTemporaries {
+            temp: temp_base,
+            start: start as u32,
+            end: end as u32,
+        });
         temp_base
     }
 
     pub fn push_temporary(&mut self, temp: u32) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: push_temporary({})", temp);
-        self.emitter.push_temporary(temp);
+        self.emitter.push(Edit::PushTemporary { temp });
+        // self.emitter.push_temporary(temp);
     }
 
     pub fn remove_child(&mut self, child: usize) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: remove_child({})", child);
-        self.emitter.remove_child(child as u32);
+        // self.emitter.remove_child(child as u32);
+        self.emitter.push(Edit::RemoveChild { n: child as u32 })
     }
 
     pub fn insert_before(&mut self) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: insert_before()");
-        self.emitter.insert_before();
+        // self.emitter.insert_before();
+        self.emitter.push(Edit::InsertBefore {})
     }
 
     pub fn ensure_string(&mut self, string: &str) -> StringKey {
@@ -182,45 +192,52 @@ impl ChangeList<'_> {
         // self.strings.ensure_string(string, &self.emitter)
     }
 
-    pub fn set_text(&mut self, text: &str) {
+    pub fn set_text(&mut self, text: &'a str) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: set_text({:?})", text);
-        self.emitter.set_text(text);
+        // self.emitter.set_text(text);
+        self.emitter.push(Edit::SetText { text });
         // .set_text(text.as_ptr() as u32, text.len() as u32);
     }
 
     pub fn remove_self_and_next_siblings(&mut self) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: remove_self_and_next_siblings()");
-        self.emitter.remove_self_and_next_siblings();
+        self.emitter.push(Edit::RemoveSelfAndNextSiblings {});
+        // self.emitter.remove_self_and_next_siblings();
     }
 
     pub fn replace_with(&mut self) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: replace_with()");
-        self.emitter.replace_with();
+        self.emitter.push(Edit::ReplaceWith {});
+        // self.emitter.replace_with();
     }
 
-    pub fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {
+    pub fn set_attribute(&mut self, name: &'a str, value: &'a str, is_namespaced: bool) {
         debug_assert!(self.traversal_is_committed());
-        todo!()
-        // if name == "class" && !is_namespaced {
-        //     let class_id = self.ensure_string(value);
-        //     // debug!("emit: set_class({:?})", value);
-        //     self.emitter.set_class(class_id.into());
-        // } else {
-        //     let name_id = self.ensure_string(name);
-        //     let value_id = self.ensure_string(value);
-        //     // debug!("emit: set_attribute({:?}, {:?})", name, value);
-        //     self.state
-        //         .emitter
-        //         .set_attribute(name_id.into(), value_id.into());
-        // }
-    }
-
-    pub fn remove_attribute(&mut self, name: &str) {
+        // todo!()
+        if name == "class" && !is_namespaced {
+            // let class_id = self.ensure_string(value);
+            // let class_id = self.ensure_string(value);
+            // debug!("emit: set_class({:?})", value);
+            // self.emitter.set_class(class_id.into());
+            self.emitter.push(Edit::SetClass { class_name: value });
+        } else {
+            self.emitter.push(Edit::SetAttribute { name, value });
+            // let name_id = self.ensure_string(name);
+            // let value_id = self.ensure_string(value);
+            // debug!("emit: set_attribute({:?}, {:?})", name, value);
+            // self.state
+            //     .emitter
+            //     .set_attribute(name_id.into(), value_id.into());
+        }
+    }
+
+    pub fn remove_attribute(&mut self, name: &'a str) {
         // todo!("figure out how to get this working with ensure string");
-        self.emitter.remove_attribute(name);
+        self.emitter.push(Edit::RemoveAttribute { name });
+        // self.emitter.remove_attribute(name);
         // debug_assert!(self.traversal_is_committed());
         // // debug!("emit: remove_attribute({:?})", name);
         // let name_id = self.ensure_string(name);
@@ -230,29 +247,33 @@ impl ChangeList<'_> {
     pub fn append_child(&mut self) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: append_child()");
-        self.emitter.append_child();
+        self.emitter.push(Edit::AppendChild {});
+        // self.emitter.append_child();
     }
 
-    pub fn create_text_node(&mut self, text: &str) {
+    pub fn create_text_node(&mut self, text: &'a str) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: create_text_node({:?})", text);
-        self.emitter.create_text_node(text);
+        // self.emitter.create_text_node(text);
+        self.emitter.push(Edit::CreateTextNode { text });
     }
 
-    pub fn create_element(&mut self, tag_name: &str) {
+    pub fn create_element(&mut self, tag_name: &'a str) {
         // debug_assert!(self.traversal_is_committed());
         // debug!("emit: create_element({:?})", tag_name);
         // let tag_name_id = self.ensure_string(tag_name);
-        self.emitter.create_element(tag_name);
+        self.emitter.push(Edit::CreateElement { tag_name });
+        // self.emitter.create_element(tag_name);
         // self.emitter.create_element(tag_name_id.into());
     }
 
-    pub fn create_element_ns(&mut self, tag_name: &str, ns: &str) {
+    pub fn create_element_ns(&mut self, tag_name: &'a str, ns: &'a str) {
         debug_assert!(self.traversal_is_committed());
         // debug!("emit: create_element_ns({:?}, {:?})", tag_name, ns);
         // let tag_name_id = self.ensure_string(tag_name);
         // let ns_id = self.ensure_string(ns);
-        self.emitter.create_element_ns(tag_name, ns);
+        // self.emitter.create_element_ns(tag_name, ns);
+        self.emitter.push(Edit::CreateElementNs { tag_name, ns });
         // self.emitter
         //     .create_element_ns(tag_name_id.into(), ns_id.into());
     }
@@ -455,7 +476,9 @@ impl Traversal {
     /// that have *not* been committed yet?
     #[inline]
     pub fn is_committed(&self) -> bool {
-        self.uncommitted.is_empty()
+        // is_empty is not inlined?
+        // self.uncommitted.is_empty()
+        self.uncommitted.len() == 0
     }
 
     /// Commit this traversals moves and return the optimized path from the last

+ 561 - 609
packages/core/src/diff.rs

@@ -1,186 +1,214 @@
-use crate::innerlude::{VNode, VText};
-
-// Diff the `old` node with the `new` node. Emits instructions to modify a
-// physical DOM node that reflects `old` into something that reflects `new`.
-//
-// 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.
-pub(crate) fn diff(
-    // cached_set: &CachedSet,
-    // change_list: &mut ChangeListBuilder,
-    // registry: &mut EventsRegistry,
-    old: &VNode,
-    new: &VNode,
-    // cached_roots: &mut FxHashSet<CacheId>,
-) {
-    match (old, new) {
-        // This case occurs when we generate two text nodes that are the sa
-        (VNode::Text(VText { text: old_text }), VNode::Text(VText { text: new_text })) => {
-            //
-            if old_text != new_text {}
-        }
-
-        // compare elements
-        // if different, schedule different types of update
-        (VNode::Element(_), VNode::Element(_)) => {}
-        (VNode::Element(_), VNode::Text(_)) => {}
-        (VNode::Element(_), VNode::Component(_)) => {}
-        (VNode::Text(_), VNode::Element(_)) => {}
-        (VNode::Text(_), VNode::Component(_)) => {}
+/// Diff the `old` node with the `new` node. Emits instructions to modify a
+/// physical DOM node that reflects `old` into something that reflects `new`.
+///
+/// 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.
+///
+/// ----
+///
+/// There are more ways of increasing diff performance here that are currently not implemented.
+/// Additionally, the caching mechanism has also been tweaked.
+///
+/// Instead of having "cached" nodes, each component is, by default, a cached node. This leads to increased
+/// memory overhead for large numbers of small components, but we can optimize this by tracking alloc size over time
+/// and shrinking bumps down if possible.
+///
+/// Additionally, clean up of these components is not done at diff time (though it should), but rather, the diffing
+/// proprogates removal lifecycle events for affected components into the event queue. It's not imperative that these
+/// are ran immediately, but it should be noted that cleanup of components might be able to emit changes.
+///
+/// This diffing only ever occurs on a component-by-component basis (not entire trees at once).
+///
+/// Currently, the listener situation is a bit broken.
+/// We aren't removing listeners (choosing to leak them instead) :(
+/// Eventually, we'll set things up so add/remove listener is an instruction again
+///
+/// A major assumption of this diff algorithm when combined with the ChangeList is that the Changelist will be
+/// fresh and the event queue is clean. This lets us continue to batch edits together under the same ChangeList
+///
+/// More info on how to improve this diffing algorithm:
+///  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
+use fxhash::{FxHashMap, FxHashSet};
+use generational_arena::Index;
+
+use crate::{
+    changelist::EditList,
+    innerlude::{Attribute, Listener, Scope, VElement, VNode, VText},
+    virtual_dom::LifecycleEvent,
+};
+
+use std::cmp::Ordering;
+
+/// 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.
+///
+/// By re-entering via NodeDiff, we can connect disparate edits together into a single EditList. This batching of edits
+/// leads to very fast re-renders (all done in a single animation frame).
+///
+/// It also means diffing two trees is only ever complex as diffing a single smaller tree, and then re-entering at a
+/// different cursor position.
+///
+/// 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 wereboth updated via
+/// subscriptions and props changes.
+struct DiffingMachine<'a> {
+    change_list: &'a mut EditList<'a>,
+    queue: FxHashMap<Index, NeedToDiff>,
+}
 
-        (VNode::Component(_), VNode::Element(_)) => {}
-        (VNode::Component(_), VNode::Text(_)) => {}
+enum NeedToDiff {
+    PropsChanged,
+    Subscription,
+}
 
-        // No immediate change to dom. If props changed though, queue a "props changed" update
-        // However, mark these for a
-        (VNode::Component(_), VNode::Component(_)) => {}
-        //
-        (VNode::Suspended, _) | (_, VNode::Suspended) => {
-            todo!("Suspended components not currently available")
-        } // (VNode::Element(_), VNode::Suspended) => {}
-          // (VNode::Text(_), VNode::Suspended) => {}
-          // (VNode::Component(_), VNode::Suspended) => {}
-          // (VNode::Suspended, VNode::Element(_)) => {}
-          // (VNode::Suspended, VNode::Text(_)) => {}
-          // (VNode::Suspended, VNode::Suspended) => {}
-          // (VNode::Suspended, VNode::Component(_)) => {}
-    }
-    todo!()
-    // match (&new.kind, &old.kind) {
-    //     (
-    //         &NodeKind::Text(TextNode { text: new_text }),
-    //         &NodeKind::Text(TextNode { text: old_text }),
-    //     ) => {
-    //         if new_text != old_text {
-    //             change_list.commit_traversal();
-    //             change_list.set_text(new_text);
-    //         }
-    //     }
+impl<'a> DiffingMachine<'a> {
+    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.
+        Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
+        When re-entering, we reuse the EditList in DiffState
+        */
+        match (old, new) {
+            // This case occurs when two text nodes are generation
+            (VNode::Text(VText { text: old_text }), VNode::Text(VText { text: new_text })) => {
+                if old_text != new_text {
+                    self.change_list.commit_traversal();
+                    self.change_list.set_text(new_text);
+                }
+            }
 
-    //     (&NodeKind::Text(_), &NodeKind::Element(_)) => {
-    //         change_list.commit_traversal();
-    //         create(cached_set, change_list, registry, new, cached_roots);
-    //         registry.remove_subtree(&old);
-    //         change_list.replace_with();
-    //     }
+            // Definitely different, need to commit update
+            (VNode::Text(_), VNode::Element(_)) => {
+                // TODO: Hook up the events properly
+                // todo!("Hook up events registry");
+                self.change_list.commit_traversal();
+                // diff_support::create(cached_set, self.change_list, registry, new, cached_roots);
+                self.create(new);
+                // registry.remove_subtree(&old);
+                self.change_list.replace_with();
+            }
 
-    //     (&NodeKind::Element(_), &NodeKind::Text(_)) => {
-    //         change_list.commit_traversal();
-    //         create(cached_set, change_list, registry, new, cached_roots);
-    //         // Note: text nodes cannot have event listeners, so we don't need to
-    //         // remove the old node's listeners from our registry her.
-    //         change_list.replace_with();
-    //     }
+            // Definitely different, need to commit update
+            (VNode::Element(_), VNode::Text(_)) => {
+                self.change_list.commit_traversal();
+                self.create(new);
 
-    //     (
-    //         &NodeKind::Element(ElementNode {
-    //             key: _,
-    //             tag_name: new_tag_name,
-    //             listeners: new_listeners,
-    //             attributes: new_attributes,
-    //             children: new_children,
-    //             namespace: new_namespace,
-    //         }),
-    //         &NodeKind::Element(ElementNode {
-    //             key: _,
-    //             tag_name: old_tag_name,
-    //             listeners: old_listeners,
-    //             attributes: old_attributes,
-    //             children: old_children,
-    //             namespace: old_namespace,
-    //         }),
-    //     ) => {
-    //         if new_tag_name != old_tag_name || new_namespace != old_namespace {
-    //             change_list.commit_traversal();
-    //             create(cached_set, change_list, registry, new, cached_roots);
-    //             registry.remove_subtree(&old);
-    //             change_list.replace_with();
-    //             return;
-    //         }
-    //         diff_listeners(change_list, registry, old_listeners, new_listeners);
-    //         diff_attributes(
-    //             change_list,
-    //             old_attributes,
-    //             new_attributes,
-    //             new_namespace.is_some(),
-    //         );
-    //         diff_children(
-    //             cached_set,
-    //             change_list,
-    //             registry,
-    //             old_children,
-    //             new_children,
-    //             cached_roots,
-    //         );
-    //     }
+                // create(cached_set, self.change_list, registry, new, cached_roots);
+                // Note: text nodes cannot have event listeners, so we don't need to
+                // remove the old node's listeners from our registry her.
+                self.change_list.replace_with();
+            }
+            // compare elements
+            // if different, schedule different types of update
+            (VNode::Element(eold), VNode::Element(enew)) => {
+                // 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();
+                    // create(cached_set, self.change_list, registry, new, cached_roots);
+                    // registry.remove_subtree(&old);
+                    self.change_list.replace_with();
+                    return;
+                }
 
-    //     // Both the new and old nodes are cached.
-    //     (&NodeKind::Cached(ref new), &NodeKind::Cached(ref old)) => {
-    //         cached_roots.insert(new.id);
+                self.diff_listeners(eold.listeners, enew.listeners);
 
-    //         if new.id == old.id {
-    //             // This is the same cached node, so nothing has changed!
-    //             return;
-    //         }
+                self.diff_attr(eold.attributes, enew.attributes, enew.namespace.is_some());
 
-    //         let (new, new_template) = cached_set.get(new.id);
-    //         let (old, old_template) = cached_set.get(old.id);
-    //         if new_template == old_template {
-    //             // If they are both using the same template, then just diff the
-    //             // subtrees.
-    //             diff(cached_set, change_list, registry, old, new, cached_roots);
-    //         } else {
-    //             // Otherwise, they are probably different enough that
-    //             // re-constructing the subtree from scratch should be faster.
-    //             // This doubly holds true if we have a new template.
-    //             change_list.commit_traversal();
-    //             create_and_replace(
-    //                 cached_set,
-    //                 change_list,
-    //                 registry,
-    //                 new_template,
-    //                 old,
-    //                 new,
-    //                 cached_roots,
-    //             );
-    //         }
-    //     }
+                self.diff_children(eold.children, enew.children);
+            }
+            // No immediate change to dom. If props changed though, queue a "props changed" update
+            // However, mark these for a
+            (VNode::Component(_), VNode::Component(_)) => {
+                todo!("Usage of component VNode not currently supported");
+                //     // Both the new and old nodes are cached.
+                //     (&NodeKind::Cached(ref new), &NodeKind::Cached(ref old)) => {
+                //         cached_roots.insert(new.id);
+                //         if new.id == old.id {
+                //             // This is the same cached node, so nothing has changed!
+                //             return;
+                //         }
+                //         let (new, new_template) = cached_set.get(new.id);
+                //         let (old, old_template) = cached_set.get(old.id);
+                //         if new_template == old_template {
+                //             // If they are both using the same template, then just diff the
+                //             // subtrees.
+                //             diff(cached_set, change_list, registry, old, new, cached_roots);
+                //         } else {
+                //             // Otherwise, they are probably different enough that
+                //             // re-constructing the subtree from scratch should be faster.
+                //             // This doubly holds true if we have a new template.
+                //             change_list.commit_traversal();
+                //             create_and_replace(
+                //                 cached_set,
+                //                 change_list,
+                //                 registry,
+                //                 new_template,
+                //                 old,
+                //                 new,
+                //                 cached_roots,
+                //             );
+                //         }
+                //     }
+                // queue a lifecycle event.
+                // no change
+            }
 
-    //     // New cached node when the old node was not cached. In this scenario,
-    //     // we assume that they are pretty different, and it isn't worth diffing
-    //     // the subtrees, so we just create the new cached node afresh.
-    //     (&NodeKind::Cached(ref c), _) => {
-    //         change_list.commit_traversal();
-    //         cached_roots.insert(c.id);
-    //         let (new, new_template) = cached_set.get(c.id);
-    //         create_and_replace(
-    //             cached_set,
-    //             change_list,
-    //             registry,
-    //             new_template,
-    //             old,
-    //             new,
-    //             cached_roots,
-    //         );
-    //     }
+            // A component has been birthed!
+            // Queue its arrival
+            (_, VNode::Component(_)) => {
+                todo!("Usage of component VNode not currently supported");
+                //     // Old cached node and new non-cached node. Again, assume that they are
+                //     // probably pretty different and create the new non-cached node afresh.
+                //     (_, &NodeKind::Cached(_)) => {
+                //         change_list.commit_traversal();
+                //         create(cached_set, change_list, registry, new, cached_roots);
+                //         registry.remove_subtree(&old);
+                //         change_list.replace_with();
+                //     }
+                // }
+            }
 
-    //     // Old cached node and new non-cached node. Again, assume that they are
-    //     // probably pretty different and create the new non-cached node afresh.
-    //     (_, &NodeKind::Cached(_)) => {
-    //         change_list.commit_traversal();
-    //         create(cached_set, change_list, registry, new, cached_roots);
-    //         registry.remove_subtree(&old);
-    //         change_list.replace_with();
-    //     }
-    // }
-}
+            // A component was removed :(
+            // Queue its removal
+            (VNode::Component(_), _) => {
+                //     // New cached node when the old node was not cached. In this scenario,
+                //     // we assume that they are pretty different, and it isn't worth diffing
+                //     // the subtrees, so we just create the new cached node afresh.
+                //     (&NodeKind::Cached(ref c), _) => {
+                //         change_list.commit_traversal();
+                //         cached_roots.insert(c.id);
+                //         let (new, new_template) = cached_set.get(c.id);
+                //         create_and_replace(
+                //             cached_set,
+                //             change_list,
+                //             registry,
+                //             new_template,
+                //             old,
+                //             new,
+                //             cached_roots,
+                //         );
+                //     }
+                todo!("Usage of component VNode not currently supported");
+            }
 
-#[cfg(predicate)]
-mod todo_diff {
+            // A suspended component appeared!
+            // Don't do much, just wait
+            (VNode::Suspended, _) | (_, VNode::Suspended) => {
+                // (VNode::Element(_), VNode::Suspended) => {}
+                // (VNode::Text(_), VNode::Suspended) => {}
+                // (VNode::Component(_), VNode::Suspended) => {}
+                // (VNode::Suspended, VNode::Element(_)) => {}
+                // (VNode::Suspended, VNode::Text(_)) => {}
+                // (VNode::Suspended, VNode::Suspended) => {}
+                // (VNode::Suspended, VNode::Component(_)) => {}
+                todo!("Suspended components not currently available")
+            }
+        }
+    }
 
     // Diff event listeners between `old` and `new`.
     //
@@ -189,14 +217,9 @@ mod todo_diff {
     //     [... node]
     //
     // The change list stack is left unchanged.
-    fn diff_listeners(
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Listener],
-        new: &[Listener],
-    ) {
+    fn diff_listeners(&mut self, old: &[Listener<'a>], new: &[Listener<'a>]) {
         if !old.is_empty() || !new.is_empty() {
-            change_list.commit_traversal();
+            self.change_list.commit_traversal();
         }
 
         'outer1: for new_l in new {
@@ -204,28 +227,28 @@ mod todo_diff {
                 // Safety relies on removing `new_l` from the registry manually at
                 // the end of its lifetime. This happens below in the `'outer2`
                 // loop, and elsewhere in diffing when removing old dom trees.
-                registry.add(new_l);
+                // registry.add(new_l);
             }
 
             for old_l in old {
                 if new_l.event == old_l.event {
-                    change_list.update_event_listener(new_l);
+                    self.change_list.update_event_listener(new_l);
                     continue 'outer1;
                 }
             }
 
-            change_list.new_event_listener(new_l);
+            self.change_list.new_event_listener(new_l);
         }
 
         'outer2: for old_l in old {
-            registry.remove(old_l);
+            // registry.remove(old_l);
 
             for new_l in new {
                 if new_l.event == old_l.event {
                     continue 'outer2;
                 }
             }
-            change_list.remove_event_listener(old_l.event);
+            self.change_list.remove_event_listener(old_l.event);
         }
     }
 
@@ -236,31 +259,37 @@ mod todo_diff {
     //     [... node]
     //
     // The change list stack is left unchanged.
-    fn diff_attributes(
-        change_list: &mut ChangeListBuilder,
-        old: &[Attribute],
-        new: &[Attribute],
+    fn diff_attr(
+        &mut self,
+        old: &'a [Attribute<'a>],
+        new: &'a [Attribute<'a>],
         is_namespaced: bool,
     ) {
         // Do O(n^2) passes to add/update and remove attributes, since
         // there are almost always very few attributes.
         'outer: for new_attr in new {
             if new_attr.is_volatile() {
-                change_list.commit_traversal();
-                change_list.set_attribute(new_attr.name, new_attr.value, is_namespaced);
+                self.change_list.commit_traversal();
+                self.change_list
+                    .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 {
-                            change_list.commit_traversal();
-                            change_list.set_attribute(new_attr.name, new_attr.value, is_namespaced);
+                            self.change_list.commit_traversal();
+                            self.change_list.set_attribute(
+                                new_attr.name,
+                                new_attr.value,
+                                is_namespaced,
+                            );
                         }
                         continue 'outer;
                     }
                 }
 
-                change_list.commit_traversal();
-                change_list.set_attribute(new_attr.name, new_attr.value, is_namespaced);
+                self.change_list.commit_traversal();
+                self.change_list
+                    .set_attribute(new_attr.name, new_attr.value, is_namespaced);
             }
         }
 
@@ -271,8 +300,8 @@ mod todo_diff {
                 }
             }
 
-            change_list.commit_traversal();
-            change_list.remove_attribute(old_attr.name);
+            self.change_list.commit_traversal();
+            self.change_list.remove_attribute(old_attr.name);
         }
     }
 
@@ -284,18 +313,11 @@ mod todo_diff {
     //     [... parent]
     //
     // the change list stack is in the same state when this function returns.
-    fn diff_children(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-        new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
+    fn diff_children(&mut self, old: &'a [VNode<'a>], new: &'a [VNode<'a>]) {
         if new.is_empty() {
             if !old.is_empty() {
-                change_list.commit_traversal();
-                remove_all_children(change_list, registry, old);
+                self.change_list.commit_traversal();
+                self.remove_all_children(old);
             }
             return;
         }
@@ -303,36 +325,29 @@ mod todo_diff {
         if new.len() == 1 {
             match (old.first(), &new[0]) {
                 (
-                    Some(&Node {
-                        kind: NodeKind::Text(TextNode { text: old_text }),
-                    }),
-                    &Node {
-                        kind: NodeKind::Text(TextNode { text: new_text }),
-                    },
+                    Some(&VNode::Text(VText { text: old_text })),
+                    &VNode::Text(VText { text: new_text }),
                 ) if old_text == new_text => {
                     // Don't take this fast path...
                 }
-                (
-                    _,
-                    &Node {
-                        kind: NodeKind::Text(TextNode { text }),
-                    },
-                ) => {
-                    change_list.commit_traversal();
-                    change_list.set_text(text);
-                    for o in old {
-                        registry.remove_subtree(o);
-                    }
+
+                (_, &VNode::Text(VText { text })) => {
+                    self.change_list.commit_traversal();
+                    self.change_list.set_text(text);
+                    // for o in old {
+                    //     registry.remove_subtree(o);
+                    // }
                     return;
                 }
+
                 (_, _) => {}
             }
         }
 
         if old.is_empty() {
             if !new.is_empty() {
-                change_list.commit_traversal();
-                create_and_append_children(cached_set, change_list, registry, new, cached_roots);
+                self.change_list.commit_traversal();
+                self.create_and_append_children(new);
             }
             return;
         }
@@ -350,11 +365,14 @@ mod todo_diff {
         );
 
         if new_is_keyed && old_is_keyed {
-            let t = change_list.next_temporary();
-            diff_keyed_children(cached_set, change_list, registry, old, new, cached_roots);
-            change_list.set_next_temporary(t);
+            let t = self.change_list.next_temporary();
+            // diff_keyed_children(self.change_list, old, new);
+            // diff_keyed_children(self.change_list, old, new, cached_roots);
+            // diff_keyed_children(cached_set, self.change_list, registry, old, new, cached_roots);
+            self.change_list.set_next_temporary(t);
         } else {
-            diff_non_keyed_children(cached_set, change_list, registry, old, new, cached_roots);
+            self.diff_non_keyed_children(old, new);
+            // diff_non_keyed_children(cached_set, change_list, registry, old, new, cached_roots);
         }
     }
 
@@ -379,17 +397,12 @@ mod todo_diff {
     //     [... parent]
     //
     // Upon exiting, the change list stack is in the same state.
-    fn diff_keyed_children(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-        new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
+    fn diff_keyed_children(&mut self, old: &[VNode<'a>], new: &[VNode<'a>]) {
+        // let DiffState { change_list, queue } = &*state;
+
         if cfg!(debug_assertions) {
-            let mut keys = FxHashSet::default();
-            let mut assert_unique_keys = |children: &[Node]| {
+            let mut keys = fxhash::FxHashSet::default();
+            let mut assert_unique_keys = |children: &[VNode]| {
                 keys.clear();
                 for child in children {
                     let key = child.key();
@@ -414,11 +427,15 @@ mod todo_diff {
         //
         // `shared_prefix_count` is the count of how many nodes at the start of
         // `new` and `old` share the same keys.
-        let shared_prefix_count =
-            match diff_keyed_prefix(cached_set, change_list, registry, old, new, cached_roots) {
-                KeyedPrefixResult::Finished => return,
-                KeyedPrefixResult::MoreWorkToDo(count) => count,
-            };
+        let shared_prefix_count = match self.diff_keyed_prefix(old, new) {
+            KeyedPrefixResult::Finished => return,
+            KeyedPrefixResult::MoreWorkToDo(count) => count,
+        };
+
+        match self.diff_keyed_prefix(old, new) {
+            KeyedPrefixResult::Finished => return,
+            KeyedPrefixResult::MoreWorkToDo(count) => count,
+        };
 
         // Next, we find out how many of the nodes at the end of the children have
         // the same key. We do _not_ diff them yet, since we want to emit the change
@@ -440,13 +457,9 @@ mod todo_diff {
         // Ok, we now hopefully have a smaller range of children in the middle
         // within which to re-order nodes with the same keys, remove old nodes with
         // now-unused keys, and create new nodes with fresh keys.
-        diff_keyed_middle(
-            cached_set,
-            change_list,
-            registry,
+        self.diff_keyed_middle(
             &old[shared_prefix_count..old_shared_suffix_start],
             &new[shared_prefix_count..new_shared_suffix_start],
-            cached_roots,
             shared_prefix_count,
             shared_suffix_count,
             old_shared_suffix_start,
@@ -457,27 +470,10 @@ mod todo_diff {
         let new_suffix = &new[new_shared_suffix_start..];
         debug_assert_eq!(old_suffix.len(), new_suffix.len());
         if !old_suffix.is_empty() {
-            diff_keyed_suffix(
-                cached_set,
-                change_list,
-                registry,
-                old_suffix,
-                new_suffix,
-                cached_roots,
-                new_shared_suffix_start,
-            );
+            self.diff_keyed_suffix(old_suffix, new_suffix, new_shared_suffix_start)
         }
     }
 
-    enum KeyedPrefixResult {
-        // Fast path: we finished diffing all the children just by looking at the
-        // prefix of shared keys!
-        Finished,
-        // There is more diffing work to do. Here is a count of how many children at
-        // the beginning of `new` and `old` we already processed.
-        MoreWorkToDo(usize),
-    }
-
     // Diff the prefix of children in `new` and `old` that share the same keys in
     // the same order.
     //
@@ -486,15 +482,8 @@ mod todo_diff {
     //     [... parent]
     //
     // Upon exit, the change list stack is the same.
-    fn diff_keyed_prefix(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-        new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) -> KeyedPrefixResult {
-        change_list.go_down();
+    fn diff_keyed_prefix(&mut self, old: &[VNode<'a>], new: &[VNode<'a>]) -> KeyedPrefixResult {
+        self.change_list.go_down();
         let mut shared_prefix_count = 0;
 
         for (i, (old, new)) in old.iter().zip(new.iter()).enumerate() {
@@ -502,36 +491,32 @@ mod todo_diff {
                 break;
             }
 
-            change_list.go_to_sibling(i);
-            diff(cached_set, change_list, registry, old, new, cached_roots);
+            self.change_list.go_to_sibling(i);
+
+            self.diff_node(old, new);
+
             shared_prefix_count += 1;
         }
 
         // 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() {
-            change_list.go_up();
-            change_list.commit_traversal();
-            create_and_append_children(
-                cached_set,
-                change_list,
-                registry,
-                &new[shared_prefix_count..],
-                cached_roots,
-            );
+            self.change_list.go_up();
+            self.change_list.commit_traversal();
+            self.create_and_append_children(&new[shared_prefix_count..]);
             return KeyedPrefixResult::Finished;
         }
 
         // 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() {
-            change_list.go_to_sibling(shared_prefix_count);
-            change_list.commit_traversal();
-            remove_self_and_next_siblings(change_list, registry, &old[shared_prefix_count..]);
+            self.change_list.go_to_sibling(shared_prefix_count);
+            self.change_list.commit_traversal();
+            self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
             return KeyedPrefixResult::Finished;
         }
 
-        change_list.go_up();
+        self.change_list.go_up();
         KeyedPrefixResult::MoreWorkToDo(shared_prefix_count)
     }
 
@@ -549,12 +534,9 @@ mod todo_diff {
     //
     // Upon exit from this function, it will be restored to that same state.
     fn diff_keyed_middle(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-        mut new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
+        &mut self,
+        old: &[VNode<'a>],
+        mut new: &[VNode<'a>],
         shared_prefix_count: usize,
         shared_suffix_count: usize,
         old_shared_suffix_start: usize,
@@ -595,14 +577,16 @@ mod todo_diff {
         // afresh.
         if shared_suffix_count == 0 && shared_keys.is_empty() {
             if shared_prefix_count == 0 {
-                change_list.commit_traversal();
-                remove_all_children(change_list, registry, old);
+                self.change_list.commit_traversal();
+                self.remove_all_children(old);
             } else {
-                change_list.go_down_to_child(shared_prefix_count);
-                change_list.commit_traversal();
-                remove_self_and_next_siblings(change_list, registry, &old[shared_prefix_count..]);
+                self.change_list.go_down_to_child(shared_prefix_count);
+                self.change_list.commit_traversal();
+                self.remove_self_and_next_siblings(&old[shared_prefix_count..]);
             }
-            create_and_append_children(cached_set, change_list, registry, new, cached_roots);
+
+            self.create_and_append_children(new);
+
             return;
         }
 
@@ -619,8 +603,8 @@ mod todo_diff {
                 .unwrap_or(old.len());
 
             if end - start > 0 {
-                change_list.commit_traversal();
-                let mut t = change_list.save_children_to_temporaries(
+                self.change_list.commit_traversal();
+                let mut t = self.change_list.save_children_to_temporaries(
                     shared_prefix_count + start,
                     shared_prefix_count + end,
                 );
@@ -643,9 +627,10 @@ mod todo_diff {
         let mut removed_count = 0;
         for (i, old_child) in old.iter().enumerate().rev() {
             if !shared_keys.contains(&old_child.key()) {
-                registry.remove_subtree(old_child);
-                change_list.commit_traversal();
-                change_list.remove_child(i + shared_prefix_count);
+                // registry.remove_subtree(old_child);
+                // todo
+                self.change_list.commit_traversal();
+                self.change_list.remove_child(i + shared_prefix_count);
                 removed_count += 1;
             }
         }
@@ -688,47 +673,45 @@ mod todo_diff {
             // shared suffix to the change list stack.
             //
             // [... parent]
-            change_list.go_down_to_child(old_shared_suffix_start - removed_count);
+            self.change_list
+                .go_down_to_child(old_shared_suffix_start - removed_count);
         // [... parent first_child_of_shared_suffix]
         } else {
             // There is no shared suffix coming after these middle children.
             // Therefore we have to process the last child in `new` and move it to
             // the end of the parent's children if it isn't already there.
             let last_index = new.len() - 1;
-            let last = new.last().unwrap_throw();
+            // uhhhh why an unwrap?
+            let last = new.last().unwrap();
+            // let last = new.last().unwrap_throw();
             new = &new[..new.len() - 1];
             if shared_keys.contains(&last.key()) {
                 let old_index = new_index_to_old_index[last_index];
                 let temp = old_index_to_temp[old_index];
                 // [... parent]
-                change_list.go_down_to_temp_child(temp);
+                self.change_list.go_down_to_temp_child(temp);
                 // [... parent last]
-                diff(
-                    cached_set,
-                    change_list,
-                    registry,
-                    &old[old_index],
-                    last,
-                    cached_roots,
-                );
+                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 {
-                    change_list.commit_traversal();
+                    self.change_list.commit_traversal();
                     // [... parent last]
-                    change_list.append_child();
+                    self.change_list.append_child();
                     // [... parent]
-                    change_list.go_down_to_temp_child(temp);
+                    self.change_list.go_down_to_temp_child(temp);
                     // [... parent last]
                 }
             } else {
-                change_list.commit_traversal();
+                self.change_list.commit_traversal();
                 // [... parent]
-                create(cached_set, change_list, registry, last, cached_roots);
+                self.create(last);
+
                 // [... parent last]
-                change_list.append_child();
+                self.change_list.append_child();
                 // [... parent]
-                change_list.go_down_to_reverse_child(0);
+                self.change_list.go_down_to_reverse_child(0);
                 // [... parent last]
             }
         }
@@ -737,11 +720,11 @@ mod todo_diff {
             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()));
-                change_list.commit_traversal();
+                self.change_list.commit_traversal();
                 // [... parent successor]
-                create(cached_set, change_list, registry, new_child, cached_roots);
+                self.create(new_child);
                 // [... parent successor new_child]
-                change_list.insert_before();
+                self.change_list.insert_before();
             // [... parent new_child]
             } else {
                 debug_assert!(shared_keys.contains(&new_child.key()));
@@ -750,30 +733,23 @@ mod todo_diff {
 
                 if new_index_is_in_lis.contains(&new_index) {
                     // [... parent successor]
-                    change_list.go_to_temp_sibling(temp);
+                    self.change_list.go_to_temp_sibling(temp);
                 // [... parent new_child]
                 } else {
-                    change_list.commit_traversal();
+                    self.change_list.commit_traversal();
                     // [... parent successor]
-                    change_list.push_temporary(temp);
+                    self.change_list.push_temporary(temp);
                     // [... parent successor new_child]
-                    change_list.insert_before();
+                    self.change_list.insert_before();
                     // [... parent new_child]
                 }
 
-                diff(
-                    cached_set,
-                    change_list,
-                    registry,
-                    &old[old_index],
-                    new_child,
-                    cached_roots,
-                );
+                self.diff_node(&old[old_index], new_child);
             }
         }
 
         // [... parent child]
-        change_list.go_up();
+        self.change_list.go_up();
         // [... parent]
     }
 
@@ -785,35 +761,26 @@ mod todo_diff {
     //
     // When this function exits, the change list stack remains the same.
     fn diff_keyed_suffix(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-        new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
+        &mut self,
+        old: &[VNode<'a>],
+        new: &[VNode<'a>],
         new_shared_suffix_start: usize,
     ) {
         debug_assert_eq!(old.len(), new.len());
         debug_assert!(!old.is_empty());
 
         // [... parent]
-        change_list.go_down();
+        self.change_list.go_down();
         // [... parent new_child]
 
         for (i, (old_child, new_child)) in old.iter().zip(new.iter()).enumerate() {
-            change_list.go_to_sibling(new_shared_suffix_start + i);
-            diff(
-                cached_set,
-                change_list,
-                registry,
-                old_child,
-                new_child,
-                cached_roots,
-            );
+            self.change_list.go_to_sibling(new_shared_suffix_start + i);
+
+            self.diff_node(old_child, new_child);
         }
 
         // [... parent]
-        change_list.go_up();
+        self.change_list.go_up();
     }
 
     // Diff children that are not keyed.
@@ -824,128 +791,50 @@ mod todo_diff {
     //     [... parent]
     //
     // the change list stack is in the same state when this function returns.
-    fn diff_non_keyed_children(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-        new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
+    fn diff_non_keyed_children(&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]
-        change_list.go_down();
+        self.change_list.go_down();
         //     [... parent child]
 
         for (i, (new_child, old_child)) in new.iter().zip(old.iter()).enumerate() {
             // [... parent prev_child]
-            change_list.go_to_sibling(i);
+            self.change_list.go_to_sibling(i);
             // [... parent this_child]
-            diff(
-                cached_set,
-                change_list,
-                registry,
-                old_child,
-                new_child,
-                cached_roots,
-            );
+            self.diff_node(old_child, new_child);
         }
 
         match old.len().cmp(&new.len()) {
             Ordering::Greater => {
                 // [... parent prev_child]
-                change_list.go_to_sibling(new.len());
+                self.change_list.go_to_sibling(new.len());
                 // [... parent first_child_to_remove]
-                change_list.commit_traversal();
-                remove_self_and_next_siblings(change_list, registry, &old[new.len()..]);
+                self.change_list.commit_traversal();
+                // support::remove_self_and_next_siblings(state, &old[new.len()..]);
+                self.remove_self_and_next_siblings(&old[new.len()..]);
                 // [... parent]
             }
             Ordering::Less => {
                 // [... parent last_child]
-                change_list.go_up();
+                self.change_list.go_up();
                 // [... parent]
-                change_list.commit_traversal();
-                create_and_append_children(
-                    cached_set,
-                    change_list,
-                    registry,
-                    &new[old.len()..],
-                    cached_roots,
-                );
+                self.change_list.commit_traversal();
+                self.create_and_append_children(&new[old.len()..]);
             }
             Ordering::Equal => {
                 // [... parent child]
-                change_list.go_up();
+                self.change_list.go_up();
                 // [... parent]
             }
         }
     }
 
-    // Create the given children and append them to the parent node.
-    //
-    // The parent node must currently be on top of the change list stack:
-    //
-    //     [... parent]
-    //
-    // When this function returns, the change list stack is in the same state.
-    fn create_and_append_children(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        new: &[Node],
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
-        debug_assert!(change_list.traversal_is_committed());
-        for child in new {
-            create(cached_set, change_list, registry, child, cached_roots);
-            change_list.append_child();
-        }
-    }
-
-    // Remove all of a node's children.
-    //
-    // The change list stack must have this shape upon entry to this function:
-    //
-    //     [... parent]
-    //
-    // When this function returns, the change list stack is in the same state.
-    fn remove_all_children(
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-    ) {
-        debug_assert!(change_list.traversal_is_committed());
-        for child in old {
-            registry.remove_subtree(child);
-        }
-        // Fast way to remove all children: set the node's textContent to an empty
-        // string.
-        change_list.set_text("");
-    }
-
-    // Remove the current child and all of its following siblings.
-    //
-    // The change list stack must have this shape upon entry to this function:
-    //
-    //     [... parent child]
-    //
-    // After the function returns, the child is no longer on the change list stack:
-    //
-    //     [... parent]
-    fn remove_self_and_next_siblings(
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        old: &[Node],
-    ) {
-        debug_assert!(change_list.traversal_is_committed());
-        for child in old {
-            registry.remove_subtree(child);
-        }
-        change_list.remove_self_and_next_siblings();
-    }
+    // ======================
+    // Support methods
+    // ======================
 
     // Emit instructions to create the given virtual node.
     //
@@ -956,19 +845,13 @@ mod todo_diff {
     // When this function returns, the new node is on top of the change list stack:
     //
     //     [... node]
-    fn create(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        node: &Node,
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
-        debug_assert!(change_list.traversal_is_committed());
-        match node.kind {
-            NodeKind::Text(TextNode { text }) => {
-                change_list.create_text_node(text);
+    fn create(&mut self, node: &VNode<'a>) {
+        debug_assert!(self.change_list.traversal_is_committed());
+        match node {
+            VNode::Text(VText { text }) => {
+                self.change_list.create_text_node(text);
             }
-            NodeKind::Element(&ElementNode {
+            VNode::Element(&VElement {
                 key: _,
                 tag_name,
                 listeners,
@@ -977,20 +860,21 @@ mod todo_diff {
                 namespace,
             }) => {
                 if let Some(namespace) = namespace {
-                    change_list.create_element_ns(tag_name, namespace);
+                    self.change_list.create_element_ns(tag_name, namespace);
                 } else {
-                    change_list.create_element(tag_name);
+                    self.change_list.create_element(tag_name);
                 }
 
                 for l in listeners {
-                    unsafe {
-                        registry.add(l);
-                    }
-                    change_list.new_event_listener(l);
+                    // unsafe {
+                    //     registry.add(l);
+                    // }
+                    self.change_list.new_event_listener(l);
                 }
 
                 for attr in attributes {
-                    change_list.set_attribute(&attr.name, &attr.value, namespace.is_some());
+                    self.change_list
+                        .set_attribute(&attr.name, &attr.value, namespace.is_some());
                 }
 
                 // Fast path: if there is a single text child, it is faster to
@@ -1000,169 +884,237 @@ mod todo_diff {
                 // text content, and finally (3) append the text node to this
                 // parent.
                 if children.len() == 1 {
-                    if let Node {
-                        kind: NodeKind::Text(TextNode { text }),
-                    } = children[0]
-                    {
-                        change_list.set_text(text);
+                    if let VNode::Text(VText { text }) = children[0] {
+                        self.change_list.set_text(text);
                         return;
                     }
                 }
 
                 for child in children {
-                    create(cached_set, change_list, registry, child, cached_roots);
-                    change_list.append_child();
+                    self.create(child);
+                    self.change_list.append_child();
                 }
             }
-            NodeKind::Cached(ref c) => {
-                cached_roots.insert(c.id);
-                let (node, template) = cached_set.get(c.id);
-                if let Some(template) = template {
-                    create_with_template(
-                        cached_set,
-                        change_list,
-                        registry,
-                        template,
-                        node,
-                        cached_roots,
-                    );
-                } else {
-                    create(cached_set, change_list, registry, node, cached_roots);
-                }
+
+            /*
+            todo: integrate re-entrace
+            */
+            // NodeKind::Cached(ref c) => {
+            //     cached_roots.insert(c.id);
+            //     let (node, template) = cached_set.get(c.id);
+            //     if let Some(template) = template {
+            //         create_with_template(
+            //             cached_set,
+            //             self.change_list,
+            //             registry,
+            //             template,
+            //             node,
+            //             cached_roots,
+            //         );
+            //     } else {
+            //         create(cached_set, change_list, registry, node, cached_roots);
+            //     }
+            // }
+            VNode::Suspended => {
+                todo!("Creation of VNode::Suspended not yet supported")
+            }
+            VNode::Component(_) => {
+                todo!("Creation of VNode::Component not yet supported")
             }
         }
     }
 
-    // Get or create the template.
-    //
-    // Upon entering this function the change list stack may be in any shape:
+    // Remove all of a node's children.
     //
-    //     [...]
+    // The change list stack must have this shape upon entry to this function:
     //
-    // When this function returns, it leaves a freshly cloned copy of the template
-    // on the top of the change list stack:
+    //     [... parent]
     //
-    //     [... template]
-    #[inline]
-    fn get_or_create_template<'a>(
-        cached_set: &'a CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        cached_roots: &mut FxHashSet<CacheId>,
-        template_id: CacheId,
-    ) -> (&'a Node<'a>, bool) {
-        let (template, template_template) = cached_set.get(template_id);
-        debug_assert!(
-            template_template.is_none(),
-            "templates should not be templated themselves"
-        );
-
-        // If we haven't already created and saved the physical DOM subtree for this
-        // template, do that now.
-        if change_list.has_template(template_id) {
-            // Clone the template and push it onto the stack.
-            //
-            // [...]
-            change_list.push_template(template_id);
-            // [... template]
+    // 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());
+        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("");
+    }
 
-            (template, true)
-        } else {
-            // [...]
-            create(cached_set, change_list, registry, template, cached_roots);
-            // [... template]
-            change_list.save_template(template_id);
-            // [... template]
+    // Create the given children and append them to the parent node.
+    //
+    // The parent node must currently be on top of the change list stack:
+    //
+    //     [... 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());
+        for child in new {
+            self.create(child);
+            self.change_list.append_child();
+        }
+    }
 
-            (template, false)
+    // Remove the current child and all of its following siblings.
+    //
+    // The change list stack must have this shape upon entry to this function:
+    //
+    //     [... parent child]
+    //
+    // 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());
+        for child in old {
+            // registry.remove_subtree(child);
         }
+        self.change_list.remove_self_and_next_siblings();
     }
+}
 
-    fn create_and_replace(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        new_template: Option<CacheId>,
-        old: &Node,
-        new: &Node,
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
-        debug_assert!(change_list.traversal_is_committed());
-
-        if let Some(template_id) = new_template {
-            let (template, needs_listeners) = get_or_create_template(
-                cached_set,
-                change_list,
-                registry,
-                cached_roots,
-                template_id,
-            );
-            change_list.replace_with();
-
-            let mut old_forcing = None;
-            if needs_listeners {
-                old_forcing = Some(change_list.push_force_new_listeners());
-            }
+enum KeyedPrefixResult {
+    // Fast path: we finished diffing all the children just by looking at the
+    // prefix of shared keys!
+    Finished,
+    // There is more diffing work to do. Here is a count of how many children at
+    // the beginning of `new` and `old` we already processed.
+    MoreWorkToDo(usize),
+}
 
-            diff(
-                cached_set,
-                change_list,
-                registry,
-                template,
-                new,
-                cached_roots,
-            );
-
-            if let Some(old) = old_forcing {
-                change_list.pop_force_new_listeners(old);
-            }
+mod support {
+    use super::*;
+
+    // // Get or create the template.
+    // //
+    // // Upon entering this function the change list stack may be in any shape:
+    // //
+    // //     [...]
+    // //
+    // // When this function returns, it leaves a freshly cloned copy of the template
+    // // on the top of the change list stack:
+    // //
+    // //     [... template]
+    // #[inline]
+    // pub fn get_or_create_template<'a>(// cached_set: &'a CachedSet,
+    //     // change_list: &mut ChangeListBuilder,
+    //     // registry: &mut EventsRegistry,
+    //     // cached_roots: &mut FxHashSet<CacheId>,
+    //     // template_id: CacheId,
+    // ) -> (&'a Node<'a>, bool) {
+    //     let (template, template_template) = cached_set.get(template_id);
+    //     debug_assert!(
+    //         template_template.is_none(),
+    //         "templates should not be templated themselves"
+    //     );
+
+    //     // If we haven't already created and saved the physical DOM subtree for this
+    //     // template, do that now.
+    //     if change_list.has_template(template_id) {
+    //         // Clone the template and push it onto the stack.
+    //         //
+    //         // [...]
+    //         change_list.push_template(template_id);
+    //         // [... template]
+
+    //         (template, true)
+    //     } else {
+    //         // [...]
+    //         create(cached_set, change_list, registry, template, cached_roots);
+    //         // [... template]
+    //         change_list.save_template(template_id);
+    //         // [... template]
+
+    //         (template, false)
+    //     }
+    // }
 
-            change_list.commit_traversal();
-        } else {
-            create(cached_set, change_list, registry, new, cached_roots);
-            change_list.replace_with();
-        }
-        registry.remove_subtree(old);
-    }
+    // pub fn create_and_replace(
+    //     cached_set: &CachedSet,
+    //     change_list: &mut ChangeListBuilder,
+    //     registry: &mut EventsRegistry,
+    //     new_template: Option<CacheId>,
+    //     old: &Node,
+    //     new: &Node,
+    //     cached_roots: &mut FxHashSet<CacheId>,
+    // ) {
+    //     debug_assert!(change_list.traversal_is_committed());
+
+    //     if let Some(template_id) = new_template {
+    //         let (template, needs_listeners) = get_or_create_template(
+    //             cached_set,
+    //             change_list,
+    //             registry,
+    //             cached_roots,
+    //             template_id,
+    //         );
+    //         change_list.replace_with();
 
-    fn create_with_template(
-        cached_set: &CachedSet,
-        change_list: &mut ChangeListBuilder,
-        registry: &mut EventsRegistry,
-        template_id: CacheId,
-        node: &Node,
-        cached_roots: &mut FxHashSet<CacheId>,
-    ) {
-        debug_assert!(change_list.traversal_is_committed());
+    //         let mut old_forcing = None;
+    //         if needs_listeners {
+    //             old_forcing = Some(change_list.push_force_new_listeners());
+    //         }
 
-        // [...]
-        let (template, needs_listeners) =
-            get_or_create_template(cached_set, change_list, registry, cached_roots, template_id);
-        // [... template]
+    //         diff(
+    //             cached_set,
+    //             change_list,
+    //             registry,
+    //             template,
+    //             new,
+    //             cached_roots,
+    //         );
 
-        // Now diff the node with its template.
-        //
-        // We must force adding new listeners instead of updating existing ones,
-        // since listeners don't get cloned in `cloneNode`.
-        let mut old_forcing = None;
-        if needs_listeners {
-            old_forcing = Some(change_list.push_force_new_listeners());
-        }
+    //         if let Some(old) = old_forcing {
+    //             change_list.pop_force_new_listeners(old);
+    //         }
 
-        diff(
-            cached_set,
-            change_list,
-            registry,
-            template,
-            node,
-            cached_roots,
-        );
+    //         change_list.commit_traversal();
+    //     } else {
+    //         create(cached_set, change_list, registry, new, cached_roots);
+    //         change_list.replace_with();
+    //     }
+    //     registry.remove_subtree(old);
+    // }
 
-        if let Some(old) = old_forcing {
-            change_list.pop_force_new_listeners(old);
-        }
+    // pub fn create_with_template(
+    //     cached_set: &CachedSet,
+    //     change_list: &mut ChangeListBuilder,
+    //     registry: &mut EventsRegistry,
+    //     template_id: CacheId,
+    //     node: &Node,
+    //     cached_roots: &mut FxHashSet<CacheId>,
+    // ) {
+    //     debug_assert!(change_list.traversal_is_committed());
+
+    //     // [...]
+    //     let (template, needs_listeners) =
+    //         get_or_create_template(cached_set, change_list, registry, cached_roots, template_id);
+    //     // [... template]
+
+    //     // Now diff the node with its template.
+    //     //
+    //     // We must force adding new listeners instead of updating existing ones,
+    //     // since listeners don't get cloned in `cloneNode`.
+    //     let mut old_forcing = None;
+    //     if needs_listeners {
+    //         old_forcing = Some(change_list.push_force_new_listeners());
+    //     }
 
-        // Make sure that we come back up to the level we were at originally.
-        change_list.commit_traversal();
-    }
+    //     diff(
+    //         cached_set,
+    //         change_list,
+    //         registry,
+    //         template,
+    //         node,
+    //         cached_roots,
+    //     );
+
+    //     if let Some(old) = old_forcing {
+    //         change_list.pop_force_new_listeners(old);
+    //     }
+
+    //     // Make sure that we come back up to the level we were at originally.
+    //     change_list.commit_traversal();
+    // }
 }

+ 13 - 13
packages/core/src/lib.rs

@@ -65,19 +65,19 @@
 //! - dioxus-liveview (SSR + StringRenderer)
 //!
 
-pub mod changelist;
-pub mod component;
-pub mod context;
-pub mod debug_renderer;
-pub mod diff;
-pub mod error;
-pub mod events;
-pub mod hooks;
-pub mod nodebuilder;
-pub mod nodes;
-pub mod scope;
-pub mod validation;
-pub mod virtual_dom;
+pub mod changelist; // An "edit phase" described by transitions and edit operations
+pub mod component; // Logic for extending FC
+pub mod context; // Logic for providing hook + context functionality to user components
+pub mod debug_renderer; // Test harness for validating that lifecycles and diffs work appropriately
+pub mod diff; // The diffing algorithm that builds the ChangeList
+pub mod error; // Error type we expose to the renderers
+pub mod events; // Manages the synthetic event API
+pub mod hooks; // Built-in hooks
+pub mod nodebuilder; // Logic for building VNodes with a direct syntax
+pub mod nodes; // Logic for the VNodes
+pub mod scope; // Logic for single components
+pub mod validation; //  Logic for validating trees
+pub mod virtual_dom; // Most fun logic starts here, manages the lifecycle and suspense
 
 pub mod builder {
     pub use super::nodebuilder::*;

+ 37 - 9
packages/core/src/scope.rs

@@ -1,11 +1,11 @@
+use crate::context::hooks::Hook;
 use crate::innerlude::*;
 use crate::nodes::VNode;
-use crate::{context::hooks::Hook, diff::diff};
 use bumpalo::Bump;
 use generational_arena::Index;
 use std::{
     any::TypeId, borrow::Borrow, cell::RefCell, future::Future, marker::PhantomData,
-    sync::atomic::AtomicUsize,
+    sync::atomic::AtomicUsize, todo,
 };
 
 /// Every component in Dioxus is represented by a `Scope`.
@@ -29,9 +29,13 @@ pub struct Scope {
     pub frames: [Bump; 2],
 
     // somehow build this vnode with a lifetime tied to self
-    pub cur_node: *mut VNode<'static>,
+    // This root node has  "static" lifetime, but it's really not static.
+    // It's goverened by the oldest of the two frames and is switched every time a new render occurs
+    // Use this node as if it were static is unsafe, and needs to be fixed with ourborous or owning ref
+    // ! do not copy this reference are things WILL break !
+    pub root_node: *mut VNode<'static>,
 
-    pub active_frame: u8,
+    pub active_frame: ActiveFrame,
 
     // IE Which listeners need to be woken up?
     pub listeners: Vec<Box<dyn Fn()>>,
@@ -41,6 +45,20 @@ pub struct Scope {
     pub caller: *const i32,
 }
 
+pub enum ActiveFrame {
+    First,
+    Second,
+}
+
+impl ActiveFrame {
+    fn next(&mut self) {
+        match self {
+            ActiveFrame::First => *self = ActiveFrame::Second,
+            ActiveFrame::Second => *self = ActiveFrame::First,
+        }
+    }
+}
+
 impl Scope {
     // create a new scope from a function
     pub(crate) fn new<T: 'static>(f: FC<T>, parent: Option<Index>) -> Self {
@@ -49,15 +67,19 @@ impl Scope {
         let hook_arena = typed_arena::Arena::new();
         let hooks = RefCell::new(Vec::new());
 
+        // Capture the caller
         let caller = f as *const i32;
 
+        // Create the two buffers the componetn will render into
+        // There will always be an "old" and "new"
         let frames = [Bump::new(), Bump::new()];
 
         let listeners = Vec::new();
 
-        let active_frame = 1;
+        let active_frame = ActiveFrame::First;
 
         let new = frames[0].alloc(VNode::Text(VText::new("")));
+
         let cur_node = new as *mut _;
 
         Self {
@@ -69,7 +91,7 @@ impl Scope {
             listeners,
             parent,
             frames,
-            cur_node,
+            root_node: cur_node,
         }
     }
 
@@ -78,7 +100,12 @@ impl Scope {
     ///
     /// Props is ?Sized because we borrow the props and don't need to know the size. P (sized) is used as a marker (unsized)
     pub(crate) fn run<'a, P: Properties + ?Sized>(&self, props: &'a P) {
-        let bump = &self.frames[0];
+        let bump = match self.active_frame {
+            // If the active frame is the first, then we need to bump into the second
+            ActiveFrame::First => &self.frames[1],
+            // If the active frame is the second, then we need to bump into the first
+            ActiveFrame::Second => &self.frames[0],
+        }; // n.b, there might be a better way of doing this active frame stuff - perhaps swapping
 
         let ctx = Context {
             scope: &*self,
@@ -102,13 +129,14 @@ impl Scope {
         */
         let caller = unsafe { std::mem::transmute::<*const i32, FC<P>>(self.caller) };
         let new_nodes = caller(ctx, props);
-        let old_nodes: &mut VNode<'static> = unsafe { &mut *self.cur_node };
+        let old_nodes: &mut VNode<'static> = unsafe { &mut *self.root_node };
 
         // TODO: Iterate through the new nodes
         // move any listeners into ourself
 
         // perform the diff, dumping into the mutable change list
         // this doesnt perform any "diff compression" where an event and a re-render
-        crate::diff::diff(old_nodes, &new_nodes);
+        // crate::diff::diff(old_nodes, &new_nodes);
+        todo!()
     }
 }

+ 2 - 9
packages/core/src/virtual_dom.rs

@@ -1,4 +1,4 @@
-use crate::{changelist::ChangeList, nodes::VNode};
+use crate::{changelist::EditList, nodes::VNode};
 use crate::{events::EventTrigger, innerlude::*};
 use any::Any;
 use bumpalo::Bump;
@@ -23,10 +23,6 @@ pub struct VirtualDom {
     /// The index of the root component.
     base_scope: Index,
 
-    ///
-    ///
-    ///
-    ///
     event_queue: Rc<RefCell<Vec<LifecycleEvent>>>,
 
     // Mark the root props with P, even though they're held by the root component
@@ -45,10 +41,7 @@ impl VirtualDom {
     pub fn new(root: FC<()>) -> Self {
         Self::new_with_props(root, ())
     }
-}
 
-impl VirtualDom {
-    // impl<P: Properties + 'static> VirtualDom<P> {
     /// Start a new VirtualDom instance with a dependent props.
     /// Later, the props can be updated by calling "update" with a new set of props, causing a set of re-renders.
     ///
@@ -128,7 +121,7 @@ impl VirtualDom {
     ///
     ///
     /// ```
-    pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<ChangeList<'_>> {
+    pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<EditList<'_>> {
         let EventTrigger {
             component_id,
             listener_id,

+ 1 - 6
packages/web/src/lib.rs

@@ -2,7 +2,7 @@
 //!
 //! This crate implements a renderer of the Dioxus Virtual DOM for the web browser.
 //!
-//! While `VNode` supports "to_string" directly, it renders child components as the RSX! macro tokens. For custom components,
+//! While it is possible to render a single component directly, it is not possible to render component trees. For these,
 //! an external renderer is needed to progress the component lifecycles. The `WebsysRenderer` shows how to use the Virtual DOM
 //! API to progress these lifecycle events to generate a fully-mounted Virtual DOM instance which can be renderer in the
 //! `render` method.
@@ -16,7 +16,6 @@
 //! ```
 //!
 //! The `WebsysRenderer` is particularly useful when needing to cache a Virtual DOM in between requests
-//!
 
 use dioxus_core::{
     events::EventTrigger,
@@ -33,7 +32,6 @@ pub struct WebsysRenderer {
     internal_dom: VirtualDom,
 }
 
-/// Implement VirtualDom with no props for components that initialize their state internal to the VDom rather than externally.
 impl WebsysRenderer {
     /// Create a new instance of the Dioxus Virtual Dom with no properties for the root component.
     ///
@@ -42,9 +40,6 @@ impl WebsysRenderer {
     pub fn new(root: FC<()>) -> Self {
         Self::new_with_props(root, ())
     }
-}
-
-impl WebsysRenderer {
     /// Create a new text-renderer instance from a functional component root.
     /// Automatically progresses the creation of the VNode tree to completion.
     ///