Browse Source

polish: tests and documentation

Jonathan Kelley 3 years ago
parent
commit
da8159190b

+ 1 - 0
.vscode/spellright.dict

@@ -66,3 +66,4 @@ constified
 SegVec
 SegVec
 contentful
 contentful
 Jank
 Jank
+noderef

+ 2 - 0
README.md

@@ -109,6 +109,8 @@ If you know React, then you already know Dioxus.
 - Sierpinski's triangle (web SPA)
 - Sierpinski's triangle (web SPA)
 - Doxie Documentation Library (Web SPA with Hydration)
 - Doxie Documentation Library (Web SPA with Hydration)
 
 
+See the awesome-dioxus page for a curated list of content in the Dioxus Ecosystem.
+
 <!-- 
 <!-- 
 currently commented out until we have more content on the website
 currently commented out until we have more content on the website
 ## Explore
 ## Explore

+ 5 - 0
packages/core/src/bumpframe.rs

@@ -14,6 +14,7 @@ pub(crate) struct BumpFrame {
     pub bump: Bump,
     pub bump: Bump,
     pub(crate) head_node: VNode<'static>,
     pub(crate) head_node: VNode<'static>,
 
 
+    #[cfg(test)]
     // used internally for debugging
     // used internally for debugging
     _name: &'static str,
     _name: &'static str,
 }
 }
@@ -23,11 +24,15 @@ impl ActiveFrame {
         let frame_a = BumpFrame {
         let frame_a = BumpFrame {
             bump: Bump::new(),
             bump: Bump::new(),
             head_node: NodeFactory::unstable_place_holder(),
             head_node: NodeFactory::unstable_place_holder(),
+
+            #[cfg(test)]
             _name: "wip",
             _name: "wip",
         };
         };
         let frame_b = BumpFrame {
         let frame_b = BumpFrame {
             bump: Bump::new(),
             bump: Bump::new(),
             head_node: NodeFactory::unstable_place_holder(),
             head_node: NodeFactory::unstable_place_holder(),
+
+            #[cfg(test)]
             _name: "fin",
             _name: "fin",
         };
         };
         Self {
         Self {

+ 0 - 1
packages/core/src/childiter.rs

@@ -64,7 +64,6 @@ impl<'a> Iterator for RealChildIterator<'a> {
                             .scopes
                             .scopes
                             .get_scope(sc.associated_scope.get().unwrap())
                             .get_scope(sc.associated_scope.get().unwrap())
                             .unwrap();
                             .unwrap();
-                        // let scope = self.scopes.get(sc.ass_scope.get().unwrap()).unwrap();
 
 
                         // Simply swap the current node on the stack with the root of the component
                         // Simply swap the current node on the stack with the root of the component
                         *node = scope.frames.fin_head();
                         *node = scope.frames.fin_head();

+ 27 - 22
packages/core/src/component.rs

@@ -7,6 +7,32 @@
 
 
 use crate::innerlude::{Context, DomTree, LazyNodes, FC};
 use crate::innerlude::{Context, DomTree, LazyNodes, FC};
 
 
+/// Create inline fragments using Component syntax.
+///
+/// Fragments capture a series of children without rendering extra nodes.
+///
+/// # Example
+///
+/// ```rust
+/// rsx!{
+///     Fragment { key: "abc" }
+/// }
+/// ```
+///
+/// # Details
+///
+/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
+/// Try to avoid nesting fragments if you can. There is no protection against infinitely nested fragments.
+///
+/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
+///
+/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
+///
+#[allow(non_upper_case_globals, non_snake_case)]
+pub fn Fragment<'a>(cx: Context<'a, ()>) -> DomTree<'a> {
+    cx.render(LazyNodes::new(|f| f.fragment_from_iter(cx.children())))
+}
+
 /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
 /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus
 /// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
 /// on how to memoize the props and some additional optimizations that can be made. We strongly encourage using the
 /// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
 /// derive macro to implement the `Properties` trait automatically as guarantee that your memoization strategy is safe.
@@ -44,7 +70,7 @@ pub trait Properties: Sized {
     const IS_STATIC: bool;
     const IS_STATIC: bool;
     fn builder() -> Self::Builder;
     fn builder() -> Self::Builder;
 
 
-    /// Memoization can only happen if the props are 'static
+    /// Memoization can only happen if the props are valid for the 'static lifetime
     /// The user must know if their props are static, but if they make a mistake, UB happens
     /// The user must know if their props are static, but if they make a mistake, UB happens
     /// Therefore it's unsafe to memeoize.
     /// Therefore it's unsafe to memeoize.
     unsafe fn memoize(&self, other: &Self) -> bool;
     unsafe fn memoize(&self, other: &Self) -> bool;
@@ -75,24 +101,3 @@ impl EmptyBuilder {
 pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
 pub fn fc_to_builder<T: Properties>(_: FC<T>) -> T::Builder {
     T::builder()
     T::builder()
 }
 }
-
-/// Create inline fragments
-///
-/// Fragments capture a series of children without rendering extra nodes.
-///
-/// Fragments are incredibly useful when necessary, but *do* add cost in the diffing phase.
-/// Try to avoid nesting fragments if you can. Infinitely nested Fragments *will* cause diffing to crash.
-///
-/// This function defines a dedicated `Fragment` component that can be used to create inline fragments in the RSX macro.
-///
-/// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
-///
-/// ```rust
-/// rsx!{
-///     Fragment { key: "abc" }
-/// }
-/// ```
-#[allow(non_upper_case_globals, non_snake_case)]
-pub fn Fragment<'a>(cx: Context<'a, ()>) -> DomTree<'a> {
-    cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))
-}

+ 59 - 117
packages/core/src/diff.rs

@@ -124,6 +124,7 @@ impl<'a> SavedDiffWork<'a> {
     pub unsafe fn extend(self: SavedDiffWork<'a>) -> SavedDiffWork<'static> {
     pub unsafe fn extend(self: SavedDiffWork<'a>) -> SavedDiffWork<'static> {
         std::mem::transmute(self)
         std::mem::transmute(self)
     }
     }
+
     pub unsafe fn promote<'b>(self, vdom: &'b mut ResourcePool) -> DiffMachine<'b> {
     pub unsafe fn promote<'b>(self, vdom: &'b mut ResourcePool) -> DiffMachine<'b> {
         let extended: SavedDiffWork<'b> = std::mem::transmute(self);
         let extended: SavedDiffWork<'b> = std::mem::transmute(self);
         DiffMachine {
         DiffMachine {
@@ -136,10 +137,10 @@ impl<'a> SavedDiffWork<'a> {
 }
 }
 
 
 impl<'bump> DiffMachine<'bump> {
 impl<'bump> DiffMachine<'bump> {
-    pub(crate) fn new(edits: Mutations<'bump>, shared: &'bump ResourcePool) -> Self {
+    pub(crate) fn new(mutations: Mutations<'bump>, shared: &'bump ResourcePool) -> Self {
         Self {
         Self {
+            mutations,
             stack: DiffStack::new(),
             stack: DiffStack::new(),
-            mutations: edits,
             vdom: shared,
             vdom: shared,
             seen_scopes: FxHashSet::default(),
             seen_scopes: FxHashSet::default(),
         }
         }
@@ -156,7 +157,7 @@ impl<'bump> DiffMachine<'bump> {
     pub fn diff_scope(&mut self, id: ScopeId) {
     pub fn diff_scope(&mut self, id: ScopeId) {
         if let Some(component) = self.vdom.get_scope_mut(id) {
         if let Some(component) = self.vdom.get_scope_mut(id) {
             let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
             let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
-            self.stack.push(DiffInstruction::DiffNode { new, old });
+            self.stack.push(DiffInstruction::Diff { new, old });
         }
         }
     }
     }
 
 
@@ -167,24 +168,14 @@ impl<'bump> DiffMachine<'bump> {
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
     /// We do depth-first to maintain high cache locality (nodes were originally generated recursively).
     ///
     ///
     /// Returns a `bool` indicating that the work completed properly.
     /// Returns a `bool` indicating that the work completed properly.
-    pub fn work(&mut self, deadline_expired: &mut impl FnMut() -> bool) -> bool {
+    pub fn work(&mut self, mut deadline_expired: impl FnMut() -> bool) -> bool {
         while let Some(instruction) = self.stack.pop() {
         while let Some(instruction) = self.stack.pop() {
-            // defer to individual functions so the compiler produces better code
-            // large functions tend to be difficult for the compiler to work with
             match instruction {
             match instruction {
-                DiffInstruction::PopScope => {
-                    self.stack.pop_scope();
-                }
-
-                DiffInstruction::DiffNode { old, new, .. } => self.diff_node(old, new),
-
-                DiffInstruction::DiffChildren { old, new } => self.diff_children(old, new),
-
+                DiffInstruction::Diff { old, new } => self.diff_node(old, new),
                 DiffInstruction::Create { node } => self.create_node(node),
                 DiffInstruction::Create { node } => self.create_node(node),
-
                 DiffInstruction::Mount { and } => self.mount(and),
                 DiffInstruction::Mount { and } => self.mount(and),
-
-                DiffInstruction::PrepareMoveNode { node } => self.prepare_move_node(node),
+                DiffInstruction::PrepareMove { node } => self.prepare_move_node(node),
+                DiffInstruction::PopScope => self.stack.pop_off_scope(),
             };
             };
 
 
             if deadline_expired() {
             if deadline_expired() {
@@ -217,15 +208,21 @@ impl<'bump> DiffMachine<'bump> {
             }
             }
 
 
             MountType::Replace { old } => {
             MountType::Replace { old } => {
-                let mut iter = RealChildIterator::new(old, self.vdom);
-                let first = iter.next().unwrap();
-                self.mutations
-                    .replace_with(first.mounted_id(), nodes_created as u32);
-                self.remove_nodes(iter);
+                if let Some(old_id) = old.try_mounted_id() {
+                    self.mutations.replace_with(old_id, nodes_created as u32);
+                } else {
+                    let mut iter = RealChildIterator::new(old, self.vdom);
+                    let first = iter.next().unwrap();
+                    self.mutations
+                        .replace_with(first.mounted_id(), nodes_created as u32);
+                    self.remove_nodes(iter);
+                }
             }
             }
 
 
-            MountType::ReplaceByElementId { el: old } => {
-                self.mutations.replace_with(old, nodes_created as u32);
+            MountType::ReplaceByElementId { el } => {
+                if let Some(old) = el {
+                    self.mutations.replace_with(old, nodes_created as u32);
+                }
             }
             }
 
 
             MountType::InsertAfter { other_node } => {
             MountType::InsertAfter { other_node } => {
@@ -326,6 +323,7 @@ impl<'bump> DiffMachine<'bump> {
         let parent_idx = self.stack.current_scope().unwrap();
         let parent_idx = self.stack.current_scope().unwrap();
 
 
         let shared = self.vdom.channel.clone();
         let shared = self.vdom.channel.clone();
+
         // Insert a new scope into our component list
         // Insert a new scope into our component list
         let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
         let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
@@ -376,7 +374,9 @@ impl<'bump> DiffMachine<'bump> {
         match (old_node, new_node) {
         match (old_node, new_node) {
             // Check the most common cases first
             // Check the most common cases first
             (Text(old), Text(new)) => self.diff_text_nodes(old, new),
             (Text(old), Text(new)) => self.diff_text_nodes(old, new),
-            (Component(old), Component(new)) => self.diff_component_nodes(old, new),
+            (Component(old), Component(new)) => {
+                self.diff_component_nodes(old_node, new_node, old, new)
+            }
             (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
             (Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
             (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
             (Anchor(old), Anchor(new)) => new.dom_id.set(old.dom_id.get()),
             (Suspended(old), Suspended(new)) => new.dom_id.set(old.dom_id.get()),
             (Suspended(old), Suspended(new)) => new.dom_id.set(old.dom_id.get()),
@@ -393,19 +393,19 @@ impl<'bump> DiffMachine<'bump> {
     }
     }
 
 
     fn diff_text_nodes(&mut self, old: &'bump VText<'bump>, new: &'bump VText<'bump>) {
     fn diff_text_nodes(&mut self, old: &'bump VText<'bump>, new: &'bump VText<'bump>) {
-        let root = old.dom_id.get().unwrap();
+        if let Some(root) = old.dom_id.get() {
+            if old.text != new.text {
+                self.mutations.push_root(root);
+                self.mutations.set_text(new.text);
+                self.mutations.pop();
+            }
 
 
-        if old.text != new.text {
-            self.mutations.push_root(root);
-            self.mutations.set_text(new.text);
-            self.mutations.pop();
+            new.dom_id.set(Some(root));
         }
         }
-
-        new.dom_id.set(Some(root));
     }
     }
 
 
     fn diff_element_nodes(&mut self, old: &'bump VElement<'bump>, new: &'bump VElement<'bump>) {
     fn diff_element_nodes(&mut self, old: &'bump VElement<'bump>, new: &'bump VElement<'bump>) {
-        let root = old.dom_id.get().unwrap();
+        let root = old.dom_id.get();
 
 
         // If the element type is completely different, the element needs to be re-rendered completely
         // If the element type is completely different, the element needs to be re-rendered completely
         // This is an optimization React makes due to how users structure their code
         // This is an optimization React makes due to how users structure their code
@@ -417,21 +417,23 @@ impl<'bump> DiffMachine<'bump> {
             self.stack.push_nodes_created(0);
             self.stack.push_nodes_created(0);
             self.stack.push(DiffInstruction::Mount {
             self.stack.push(DiffInstruction::Mount {
                 and: MountType::ReplaceByElementId {
                 and: MountType::ReplaceByElementId {
-                    el: old.dom_id.get().unwrap(),
+                    el: old.dom_id.get(),
                 },
                 },
             });
             });
             self.create_element_node(new);
             self.create_element_node(new);
             return;
             return;
         }
         }
 
 
-        new.dom_id.set(Some(root));
+        new.dom_id.set(root);
 
 
         // Don't push the root if we don't have to
         // Don't push the root if we don't have to
         let mut has_comitted = false;
         let mut has_comitted = false;
         let mut please_commit = |edits: &mut Vec<DomEdit>| {
         let mut please_commit = |edits: &mut Vec<DomEdit>| {
             if !has_comitted {
             if !has_comitted {
                 has_comitted = true;
                 has_comitted = true;
-                edits.push(PushRoot { id: root.as_u64() });
+                if let Some(root) = root {
+                    edits.push(PushRoot { id: root.as_u64() });
+                }
             }
             }
         };
         };
 
 
@@ -449,7 +451,6 @@ impl<'bump> DiffMachine<'bump> {
                 }
                 }
             }
             }
         } else {
         } else {
-            // TODO: provide some sort of report on how "good" the diffing was
             please_commit(&mut self.mutations.edits);
             please_commit(&mut self.mutations.edits);
             for attribute in old.attributes {
             for attribute in old.attributes {
                 self.mutations.remove_attribute(attribute);
                 self.mutations.remove_attribute(attribute);
@@ -467,7 +468,6 @@ impl<'bump> DiffMachine<'bump> {
         // We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
         // We also need to make sure that all listeners are properly attached to the parent scope (fix_listener)
         //
         //
         // TODO: take a more efficient path than this
         // TODO: take a more efficient path than this
-
         if let Some(cur_scope_id) = self.stack.current_scope() {
         if let Some(cur_scope_id) = self.stack.current_scope() {
             let scope = self.vdom.get_scope(cur_scope_id).unwrap();
             let scope = self.vdom.get_scope(cur_scope_id).unwrap();
 
 
@@ -487,22 +487,21 @@ impl<'bump> DiffMachine<'bump> {
                     self.mutations.remove_event_listener(listener.event);
                     self.mutations.remove_event_listener(listener.event);
                 }
                 }
                 for listener in new.listeners {
                 for listener in new.listeners {
-                    listener.mounted_node.set(Some(root));
+                    listener.mounted_node.set(root);
                     self.mutations.new_event_listener(listener, cur_scope_id);
                     self.mutations.new_event_listener(listener, cur_scope_id);
                     self.attach_listener_to_scope(listener, scope);
                     self.attach_listener_to_scope(listener, scope);
                 }
                 }
             }
             }
         }
         }
 
 
-        if has_comitted {
-            self.mutations.pop();
-        }
-
         self.diff_children(old.children, new.children);
         self.diff_children(old.children, new.children);
     }
     }
 
 
     fn diff_component_nodes(
     fn diff_component_nodes(
         &mut self,
         &mut self,
+        old_node: &'bump VNode<'bump>,
+        new_node: &'bump VNode<'bump>,
+
         old: &'bump VComponent<'bump>,
         old: &'bump VComponent<'bump>,
         new: &'bump VComponent<'bump>,
         new: &'bump VComponent<'bump>,
     ) {
     ) {
@@ -526,7 +525,7 @@ impl<'bump> DiffMachine<'bump> {
 
 
             match compare(new) {
             match compare(new) {
                 true => {
                 true => {
-                    // the props are the same...
+                    // the props are the same... do nothing
                 }
                 }
                 false => {
                 false => {
                     // the props are different...
                     // the props are different...
@@ -540,25 +539,8 @@ impl<'bump> DiffMachine<'bump> {
 
 
             self.seen_scopes.insert(scope_addr);
             self.seen_scopes.insert(scope_addr);
         } else {
         } else {
-            todo!();
-
-            // let mut old_iter = RealChildIterator::new(old_node, &self.vdom);
-            // let first = old_iter
-            //     .next()
-            //     .expect("Components should generate a placeholder root");
-
-            // // remove any leftovers
-            // for to_remove in old_iter {
-            //     self.mutations.push_root(to_remove.direct_id());
-            //     self.mutations.remove();
-            // }
-
-            // // seems like we could combine this into a single instruction....
-            // self.replace_node_with_node(first.direct_id(), old_node, new_node);
-
-            // // Wipe the old one and plant the new one
-            // let old_scope = old.ass_scope.get().unwrap();
-            // self.destroy_scopes(old_scope);
+            self.stack
+                .create_node(new_node, MountType::Replace { old: old_node });
         }
         }
     }
     }
 
 
@@ -608,7 +590,7 @@ impl<'bump> DiffMachine<'bump> {
                 old_anchor.dom_id.set(new_anchor.dom_id.get());
                 old_anchor.dom_id.set(new_anchor.dom_id.get());
             }
             }
             ([VNode::Anchor(anchor)], _) => {
             ([VNode::Anchor(anchor)], _) => {
-                let el = anchor.dom_id.get().unwrap();
+                let el = anchor.dom_id.get();
                 self.stack
                 self.stack
                     .create_children(new, MountType::ReplaceByElementId { el });
                     .create_children(new, MountType::ReplaceByElementId { el });
             }
             }
@@ -652,7 +634,7 @@ impl<'bump> DiffMachine<'bump> {
         debug_assert!(!old.is_empty());
         debug_assert!(!old.is_empty());
 
 
         for (new, old) in new.iter().zip(old.iter()).rev() {
         for (new, old) in new.iter().zip(old.iter()).rev() {
-            self.stack.push(DiffInstruction::DiffNode { new, old });
+            self.stack.push(DiffInstruction::Diff { new, old });
         }
         }
 
 
         if old.len() > new.len() {
         if old.len() > new.len() {
@@ -748,7 +730,7 @@ impl<'bump> DiffMachine<'bump> {
             if old.key() != new.key() {
             if old.key() != new.key() {
                 break;
                 break;
             }
             }
-            self.stack.push(DiffInstruction::DiffNode { old, new });
+            self.stack.push(DiffInstruction::Diff { old, new });
             left_offset += 1;
             left_offset += 1;
         }
         }
 
 
@@ -887,8 +869,8 @@ impl<'bump> DiffMachine<'bump> {
                 stack.create_node(new_node, MountType::Absorb);
                 stack.create_node(new_node, MountType::Absorb);
             } else {
             } else {
                 // this funciton should never take LIS indicies
                 // this funciton should never take LIS indicies
-                stack.push(DiffInstruction::PrepareMoveNode { node: new_node });
-                stack.push(DiffInstruction::DiffNode {
+                stack.push(DiffInstruction::PrepareMove { node: new_node });
+                stack.push(DiffInstruction::Diff {
                     new: new_node,
                     new: new_node,
                     old: &old[old_index],
                     old: &old[old_index],
                 });
                 });
@@ -943,7 +925,7 @@ impl<'bump> DiffMachine<'bump> {
         }
         }
 
 
         for idx in lis_sequence.iter().rev() {
         for idx in lis_sequence.iter().rev() {
-            self.stack.push(DiffInstruction::DiffNode {
+            self.stack.push(DiffInstruction::Diff {
                 new: &new[*idx],
                 new: &new[*idx],
                 old: &old[new_index_to_old_index[*idx]],
                 old: &old[new_index_to_old_index[*idx]],
             });
             });
@@ -998,6 +980,14 @@ impl<'bump> DiffMachine<'bump> {
         }
         }
     }
     }
 
 
+    fn replace_and_create_one_with_many(
+        &mut self,
+        old: &'bump VNode<'bump>,
+        new: &'bump [VNode<'bump>],
+    ) {
+        //
+    }
+
     fn replace_and_create_many_with_one(
     fn replace_and_create_many_with_one(
         &mut self,
         &mut self,
         old: &'bump [VNode<'bump>],
         old: &'bump [VNode<'bump>],
@@ -1070,60 +1060,12 @@ impl<'bump> DiffMachine<'bump> {
         }
         }
     }
     }
 
 
-    fn create_garbage(&mut self, node: &'bump VNode<'bump>) {
-        match self
-            .stack
-            .current_scope()
-            .and_then(|id| self.vdom.get_scope(id))
-        {
-            Some(scope) => {
-                let garbage: &'bump VNode<'static> = unsafe { std::mem::transmute(node) };
-                scope.pending_garbage.borrow_mut().push(garbage);
-            }
-            None => {
-                log::info!("No scope to collect garbage into")
-            }
-        }
-    }
-
     /// Adds a listener closure to a scope during diff.
     /// Adds a listener closure to a scope during diff.
     fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
     fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
         let mut queue = scope.listeners.borrow_mut();
         let mut queue = scope.listeners.borrow_mut();
         let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
         let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
         queue.push(long_listener as *const _)
         queue.push(long_listener as *const _)
     }
     }
-
-    /// Destroy a scope and all of its descendents.
-    ///
-    /// Calling this will run the destuctors on all hooks in the tree.
-    /// It will also add the destroyed nodes to the `seen_nodes` cache to prevent them from being renderered.
-    fn destroy_scopes(&mut self, old_scope: ScopeId) {
-        let mut nodes_to_delete = vec![old_scope];
-        let mut scopes_to_explore = vec![old_scope];
-
-        // explore the scope tree breadth first
-        while let Some(scope_id) = scopes_to_explore.pop() {
-            // If we're planning on deleting this node, then we don't need to both rendering it
-            self.seen_scopes.insert(scope_id);
-            let scope = self.vdom.get_scope(scope_id).unwrap();
-            for child in scope.descendents.borrow().iter() {
-                // Add this node to be explored
-                scopes_to_explore.push(child.clone());
-
-                // Also add it for deletion
-                nodes_to_delete.push(child.clone());
-            }
-        }
-
-        // Delete all scopes that we found as part of this subtree
-        for node in nodes_to_delete {
-            log::debug!("Removing scope {:#?}", node);
-            let _scope = self.vdom.try_remove(node).unwrap();
-            // do anything we need to do to delete the scope
-            // I think we need to run the destructors on the hooks
-            // TODO
-        }
-    }
 }
 }
 
 
 fn compare_strs(a: &str, b: &str) -> bool {
 fn compare_strs(a: &str, b: &str) -> bool {

+ 8 - 9
packages/core/src/diff_stack.rs

@@ -3,23 +3,18 @@ use smallvec::{smallvec, SmallVec};
 
 
 /// The stack instructions we use to diff and create new nodes.
 /// The stack instructions we use to diff and create new nodes.
 #[derive(Debug)]
 #[derive(Debug)]
-pub enum DiffInstruction<'a> {
-    DiffNode {
+pub(crate) enum DiffInstruction<'a> {
+    Diff {
         old: &'a VNode<'a>,
         old: &'a VNode<'a>,
         new: &'a VNode<'a>,
         new: &'a VNode<'a>,
     },
     },
 
 
-    DiffChildren {
-        old: &'a [VNode<'a>],
-        new: &'a [VNode<'a>],
-    },
-
     Create {
     Create {
         node: &'a VNode<'a>,
         node: &'a VNode<'a>,
     },
     },
 
 
     /// pushes the node elements onto the stack for use in mount
     /// pushes the node elements onto the stack for use in mount
-    PrepareMoveNode {
+    PrepareMove {
         node: &'a VNode<'a>,
         node: &'a VNode<'a>,
     },
     },
 
 
@@ -35,7 +30,7 @@ pub enum MountType<'a> {
     Absorb,
     Absorb,
     Append,
     Append,
     Replace { old: &'a VNode<'a> },
     Replace { old: &'a VNode<'a> },
-    ReplaceByElementId { el: ElementId },
+    ReplaceByElementId { el: Option<ElementId> },
     InsertAfter { other_node: &'a VNode<'a> },
     InsertAfter { other_node: &'a VNode<'a> },
     InsertBefore { other_node: &'a VNode<'a> },
     InsertBefore { other_node: &'a VNode<'a> },
 }
 }
@@ -63,6 +58,10 @@ impl<'bump> DiffStack<'bump> {
         self.instructions.pop()
         self.instructions.pop()
     }
     }
 
 
+    pub fn pop_off_scope(&mut self) {
+        self.scope_stack.pop();
+    }
+
     pub fn pop_scope(&mut self) -> Option<ScopeId> {
     pub fn pop_scope(&mut self) -> Option<ScopeId> {
         self.scope_stack.pop()
         self.scope_stack.pop()
     }
     }

+ 5 - 0
packages/core/src/events.rs

@@ -125,8 +125,13 @@ pub mod on {
 
 
                         let callback: BumpBox<dyn FnMut(SyntheticEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
                         let callback: BumpBox<dyn FnMut(SyntheticEvent) + 'a> = unsafe { BumpBox::from_raw(cb) };
 
 
+
+                        // ie oncopy
                         let event_name = stringify!($name);
                         let event_name = stringify!($name);
+
+                        // ie copy
                         let shortname: &'static str = &event_name[2..];
                         let shortname: &'static str = &event_name[2..];
+
                         Listener {
                         Listener {
                             event: shortname,
                             event: shortname,
                             mounted_node: Cell::new(None),
                             mounted_node: Cell::new(None),

+ 14 - 30
packages/core/src/hooklist.rs

@@ -3,45 +3,28 @@ use std::{
     cell::{Cell, RefCell, UnsafeCell},
     cell::{Cell, RefCell, UnsafeCell},
 };
 };
 
 
+/// An abstraction over internally stored data using a hook-based memory layout.
+///
+/// Hooks are allocated using Boxes and then our stored references are given out.
+///
+/// It's unsafe to "reset" the hooklist, but it is safe to add hooks into it.
+///
+/// Todo: this could use its very own bump arena, but that might be a tad overkill
+#[derive(Default)]
 pub(crate) struct HookList {
 pub(crate) struct HookList {
-    vals: RefCell<Vec<InnerHook<Box<dyn Any>>>>,
+    vals: RefCell<Vec<UnsafeCell<Box<dyn Any>>>>,
     idx: Cell<usize>,
     idx: Cell<usize>,
 }
 }
 
 
-impl Default for HookList {
-    fn default() -> Self {
-        Self {
-            vals: Default::default(),
-            idx: Cell::new(0),
-        }
-    }
-}
-
-struct InnerHook<T> {
-    cell: UnsafeCell<T>,
-}
-
-impl<T> InnerHook<T> {
-    fn new(new: T) -> Self {
-        Self {
-            cell: UnsafeCell::new(new),
-        }
-    }
-}
-
 impl HookList {
 impl HookList {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
             self.idx.set(self.idx.get() + 1);
             self.idx.set(self.idx.get() + 1);
-            let raw_box = unsafe { &mut *inn.cell.get() };
+            let raw_box = unsafe { &mut *inn.get() };
             raw_box.downcast_mut::<T>()
             raw_box.downcast_mut::<T>()
         })
         })
     }
     }
 
 
-    pub(crate) fn push<T: 'static>(&self, new: T) {
-        self.vals.borrow_mut().push(InnerHook::new(Box::new(new)))
-    }
-
     /// This resets the internal iterator count
     /// This resets the internal iterator count
     /// It's okay that we've given out each hook, but now we have the opportunity to give it out again
     /// It's okay that we've given out each hook, but now we have the opportunity to give it out again
     /// Therefore, resetting is cosudered unsafe
     /// Therefore, resetting is cosudered unsafe
@@ -52,17 +35,18 @@ impl HookList {
         self.idx.set(0);
         self.idx.set(0);
     }
     }
 
 
-    #[inline]
+    pub(crate) fn push<T: 'static>(&self, new: T) {
+        self.vals.borrow_mut().push(UnsafeCell::new(Box::new(new)))
+    }
+
     pub(crate) fn len(&self) -> usize {
     pub(crate) fn len(&self) -> usize {
         self.vals.borrow().len()
         self.vals.borrow().len()
     }
     }
 
 
-    #[inline]
     pub(crate) fn cur_idx(&self) -> usize {
     pub(crate) fn cur_idx(&self) -> usize {
         self.idx.get()
         self.idx.get()
     }
     }
 
 
-    #[inline]
     pub(crate) fn at_end(&self) -> bool {
     pub(crate) fn at_end(&self) -> bool {
         self.cur_idx() >= self.len()
         self.cur_idx() >= self.len()
     }
     }

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

@@ -56,9 +56,9 @@ pub(crate) mod innerlude {
 }
 }
 
 
 pub use crate::innerlude::{
 pub use crate::innerlude::{
-    format_args_f, html, rsx, Context, DiffInstruction, DioxusElement, DomEdit, DomTree, ElementId,
-    EventPriority, LazyNodes, MountType, Mutations, NodeFactory, Properties, ScopeId,
-    SuspendedContext, SyntheticEvent, TestDom, UserEvent, VNode, VirtualDom, FC,
+    format_args_f, html, rsx, Context, DioxusElement, DomEdit, DomTree, ElementId, EventPriority,
+    LazyNodes, MountType, Mutations, NodeFactory, Properties, ScopeId, SuspendedContext,
+    SyntheticEvent, TestDom, UserEvent, VNode, VirtualDom, FC,
 };
 };
 
 
 pub mod prelude {
 pub mod prelude {

+ 3 - 3
packages/core/src/nodes.rs

@@ -26,6 +26,7 @@ pub enum VNode<'src> {
     ///
     ///
     /// # Example
     /// # Example
     ///
     ///
+    /// ```
     /// let node = cx.render(rsx!{ "hello" }).unwrap();
     /// let node = cx.render(rsx!{ "hello" }).unwrap();
     ///
     ///
     /// if let VNode::Text(vtext) = node {
     /// if let VNode::Text(vtext) = node {
@@ -248,7 +249,7 @@ pub struct Listener<'bump> {
 
 
     /// The type of event to listen for.
     /// The type of event to listen for.
     ///
     ///
-    /// IE "onclick" - whatever the renderer needs to attach the listener by name.
+    /// IE "click" - whatever the renderer needs to attach the listener by name.
     pub event: &'static str,
     pub event: &'static str,
 
 
     pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
     pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
@@ -283,8 +284,7 @@ pub struct VComponent<'src> {
 pub struct VSuspended<'a> {
 pub struct VSuspended<'a> {
     pub task_id: u64,
     pub task_id: u64,
     pub dom_id: Cell<Option<ElementId>>,
     pub dom_id: Cell<Option<ElementId>>,
-    pub(crate) callback:
-        RefCell<Option<BumpBox<'a, dyn FnMut(SuspendedContext<'a>) -> DomTree<'a>>>>,
+    pub callback: RefCell<Option<BumpBox<'a, dyn FnMut(SuspendedContext<'a>) -> DomTree<'a>>>>,
 }
 }
 
 
 /// This struct provides an ergonomic API to quickly build VNodes.
 /// This struct provides an ergonomic API to quickly build VNodes.

+ 76 - 84
packages/core/src/scheduler.rs

@@ -14,7 +14,7 @@ Some essential reading:
 
 
 Dioxus is a framework for "user experience" - not just "user interfaces." Part of the "experience" is keeping the UI
 Dioxus is a framework for "user experience" - not just "user interfaces." Part of the "experience" is keeping the UI
 snappy and "jank free" even under heavy work loads. Dioxus already has the "speed" part figured out - but there's no
 snappy and "jank free" even under heavy work loads. Dioxus already has the "speed" part figured out - but there's no
-point if being "fast" if you can't also be "responsive."
+point in being "fast" if you can't also be "responsive."
 
 
 As such, Dioxus can manually decide on what work is most important at any given moment in time. With a properly tuned
 As such, Dioxus can manually decide on what work is most important at any given moment in time. With a properly tuned
 priority system, Dioxus can ensure that user interaction is prioritized and committed as soon as possible (sub 100ms).
 priority system, Dioxus can ensure that user interaction is prioritized and committed as soon as possible (sub 100ms).
@@ -87,7 +87,7 @@ use std::{
 };
 };
 
 
 #[derive(Clone)]
 #[derive(Clone)]
-pub struct EventChannel {
+pub(crate) struct EventChannel {
     pub task_counter: Rc<Cell<u64>>,
     pub task_counter: Rc<Cell<u64>>,
     pub sender: UnboundedSender<SchedulerMsg>,
     pub sender: UnboundedSender<SchedulerMsg>,
     pub schedule_any_immediate: Rc<dyn Fn(ScopeId)>,
     pub schedule_any_immediate: Rc<dyn Fn(ScopeId)>,
@@ -96,8 +96,13 @@ pub struct EventChannel {
 }
 }
 
 
 pub enum SchedulerMsg {
 pub enum SchedulerMsg {
-    Immediate(ScopeId),
+    // events from the host
     UiEvent(UserEvent),
     UiEvent(UserEvent),
+
+    // setstate
+    Immediate(ScopeId),
+
+    // tasks
     SubmitTask(FiberTask, u64),
     SubmitTask(FiberTask, u64),
     ToggleTask(u64),
     ToggleTask(u64),
     PauseTask(u64),
     PauseTask(u64),
@@ -109,7 +114,7 @@ pub enum SchedulerMsg {
 ///
 ///
 /// Each scope has the ability to lightly interact with the scheduler (IE, schedule an update) but ultimately the scheduler calls the components.
 /// Each scope has the ability to lightly interact with the scheduler (IE, schedule an update) but ultimately the scheduler calls the components.
 ///
 ///
-/// In Dioxus, the scheduler provides 3 priority levels - each with their own "DiffMachine". The DiffMachine state can be saved if the deadline runs
+/// In Dioxus, the scheduler provides 4 priority levels - each with their own "DiffMachine". The DiffMachine state can be saved if the deadline runs
 /// out.
 /// out.
 ///
 ///
 /// Saved DiffMachine state can be self-referential, so we need to be careful about how we save it. All self-referential data is a link between
 /// Saved DiffMachine state can be self-referential, so we need to be careful about how we save it. All self-referential data is a link between
@@ -211,6 +216,7 @@ impl Scheduler {
 
 
         Self {
         Self {
             pool,
             pool,
+
             receiver,
             receiver,
 
 
             async_tasks: FuturesUnordered::new(),
             async_tasks: FuturesUnordered::new(),
@@ -219,7 +225,6 @@ impl Scheduler {
 
 
             heuristics,
             heuristics,
 
 
-            // a storage for our receiver to dump into
             ui_events: VecDeque::new(),
             ui_events: VecDeque::new(),
 
 
             pending_immediates: VecDeque::new(),
             pending_immediates: VecDeque::new(),
@@ -230,7 +235,7 @@ impl Scheduler {
 
 
             current_priority: EventPriority::Low,
             current_priority: EventPriority::Low,
 
 
-            // a dedicated fiber for each priority
+            // sorted high to low by priority (0 = immediate, 3 = low)
             lanes: [
             lanes: [
                 PriorityLane::new(),
                 PriorityLane::new(),
                 PriorityLane::new(),
                 PriorityLane::new(),
@@ -248,25 +253,69 @@ impl Scheduler {
 
 
     // Converts UI events into dirty scopes with various priorities
     // Converts UI events into dirty scopes with various priorities
     pub fn consume_pending_events(&mut self) {
     pub fn consume_pending_events(&mut self) {
+        // consume all events that are "continuous" to be batched
+        // if we run into a discrete event, then bail early
+
         while let Some(trigger) = self.ui_events.pop_back() {
         while let Some(trigger) = self.ui_events.pop_back() {
             if let Some(scope) = self.pool.get_scope_mut(trigger.scope) {
             if let Some(scope) = self.pool.get_scope_mut(trigger.scope) {
                 if let Some(element) = trigger.mounted_dom_id {
                 if let Some(element) = trigger.mounted_dom_id {
-                    let priority = match &trigger.event {
-                        SyntheticEvent::ClipboardEvent(_) => {}
-                        SyntheticEvent::CompositionEvent(_) => {}
-                        SyntheticEvent::KeyboardEvent(_) => {}
-                        SyntheticEvent::FocusEvent(_) => {}
-                        SyntheticEvent::FormEvent(_) => {}
-                        SyntheticEvent::SelectionEvent(_) => {}
-                        SyntheticEvent::TouchEvent(_) => {}
-                        SyntheticEvent::WheelEvent(_) => {}
-                        SyntheticEvent::MediaEvent(_) => {}
-                        SyntheticEvent::AnimationEvent(_) => {}
-                        SyntheticEvent::TransitionEvent(_) => {}
-                        SyntheticEvent::ToggleEvent(_) => {}
-                        SyntheticEvent::MouseEvent(_) => {}
-                        SyntheticEvent::PointerEvent(_) => {}
-                        SyntheticEvent::GenericEvent(_) => {}
+                    let priority = match trigger.name {
+                        // clipboard
+                        "copy" | "cut" | "paste" => EventPriority::Medium,
+
+                        // Composition
+                        "compositionend" | "compositionstart" | "compositionupdate" => {
+                            EventPriority::Low
+                        }
+
+                        // Keyboard
+                        "keydown" | "keypress" | "keyup" => EventPriority::Low,
+
+                        // Focus
+                        "focus" | "blur" => EventPriority::Low,
+
+                        // Form
+                        "change" | "input" | "invalid" | "reset" | "submit" => EventPriority::Low,
+
+                        // Mouse
+                        "click" | "contextmenu" | "doubleclick" | "drag" | "dragend"
+                        | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart"
+                        | "drop" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove"
+                        | "mouseout" | "mouseover" | "mouseup" => EventPriority::Low,
+
+                        // Pointer
+                        "pointerdown" | "pointermove" | "pointerup" | "pointercancel"
+                        | "gotpointercapture" | "lostpointercapture" | "pointerenter"
+                        | "pointerleave" | "pointerover" | "pointerout" => EventPriority::Low,
+
+                        // Selection
+                        "select" | "touchcancel" | "touchend" => EventPriority::Low,
+
+                        // Touch
+                        "touchmove" | "touchstart" => EventPriority::Low,
+
+                        // Wheel
+                        "scroll" | "wheel" => EventPriority::Low,
+
+                        // Media
+                        "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied"
+                        | "encrypted" | "ended" | "error" | "loadeddata" | "loadedmetadata"
+                        | "loadstart" | "pause" | "play" | "playing" | "progress"
+                        | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
+                        | "timeupdate" | "volumechange" | "waiting" => EventPriority::Low,
+
+                        // Animation
+                        "animationstart" | "animationend" | "animationiteration" => {
+                            EventPriority::Low
+                        }
+
+                        // Transition
+                        "transitionend" => EventPriority::Low,
+
+                        // Toggle
+                        "toggle" => EventPriority::Low,
+
+                        _ => EventPriority::Low,
                     };
                     };
 
 
                     scope.call_listener(trigger.event, element);
                     scope.call_listener(trigger.event, element);
@@ -341,7 +390,6 @@ impl Scheduler {
         self.manually_poll_events();
         self.manually_poll_events();
 
 
         if !self.has_any_work() {
         if !self.has_any_work() {
-            self.clean_up_garbage();
             return committed_mutations;
             return committed_mutations;
         }
         }
 
 
@@ -349,7 +397,7 @@ impl Scheduler {
 
 
         while self.has_any_work() {
         while self.has_any_work() {
             self.shift_priorities();
             self.shift_priorities();
-            self.work_on_current_lane(&mut || false, &mut committed_mutations);
+            self.work_on_current_lane(|| false, &mut committed_mutations);
         }
         }
 
 
         committed_mutations
         committed_mutations
@@ -393,7 +441,6 @@ impl Scheduler {
 
 
             // Wait for any new events if we have nothing to do
             // Wait for any new events if we have nothing to do
             if !self.has_any_work() {
             if !self.has_any_work() {
-                self.clean_up_garbage();
                 let deadline_expired = self.wait_for_any_trigger(&mut deadline_reached).await;
                 let deadline_expired = self.wait_for_any_trigger(&mut deadline_reached).await;
 
 
                 if deadline_expired {
                 if deadline_expired {
@@ -425,7 +472,7 @@ impl Scheduler {
     /// Returns true if the lane is finished before the deadline could be met.
     /// Returns true if the lane is finished before the deadline could be met.
     pub fn work_on_current_lane(
     pub fn work_on_current_lane(
         &mut self,
         &mut self,
-        deadline_reached: &mut impl FnMut() -> bool,
+        deadline_reached: impl FnMut() -> bool,
         mutations: &mut Vec<Mutations>,
         mutations: &mut Vec<Mutations>,
     ) -> bool {
     ) -> bool {
         // Work through the current subtree, and commit the results when it finishes
         // Work through the current subtree, and commit the results when it finishes
@@ -448,7 +495,7 @@ impl Scheduler {
             if let Some(scope) = self.current_lane().dirty_scopes.pop() {
             if let Some(scope) = self.current_lane().dirty_scopes.pop() {
                 let component = self.pool.get_scope(scope).unwrap();
                 let component = self.pool.get_scope(scope).unwrap();
                 let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
                 let (old, new) = (component.frames.wip_head(), component.frames.fin_head());
-                machine.stack.push(DiffInstruction::DiffNode { new, old });
+                machine.stack.push(DiffInstruction::Diff { new, old });
             }
             }
         }
         }
 
 
@@ -537,61 +584,6 @@ impl Scheduler {
     fn collect_garbage(&mut self, id: ElementId) {
     fn collect_garbage(&mut self, id: ElementId) {
         //
         //
     }
     }
-
-    pub fn clean_up_garbage(&mut self) {
-        let mut scopes_to_kill = Vec::new();
-        let mut garbage_list = Vec::new();
-
-        for scope in self.garbage_scopes.drain() {
-            let scope = self.pool.get_scope_mut(scope).unwrap();
-            for node in scope.consume_garbage() {
-                garbage_list.push(node);
-            }
-
-            while let Some(node) = garbage_list.pop() {
-                match &node {
-                    VNode::Text(_) => {
-                        self.pool.collect_garbage(node.mounted_id());
-                    }
-                    VNode::Anchor(_) => {
-                        self.pool.collect_garbage(node.mounted_id());
-                    }
-                    VNode::Suspended(_) => {
-                        self.pool.collect_garbage(node.mounted_id());
-                    }
-
-                    VNode::Element(el) => {
-                        self.pool.collect_garbage(node.mounted_id());
-                        for child in el.children {
-                            garbage_list.push(child);
-                        }
-                    }
-
-                    VNode::Fragment(frag) => {
-                        for child in frag.children {
-                            garbage_list.push(child);
-                        }
-                    }
-
-                    VNode::Component(comp) => {
-                        // TODO: run the hook destructors and then even delete the scope
-
-                        let scope_id = comp.associated_scope.get().unwrap();
-                        let scope = self.pool.get_scope(scope_id).unwrap();
-                        let root = scope.root();
-
-                        garbage_list.push(root);
-                        scopes_to_kill.push(scope_id);
-                    }
-                }
-            }
-        }
-
-        for scope in scopes_to_kill.drain(..) {
-            //
-            // kill em
-        }
-    }
 }
 }
 
 
 pub(crate) struct PriorityLane {
 pub(crate) struct PriorityLane {
@@ -619,8 +611,8 @@ impl PriorityLane {
 }
 }
 
 
 pub struct TaskHandle {
 pub struct TaskHandle {
-    pub sender: UnboundedSender<SchedulerMsg>,
-    pub our_id: u64,
+    pub(crate) sender: UnboundedSender<SchedulerMsg>,
+    pub(crate) our_id: u64,
 }
 }
 
 
 impl TaskHandle {
 impl TaskHandle {

+ 0 - 22
packages/core/src/scope.rs

@@ -31,7 +31,6 @@ pub struct Scope {
     pub(crate) frames: ActiveFrame,
     pub(crate) frames: ActiveFrame,
     pub(crate) caller: Rc<WrappedCaller>,
     pub(crate) caller: Rc<WrappedCaller>,
     pub(crate) child_nodes: ScopeChildren<'static>,
     pub(crate) child_nodes: ScopeChildren<'static>,
-    pub(crate) pending_garbage: RefCell<Vec<*const VNode<'static>>>,
 
 
     // Listeners
     // Listeners
     pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
     pub(crate) listeners: RefCell<Vec<*const Listener<'static>>>,
@@ -90,7 +89,6 @@ impl Scope {
             listeners: Default::default(),
             listeners: Default::default(),
             borrowed_props: Default::default(),
             borrowed_props: Default::default(),
             descendents: Default::default(),
             descendents: Default::default(),
-            pending_garbage: Default::default(),
         }
         }
     }
     }
 
 
@@ -110,7 +108,6 @@ impl Scope {
         // Cycle to the next frame and then reset it
         // Cycle to the next frame and then reset it
         // This breaks any latent references, invalidating every pointer referencing into it.
         // This breaks any latent references, invalidating every pointer referencing into it.
         // Remove all the outdated listeners
         // Remove all the outdated listeners
-
         self.ensure_drop_safety(pool);
         self.ensure_drop_safety(pool);
 
 
         // Safety:
         // Safety:
@@ -146,12 +143,6 @@ impl Scope {
     /// Refrences to hook data can only be stored in listeners and component props. During diffing, we make sure to log
     /// Refrences to hook data can only be stored in listeners and component props. During diffing, we make sure to log
     /// all listeners and borrowed props so we can clear them here.
     /// all listeners and borrowed props so we can clear them here.
     pub(crate) fn ensure_drop_safety(&mut self, pool: &ResourcePool) {
     pub(crate) fn ensure_drop_safety(&mut self, pool: &ResourcePool) {
-        // make sure all garabge is collected before trying to proceed with anything else
-        debug_assert!(
-            self.pending_garbage.borrow().is_empty(),
-            "clean up your garabge please"
-        );
-
         // todo!("arch changes");
         // todo!("arch changes");
 
 
         // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
         // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
@@ -235,19 +226,6 @@ impl Scope {
         }
         }
     }
     }
 
 
-    pub(crate) fn consume_garbage(&self) -> Vec<&VNode> {
-        self.pending_garbage
-            .borrow_mut()
-            .drain(..)
-            .map(|node| {
-                // safety: scopes cannot cycle without their garbage being collected. these nodes are safe
-                let node: &VNode<'static> = unsafe { &*node };
-                let node: &VNode = unsafe { std::mem::transmute(node) };
-                node
-            })
-            .collect::<Vec<_>>()
-    }
-
     pub fn root(&self) -> &VNode {
     pub fn root(&self) -> &VNode {
         self.frames.fin_head()
         self.frames.fin_head()
     }
     }

+ 20 - 8
packages/core/src/test_dom.rs

@@ -20,17 +20,25 @@ impl TestDom {
         NodeFactory::new(&self.bump)
         NodeFactory::new(&self.bump)
     }
     }
 
 
-    pub fn render<'a, F>(&'a self, lazy_nodes: LazyNodes<'a, F>) -> VNode<'a>
+    pub fn render_direct<'a, F>(&'a self, lazy_nodes: LazyNodes<'a, F>) -> VNode<'a>
     where
     where
         F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
         F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
     {
     {
         lazy_nodes.into_vnode(NodeFactory::new(&self.bump))
         lazy_nodes.into_vnode(NodeFactory::new(&self.bump))
     }
     }
 
 
+    pub fn render<'a, F>(&'a self, lazy_nodes: LazyNodes<'a, F>) -> &'a VNode<'a>
+    where
+        F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+    {
+        self.bump
+            .alloc(lazy_nodes.into_vnode(NodeFactory::new(&self.bump)))
+    }
+
     pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
     pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
         let mutations = Mutations::new();
         let mutations = Mutations::new();
         let mut machine = DiffMachine::new(mutations, &self.scheduler.pool);
         let mut machine = DiffMachine::new(mutations, &self.scheduler.pool);
-        machine.stack.push(DiffInstruction::DiffNode { new, old });
+        machine.stack.push(DiffInstruction::Diff { new, old });
         machine.mutations
         machine.mutations
     }
     }
 
 
@@ -38,7 +46,7 @@ impl TestDom {
     where
     where
         F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
         F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
     {
     {
-        let old = self.bump.alloc(self.render(left));
+        let old = self.bump.alloc(self.render_direct(left));
 
 
         let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
         let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
 
 
@@ -58,20 +66,18 @@ impl TestDom {
         F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
         F1: FnOnce(NodeFactory<'a>) -> VNode<'a>,
         F2: FnOnce(NodeFactory<'a>) -> VNode<'a>,
         F2: FnOnce(NodeFactory<'a>) -> VNode<'a>,
     {
     {
-        let old = self.bump.alloc(self.render(left));
-
-        let new = self.bump.alloc(self.render(right));
+        let (old, new) = (self.render(left), self.render(right));
 
 
         let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
         let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
 
 
         machine.stack.create_node(old, MountType::Append);
         machine.stack.create_node(old, MountType::Append);
 
 
-        machine.work(&mut || false);
+        machine.work(|| false);
         let create_edits = machine.mutations;
         let create_edits = machine.mutations;
 
 
         let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
         let mut machine = DiffMachine::new(Mutations::new(), &self.scheduler.pool);
 
 
-        machine.stack.push(DiffInstruction::DiffNode { old, new });
+        machine.stack.push(DiffInstruction::Diff { old, new });
 
 
         machine.work(&mut || false);
         machine.work(&mut || false);
 
 
@@ -80,3 +86,9 @@ impl TestDom {
         (create_edits, edits)
         (create_edits, edits)
     }
     }
 }
 }
+
+impl VirtualDom {
+    pub fn simulate(&mut self) {
+        //
+    }
+}

+ 42 - 29
packages/core/src/virtual_dom.rs

@@ -1,4 +1,5 @@
 //! # VirtualDOM Implementation for Rust
 //! # VirtualDOM Implementation for Rust
+//!
 //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
 //! This module provides the primary mechanics to create a hook-based, concurrent VDOM for Rust.
 //!
 //!
 //! In this file, multiple items are defined. This file is big, but should be documented well to
 //! In this file, multiple items are defined. This file is big, but should be documented well to
@@ -20,21 +21,38 @@
 
 
 use crate::innerlude::*;
 use crate::innerlude::*;
 use futures_util::{Future, FutureExt};
 use futures_util::{Future, FutureExt};
-use std::{
-    any::{Any, TypeId},
-    pin::Pin,
-    rc::Rc,
-};
+use std::{any::Any, pin::Pin};
 
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 /// An integrated virtual node system that progresses events and diffs UI trees.
-/// Differences are converted into patches which a renderer can use to draw the UI.
-///
-///
 ///
 ///
+/// Differences are converted into patches which a renderer can use to draw the UI.
 ///
 ///
+/// If you are building an App with Dioxus, you probably won't want to reach for this directly, instead opting to defer
+/// to a particular crate's wrapper over the [`VirtualDom`] API.
 ///
 ///
+/// Example
+/// ```rust
+/// static App: FC<()> = |cx| {
+///     cx.render(rsx!{
+///         div {
+///             "Hello World"
+///         }
+///     })
+/// }
 ///
 ///
+/// async fn main() {
+///     let mut dom = VirtualDom::new(App);
+///     let mut inital_edits = dom.rebuild();
+///     initialize_screen(inital_edits);
 ///
 ///
+///     loop {
+///         let next_frame = TimeoutFuture::new(Duration::from_millis(16));
+///         let edits = dom.run_with_deadline(next_frame).await;
+///         apply_edits(edits);
+///         render_frame();
+///     }
+/// }
+/// ```
 pub struct VirtualDom {
 pub struct VirtualDom {
     scheduler: Scheduler,
     scheduler: Scheduler,
 
 
@@ -42,7 +60,7 @@ pub struct VirtualDom {
 
 
     root_fc: Box<dyn Any>,
     root_fc: Box<dyn Any>,
 
 
-    root_props: Pin<Box<dyn std::any::Any>>,
+    root_props: Pin<Box<dyn Any>>,
 }
 }
 
 
 impl VirtualDom {
 impl VirtualDom {
@@ -178,28 +196,22 @@ impl VirtualDom {
         }
         }
     }
     }
 
 
-    /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom rom scratch
+    /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom from scratch
     ///
     ///
-    /// The diff machine expects the RealDom's stack to be the root of the application
+    /// The diff machine expects the RealDom's stack to be the root of the application.
     ///
     ///
-    /// Events like garabge collection, application of refs, etc are not handled by this method and can only be progressed
-    /// through "run". We completely avoid the task scheduler infrastructure.
-    pub fn rebuild<'s>(&'s mut self) -> Mutations<'s> {
-        let mut fut = self.rebuild_async().boxed_local();
-
-        loop {
-            if let Some(edits) = (&mut fut).now_or_never() {
-                break edits;
-            }
-        }
-    }
-
-    /// Rebuild the dom from the ground up
+    /// Tasks will not be polled with this method, nor will any events be processed from the event queue. Instead, the
+    /// root component will be ran once and then diffed. All updates will flow out as mutations.
+    ///
+    /// # Example
+    /// ```
+    /// static App: FC<()> = |cx| cx.render(rsx!{ "hello world" });
+    /// let mut dom = VirtualDom::new();
+    /// let edits = dom.rebuild();
     ///
     ///
-    /// This method is asynchronous to prevent the application from blocking while the dom is being rebuilt. Computing
-    /// the diff and creating nodes can be expensive, so we provide this method to avoid blocking the main thread. This
-    /// method can be useful when needing to perform some crucial periodic tasks.
-    pub async fn rebuild_async<'s>(&'s mut self) -> Mutations<'s> {
+    /// apply_edits(edits);
+    /// ```
+    pub fn rebuild<'s>(&'s mut self) -> Mutations<'s> {
         let mut shared = self.scheduler.pool.clone();
         let mut shared = self.scheduler.pool.clone();
         let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
         let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared);
 
 
@@ -214,9 +226,10 @@ impl VirtualDom {
             diff_machine
             diff_machine
                 .stack
                 .stack
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
                 .create_node(cur_component.frames.fin_head(), MountType::Append);
+
             diff_machine.stack.scope_stack.push(self.base_scope);
             diff_machine.stack.scope_stack.push(self.base_scope);
 
 
-            // let completed = diff_machine.work();
+            diff_machine.work(|| false);
         } else {
         } else {
             // todo: should this be a hard error?
             // todo: should this be a hard error?
             log::warn!(
             log::warn!(

+ 53 - 0
packages/core/tests/README.md

@@ -0,0 +1,53 @@
+# Testing of Dioxus core
+
+
+NodeFactory
+- [] rsx, html, NodeFactory generate the same structures
+
+Diffing
+- [x] create elements
+- [x] create text
+- [x] create fragments
+- [x] create empty fragments (placeholders)
+- [x] diff elements
+- [x] diff from element/text to fragment
+- [x] diff from element/text to empty fragment
+- [x] diff to element with children works too
+- [x] replace with works forward
+- [x] replace with works backward
+- [x] un-keyed diffing
+- [x] keyed diffing
+- [x] keyed diffing out of order
+- [x] keyed diffing with prefix/suffix
+- [x] suspended nodes work 
+
+Lifecycle
+- [] Components mount properly
+- [] Components create new child components
+- [] Replaced components unmount old components and mount new
+- [] Post-render effects are called
+- [] 
+
+
+Shared Context
+- [] Shared context propagates downwards
+- [] unwrapping shared context if it doesn't exist works too
+
+Suspense
+- [] use_suspense generates suspended nodes
+
+
+Hooks 
+- [] Drop order is maintained
+- [] Shared hook state is okay
+- [] use_hook works
+- [] use_ref works
+- [] use_noderef works
+- [] use_provide_state
+- [] use_consume_state
+
+
+VirtualDOM API
+- [] work
+- [] rebuild
+- [] change props

+ 0 - 19
packages/core/tests/channels.rs

@@ -1,19 +0,0 @@
-use futures_channel::mpsc::unbounded;
-
-#[async_std::test]
-async fn channels() {
-    let (sender, mut receiver) = unbounded::<u32>();
-
-    // drop(sender);
-
-    match receiver.try_next() {
-        Ok(a) => {
-            dbg!(a);
-        }
-        Err(no) => {
-            dbg!(no);
-        }
-    }
-
-    sender.unbounded_send(1).unwrap();
-}

+ 36 - 34
packages/core/tests/create_iterative.rs → packages/core/tests/create_dom.rs

@@ -1,17 +1,23 @@
-//! tests to prove that the iterative implementation works
+//! Prove that the dom works normally through virtualdom methods.
+//! This methods all use "rebuild" which completely bypasses the scheduler.
+//! Hard rebuilds don't consume any events from the event queue.
 
 
-use anyhow::{Context, Result};
-use dioxus::{prelude::*, DomEdit, Mutations};
-mod test_logging;
+use dioxus::{prelude::*, DomEdit};
 use dioxus_core as dioxus;
 use dioxus_core as dioxus;
 use dioxus_html as dioxus_elements;
 use dioxus_html as dioxus_elements;
+
+mod test_logging;
 use DomEdit::*;
 use DomEdit::*;
 
 
-const LOGGING_ENABLED: bool = false;
+fn new_dom<P: Properties + 'static>(app: FC<P>, props: P) -> VirtualDom {
+    const IS_LOGGING_ENABLED: bool = false;
+    test_logging::set_up_logging(IS_LOGGING_ENABLED);
+    VirtualDom::new_with_props(app, props)
+}
 
 
 #[test]
 #[test]
 fn test_original_diff() {
 fn test_original_diff() {
-    static App: FC<()> = |cx| {
+    static APP: FC<()> = |cx| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {
             div {
                 div {
                 div {
@@ -21,7 +27,7 @@ fn test_original_diff() {
         })
         })
     };
     };
 
 
-    let mut dom = VirtualDom::new(App);
+    let mut dom = new_dom(APP, ());
     let mutations = dom.rebuild();
     let mutations = dom.rebuild();
     assert_eq!(
     assert_eq!(
         mutations.edits,
         mutations.edits,
@@ -41,7 +47,7 @@ fn test_original_diff() {
 
 
 #[async_std::test]
 #[async_std::test]
 async fn create() {
 async fn create() {
-    static App: FC<()> = |cx| {
+    static APP: FC<()> = |cx| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {
             div {
                 div {
                 div {
@@ -59,9 +65,9 @@ async fn create() {
         })
         })
     };
     };
 
 
-    test_logging::set_up_logging(LOGGING_ENABLED);
-    let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await;
+    let mut dom = new_dom(APP, ());
+    let mutations = dom.rebuild();
+
     assert_eq!(
     assert_eq!(
         mutations.edits,
         mutations.edits,
         [
         [
@@ -92,7 +98,7 @@ async fn create() {
 
 
 #[async_std::test]
 #[async_std::test]
 async fn create_list() {
 async fn create_list() {
-    static App: FC<()> = |cx| {
+    static APP: FC<()> = |cx| {
         cx.render(rsx! {
         cx.render(rsx! {
             {(0..3).map(|f| rsx!{ div {
             {(0..3).map(|f| rsx!{ div {
                 "hello"
                 "hello"
@@ -100,10 +106,8 @@ async fn create_list() {
         })
         })
     };
     };
 
 
-    test_logging::set_up_logging(LOGGING_ENABLED);
-
-    let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await;
+    let mut dom = new_dom(APP, ());
+    let mutations = dom.rebuild();
 
 
     // copilot wrote this test :P
     // copilot wrote this test :P
     assert_eq!(
     assert_eq!(
@@ -134,7 +138,7 @@ async fn create_list() {
 
 
 #[async_std::test]
 #[async_std::test]
 async fn create_simple() {
 async fn create_simple() {
-    static App: FC<()> = |cx| {
+    static APP: FC<()> = |cx| {
         cx.render(rsx! {
         cx.render(rsx! {
             div {}
             div {}
             div {}
             div {}
@@ -143,10 +147,8 @@ async fn create_simple() {
         })
         })
     };
     };
 
 
-    test_logging::set_up_logging(LOGGING_ENABLED);
-
-    let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await;
+    let mut dom = new_dom(APP, ());
+    let mutations = dom.rebuild();
 
 
     // copilot wrote this test :P
     // copilot wrote this test :P
     assert_eq!(
     assert_eq!(
@@ -179,10 +181,8 @@ async fn create_components() {
         })
         })
     };
     };
 
 
-    test_logging::set_up_logging(LOGGING_ENABLED);
-
-    let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await;
+    let mut dom = new_dom(App, ());
+    let mutations = dom.rebuild();
 
 
     assert_eq!(
     assert_eq!(
         mutations.edits,
         mutations.edits,
@@ -225,10 +225,8 @@ async fn anchors() {
         })
         })
     };
     };
 
 
-    test_logging::set_up_logging(LOGGING_ENABLED);
-
-    let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await;
+    let mut dom = new_dom(App, ());
+    let mutations = dom.rebuild();
     assert_eq!(
     assert_eq!(
         mutations.edits,
         mutations.edits,
         [
         [
@@ -247,14 +245,18 @@ async fn anchors() {
 #[async_std::test]
 #[async_std::test]
 async fn suspended() {
 async fn suspended() {
     static App: FC<()> = |cx| {
     static App: FC<()> = |cx| {
-        let val = use_suspense(cx, || async {}, |cx, _| cx.render(rsx! { "hi "}));
+        let val = use_suspense(
+            cx,
+            || async {
+                //
+            },
+            |cx, _| cx.render(rsx! { "hi "}),
+        );
         cx.render(rsx! { {val} })
         cx.render(rsx! { {val} })
     };
     };
 
 
-    test_logging::set_up_logging(LOGGING_ENABLED);
-
-    let mut dom = VirtualDom::new(App);
-    let mutations = dom.rebuild_async().await;
+    let mut dom = new_dom(App, ());
+    let mutations = dom.rebuild();
 
 
     assert_eq!(
     assert_eq!(
         mutations.edits,
         mutations.edits,

+ 0 - 2
packages/core/tests/debugdiff.rs

@@ -1,2 +0,0 @@
-/// A virtualdom wrapper used for testing purposes.
-pub struct DebugDiff {}

+ 0 - 48
packages/core/tests/diff_iterative.rs

@@ -1,48 +0,0 @@
-//! tests to prove that the iterative implementation works
-
-use dioxus::prelude::*;
-
-mod test_logging;
-use dioxus_core as dioxus;
-use dioxus_html as dioxus_elements;
-
-const LOGGING_ENABLED: bool = false;
-
-#[async_std::test]
-async fn test_iterative_create_components() {
-    static App: FC<()> = |cx| {
-        // test root fragments
-        cx.render(rsx! {
-            Child { "abc1" }
-            Child { "abc2" }
-            Child { "abc3" }
-        })
-    };
-
-    fn Child(cx: Context<()>) -> DomTree {
-        // test root fragments, anchors, and ChildNode type
-        cx.render(rsx! {
-            h1 {}
-            div { {cx.children()} }
-            Fragment {
-                Fragment {
-                    Fragment {
-                        "wozza"
-                    }
-                }
-            }
-            {(0..0).map(|_f| rsx!{ div { "walalla"}})}
-            p {}
-        })
-    }
-
-    test_logging::set_up_logging(LOGGING_ENABLED);
-
-    let mut dom = VirtualDom::new(App);
-
-    let mutations = dom.rebuild_async().await;
-    dbg!(mutations);
-
-    let mutations = dom.diff();
-    dbg!(mutations);
-}

+ 32 - 19
packages/core/tests/diffing.rs

@@ -4,24 +4,19 @@
 //!
 //!
 //! It does not validated that component lifecycles work properly. This is done in another test file.
 //! It does not validated that component lifecycles work properly. This is done in another test file.
 
 
-use dioxus::{prelude::*, DomEdit, TestDom};
+use dioxus::{nodes::VSuspended, prelude::*, DomEdit, TestDom};
 use dioxus_core as dioxus;
 use dioxus_core as dioxus;
 use dioxus_html as dioxus_elements;
 use dioxus_html as dioxus_elements;
 
 
 mod test_logging;
 mod test_logging;
-use DomEdit::*;
-
-// logging is wired up to the test harness
-// feel free to enable while debugging
-const IS_LOGGING_ENABLED: bool = false;
 
 
 fn new_dom() -> TestDom {
 fn new_dom() -> TestDom {
+    const IS_LOGGING_ENABLED: bool = false;
     test_logging::set_up_logging(IS_LOGGING_ENABLED);
     test_logging::set_up_logging(IS_LOGGING_ENABLED);
     TestDom::new()
     TestDom::new()
 }
 }
 
 
-#[test]
-fn diffing_works() {}
+use DomEdit::*;
 
 
 /// Should push the text node onto the stack and modify it
 /// Should push the text node onto the stack and modify it
 #[test]
 #[test]
@@ -98,8 +93,8 @@ fn fragments_create_properly() {
 fn empty_fragments_create_anchors() {
 fn empty_fragments_create_anchors() {
     let dom = new_dom();
     let dom = new_dom();
 
 
-    let left = rsx!({ (0..0).map(|f| rsx! { div {}}) });
-    let right = rsx!({ (0..1).map(|f| rsx! { div {}}) });
+    let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
+    let right = rsx!({ (0..1).map(|_f| rsx! { div {}}) });
 
 
     let (create, change) = dom.lazy_diff(left, right);
     let (create, change) = dom.lazy_diff(left, right);
 
 
@@ -121,8 +116,8 @@ fn empty_fragments_create_anchors() {
 fn empty_fragments_create_many_anchors() {
 fn empty_fragments_create_many_anchors() {
     let dom = new_dom();
     let dom = new_dom();
 
 
-    let left = rsx!({ (0..0).map(|f| rsx! { div {}}) });
-    let right = rsx!({ (0..5).map(|f| rsx! { div {}}) });
+    let left = rsx!({ (0..0).map(|_f| rsx! { div {}}) });
+    let right = rsx!({ (0..5).map(|_f| rsx! { div {}}) });
 
 
     let (create, change) = dom.lazy_diff(left, right);
     let (create, change) = dom.lazy_diff(left, right);
     assert_eq!(
     assert_eq!(
@@ -148,7 +143,7 @@ fn empty_fragments_create_many_anchors() {
 fn empty_fragments_create_anchors_with_many_children() {
 fn empty_fragments_create_anchors_with_many_children() {
     let dom = new_dom();
     let dom = new_dom();
 
 
-    let left = rsx!({ (0..0).map(|f| rsx! { div {} }) });
+    let left = rsx!({ (0..0).map(|_| rsx! { div {} }) });
     let right = rsx!({
     let right = rsx!({
         (0..3).map(|f| {
         (0..3).map(|f| {
             rsx! { div { "hello: {f}" }}
             rsx! { div { "hello: {f}" }}
@@ -192,11 +187,11 @@ fn many_items_become_fragment() {
     let dom = new_dom();
     let dom = new_dom();
 
 
     let left = rsx!({
     let left = rsx!({
-        (0..2).map(|f| {
+        (0..2).map(|_| {
             rsx! { div { "hello" }}
             rsx! { div { "hello" }}
         })
         })
     });
     });
-    let right = rsx!({ (0..0).map(|f| rsx! { div {} }) });
+    let right = rsx!({ (0..0).map(|_| rsx! { div {} }) });
 
 
     let (create, change) = dom.lazy_diff(left, right);
     let (create, change) = dom.lazy_diff(left, right);
     assert_eq!(
     assert_eq!(
@@ -235,17 +230,17 @@ fn two_equal_fragments_are_equal() {
     let dom = new_dom();
     let dom = new_dom();
 
 
     let left = rsx!({
     let left = rsx!({
-        (0..2).map(|f| {
+        (0..2).map(|_| {
             rsx! { div { "hello" }}
             rsx! { div { "hello" }}
         })
         })
     });
     });
     let right = rsx!({
     let right = rsx!({
-        (0..2).map(|f| {
+        (0..2).map(|_| {
             rsx! { div { "hello" }}
             rsx! { div { "hello" }}
         })
         })
     });
     });
 
 
-    let (create, change) = dom.lazy_diff(left, right);
+    let (_create, change) = dom.lazy_diff(left, right);
     assert!(change.edits.is_empty());
     assert!(change.edits.is_empty());
 }
 }
 
 
@@ -263,7 +258,7 @@ fn two_fragments_with_differrent_elements_are_differet() {
         p {}
         p {}
     );
     );
 
 
-    let (create, changes) = dom.lazy_diff(left, right);
+    let (_create, changes) = dom.lazy_diff(left, right);
     log::debug!("{:#?}", &changes);
     log::debug!("{:#?}", &changes);
     assert_eq!(
     assert_eq!(
         changes.edits,
         changes.edits,
@@ -700,3 +695,21 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
         ]
         ]
     );
     );
 }
 }
+
+#[test]
+fn suspense() {
+    let dom = new_dom();
+
+    let edits = dom.create(LazyNodes::new(|f| {
+        use std::cell::{Cell, RefCell};
+        VNode::Suspended(f.bump().alloc(VSuspended {
+            task_id: 0,
+            callback: RefCell::new(None),
+            dom_id: Cell::new(None),
+        }))
+    }));
+    assert_eq!(
+        edits.edits,
+        [CreatePlaceholder { id: 0 }, AppendChildren { many: 1 }]
+    );
+}

+ 0 - 6
packages/core/tests/integration.rs

@@ -1,6 +0,0 @@
-
-// type VirtualNode = VNode;
-
-/// Test a basic usage of a virtual dom + text renderer combo
-#[test]
-fn simple_integration() {}

+ 1 - 0
packages/core/tests/lifecycle.rs

@@ -0,0 +1 @@
+//! Tests for the lifecycle of components.

+ 1 - 0
packages/core/tests/scheduler.rs

@@ -0,0 +1 @@
+//! Tests for the scheduler.

+ 0 - 30
packages/core/tests/work_sync.rs

@@ -1,30 +0,0 @@
-//! Diffing is interruptible, but uses yield_now which is loop-pollable
-//!
-//! This means you can actually call it synchronously if you want.
-
-use anyhow::{Context, Result};
-use dioxus::{prelude::*, scope::Scope};
-use dioxus_core as dioxus;
-use dioxus_html as dioxus_elements;
-use futures_util::FutureExt;
-
-#[test]
-fn worksync() {
-    static App: FC<()> = |cx| {
-        cx.render(rsx! {
-            div {"hello"}
-        })
-    };
-    let mut dom = VirtualDom::new(App);
-
-    let mut fut = dom.rebuild_async().boxed_local();
-
-    let mutations = loop {
-        let g = (&mut fut).now_or_never();
-        if g.is_some() {
-            break g.unwrap();
-        }
-    };
-
-    dbg!(mutations);
-}

+ 0 - 4
packages/web/Cargo.toml

@@ -65,10 +65,6 @@ features = [
     "HtmlOptionElement",
     "HtmlOptionElement",
 ]
 ]
 
 
-[profile.release]
-lto = true
-opt-level = 's'
-
 [lib]
 [lib]
 crate-type = ["cdylib", "rlib"]
 crate-type = ["cdylib", "rlib"]
 
 

+ 79 - 79
packages/web/src/dom.rs

@@ -582,85 +582,85 @@ pub fn load_document() -> Document {
 
 
 pub fn event_name_from_typ(typ: &str) -> &'static str {
 pub fn event_name_from_typ(typ: &str) -> &'static str {
     match typ {
     match typ {
-        "copy" => "oncopy",
-        "cut" => "oncut",
-        "paste" => "onpaste",
-        "compositionend" => "oncompositionend",
-        "compositionstart" => "oncompositionstart",
-        "compositionupdate" => "oncompositionupdate",
-        "keydown" => "onkeydown",
-        "keypress" => "onkeypress",
-        "keyup" => "onkeyup",
-        "focus" => "onfocus",
-        "blur" => "onblur",
-        "change" => "onchange",
-        "input" => "oninput",
-        "invalid" => "oninvalid",
-        "reset" => "onreset",
-        "submit" => "onsubmit",
-        "click" => "onclick",
-        "contextmenu" => "oncontextmenu",
-        "doubleclick" => "ondoubleclick",
-        "drag" => "ondrag",
-        "dragend" => "ondragend",
-        "dragenter" => "ondragenter",
-        "dragexit" => "ondragexit",
-        "dragleave" => "ondragleave",
-        "dragover" => "ondragover",
-        "dragstart" => "ondragstart",
-        "drop" => "ondrop",
-        "mousedown" => "onmousedown",
-        "mouseenter" => "onmouseenter",
-        "mouseleave" => "onmouseleave",
-        "mousemove" => "onmousemove",
-        "mouseout" => "onmouseout",
-        "mouseover" => "onmouseover",
-        "mouseup" => "onmouseup",
-        "pointerdown" => "onpointerdown",
-        "pointermove" => "onpointermove",
-        "pointerup" => "onpointerup",
-        "pointercancel" => "onpointercancel",
-        "gotpointercapture" => "ongotpointercapture",
-        "lostpointercapture" => "onlostpointercapture",
-        "pointerenter" => "onpointerenter",
-        "pointerleave" => "onpointerleave",
-        "pointerover" => "onpointerover",
-        "pointerout" => "onpointerout",
-        "select" => "onselect",
-        "touchcancel" => "ontouchcancel",
-        "touchend" => "ontouchend",
-        "touchmove" => "ontouchmove",
-        "touchstart" => "ontouchstart",
-        "scroll" => "onscroll",
-        "wheel" => "onwheel",
-        "animationstart" => "onanimationstart",
-        "animationend" => "onanimationend",
-        "animationiteration" => "onanimationiteration",
-        "transitionend" => "ontransitionend",
-        "abort" => "onabort",
-        "canplay" => "oncanplay",
-        "canplaythrough" => "oncanplaythrough",
-        "durationchange" => "ondurationchange",
-        "emptied" => "onemptied",
-        "encrypted" => "onencrypted",
-        "ended" => "onended",
-        "error" => "onerror",
-        "loadeddata" => "onloadeddata",
-        "loadedmetadata" => "onloadedmetadata",
-        "loadstart" => "onloadstart",
-        "pause" => "onpause",
-        "play" => "onplay",
-        "playing" => "onplaying",
-        "progress" => "onprogress",
-        "ratechange" => "onratechange",
-        "seeked" => "onseeked",
-        "seeking" => "onseeking",
-        "stalled" => "onstalled",
-        "suspend" => "onsuspend",
-        "timeupdate" => "ontimeupdate",
-        "volumechange" => "onvolumechange",
-        "waiting" => "onwaiting",
-        "toggle" => "ontoggle",
+        "copy" => "copy",
+        "cut" => "cut",
+        "paste" => "paste",
+        "compositionend" => "compositionend",
+        "compositionstart" => "compositionstart",
+        "compositionupdate" => "compositionupdate",
+        "keydown" => "keydown",
+        "keypress" => "keypress",
+        "keyup" => "keyup",
+        "focus" => "focus",
+        "blur" => "blur",
+        "change" => "change",
+        "input" => "input",
+        "invalid" => "invalid",
+        "reset" => "reset",
+        "submit" => "submit",
+        "click" => "click",
+        "contextmenu" => "contextmenu",
+        "doubleclick" => "doubleclick",
+        "drag" => "drag",
+        "dragend" => "dragend",
+        "dragenter" => "dragenter",
+        "dragexit" => "dragexit",
+        "dragleave" => "dragleave",
+        "dragover" => "dragover",
+        "dragstart" => "dragstart",
+        "drop" => "drop",
+        "mousedown" => "mousedown",
+        "mouseenter" => "mouseenter",
+        "mouseleave" => "mouseleave",
+        "mousemove" => "mousemove",
+        "mouseout" => "mouseout",
+        "mouseover" => "mouseover",
+        "mouseup" => "mouseup",
+        "pointerdown" => "pointerdown",
+        "pointermove" => "pointermove",
+        "pointerup" => "pointerup",
+        "pointercancel" => "pointercancel",
+        "gotpointercapture" => "gotpointercapture",
+        "lostpointercapture" => "lostpointercapture",
+        "pointerenter" => "pointerenter",
+        "pointerleave" => "pointerleave",
+        "pointerover" => "pointerover",
+        "pointerout" => "pointerout",
+        "select" => "select",
+        "touchcancel" => "touchcancel",
+        "touchend" => "touchend",
+        "touchmove" => "touchmove",
+        "touchstart" => "touchstart",
+        "scroll" => "scroll",
+        "wheel" => "wheel",
+        "animationstart" => "animationstart",
+        "animationend" => "animationend",
+        "animationiteration" => "animationiteration",
+        "transitionend" => "transitionend",
+        "abort" => "abort",
+        "canplay" => "canplay",
+        "canplaythrough" => "canplaythrough",
+        "durationchange" => "durationchange",
+        "emptied" => "emptied",
+        "encrypted" => "encrypted",
+        "ended" => "ended",
+        "error" => "error",
+        "loadeddata" => "loadeddata",
+        "loadedmetadata" => "loadedmetadata",
+        "loadstart" => "loadstart",
+        "pause" => "pause",
+        "play" => "play",
+        "playing" => "playing",
+        "progress" => "progress",
+        "ratechange" => "ratechange",
+        "seeked" => "seeked",
+        "seeking" => "seeking",
+        "stalled" => "stalled",
+        "suspend" => "suspend",
+        "timeupdate" => "timeupdate",
+        "volumechange" => "volumechange",
+        "waiting" => "waiting",
+        "toggle" => "toggle",
         _ => {
         _ => {
             panic!("unsupported event type")
             panic!("unsupported event type")
         }
         }