瀏覽代碼

feat: async components!

Jonathan Kelley 2 年之前
父節點
當前提交
94601ccd1f

+ 17 - 9
packages/core/src/any_props.rs

@@ -1,7 +1,9 @@
+use std::marker::PhantomData;
+
 use futures_util::Future;
 use futures_util::Future;
 
 
 use crate::{
 use crate::{
-    component::{Component, ComponentFn, Dummy, IntoComponent},
+    factory::ReturnType,
     scopes::{Scope, ScopeState},
     scopes::{Scope, ScopeState},
     Element,
     Element,
 };
 };
@@ -12,25 +14,30 @@ pub trait AnyProps<'a> {
     unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
     unsafe fn memoize(&self, other: &dyn AnyProps) -> bool;
 }
 }
 
 
-pub(crate) struct VComponentProps<'a, P, F: Future<Output = Element<'a>> = Dummy<'a>> {
-    pub render_fn: ComponentFn<'a, P, F>,
+pub(crate) struct VComponentProps<'a, P, A, F = Element<'a>>
+where
+    F: ReturnType<'a, A>,
+{
+    pub render_fn: fn(Scope<'a, P>) -> F,
     pub memo: unsafe fn(&P, &P) -> bool,
     pub memo: unsafe fn(&P, &P) -> bool,
     pub props: *const P,
     pub props: *const P,
+    pub _marker: PhantomData<A>,
 }
 }
 
 
-impl<'a> VComponentProps<'a, ()> {
-    pub fn new_empty(render_fn: Component<'a, ()>) -> Self {
+impl<'a> VComponentProps<'a, (), ()> {
+    pub fn new_empty(render_fn: fn(Scope) -> Element) -> Self {
         Self {
         Self {
-            render_fn: render_fn.into_component(),
+            render_fn,
             memo: <() as PartialEq>::eq,
             memo: <() as PartialEq>::eq,
             props: std::ptr::null_mut(),
             props: std::ptr::null_mut(),
+            _marker: PhantomData,
         }
         }
     }
     }
 }
 }
 
 
-impl<'a, P, F: Future<Output = Element<'a>>> VComponentProps<'a, P, F> {
+impl<'a, P, A, F: ReturnType<'a, A>> VComponentProps<'a, P, A, F> {
     pub(crate) fn new(
     pub(crate) fn new(
-        render_fn: ComponentFn<'a, P, F>,
+        render_fn: fn(Scope<'a, P>) -> F,
         memo: unsafe fn(&P, &P) -> bool,
         memo: unsafe fn(&P, &P) -> bool,
         props: *const P,
         props: *const P,
     ) -> Self {
     ) -> Self {
@@ -38,11 +45,12 @@ impl<'a, P, F: Future<Output = Element<'a>>> VComponentProps<'a, P, F> {
             render_fn,
             render_fn,
             memo,
             memo,
             props,
             props,
+            _marker: PhantomData,
         }
         }
     }
     }
 }
 }
 
 
-impl<'a, P, F: Future<Output = Element<'a>>> AnyProps<'a> for VComponentProps<'a, P, F> {
+impl<'a, P, A, F: ReturnType<'a, A>> AnyProps<'a> for VComponentProps<'a, P, A, F> {
     fn as_ptr(&self) -> *const () {
     fn as_ptr(&self) -> *const () {
         &self.props as *const _ as *const ()
         &self.props as *const _ as *const ()
     }
     }

+ 0 - 70
packages/core/src/component.rs

@@ -9,73 +9,3 @@ use futures_util::Future;
 use crate::{scopes::Scope, Element};
 use crate::{scopes::Scope, Element};
 
 
 pub type Component<'a, T = ()> = fn(Scope<'a, T>) -> Element<'a>;
 pub type Component<'a, T = ()> = fn(Scope<'a, T>) -> Element<'a>;
-
-pub enum ComponentFn<'a, T, F: Future<Output = Element<'a>> = Dummy<'a>> {
-    Sync(fn(Scope<'a, T>) -> Element),
-    Async(fn(Scope<'a, T>) -> F),
-}
-
-pub trait IntoComponent<'a, T, F: Future<Output = Element<'a>> = Dummy<'a>, A = ()> {
-    fn into_component(self) -> ComponentFn<'a, T, F>;
-}
-
-impl<'a, T> IntoComponent<'a, T, Dummy<'a>> for fn(Scope<'a, T>) -> Element<'a> {
-    fn into_component(self) -> ComponentFn<'a, T> {
-        ComponentFn::Sync(self)
-    }
-}
-
-enum ComponentFn2 {
-    Sync(fn(Scope) -> Element),
-}
-
-trait AsComponent {
-    fn as_component(self) -> ComponentFn2;
-}
-
-// impl AsComponent for fn(Scope) -> Element {
-//     fn as_component(self) -> ComponentFn2 {
-//         ComponentFn2::Sync(self)
-//     }
-// }
-
-// impl<F> AsComponent for for<'r> fn(Scope<'r>) ->
-// where
-//     F: Future<Output = Element<'r>>,
-// {
-//     fn as_component(self) -> ComponentFn2 {
-//         ComponentFn2::Sync(self)
-//     }
-// }
-
-fn takes_f(f: impl AsComponent) {}
-
-#[test]
-fn example() {
-    fn app(cx: Scope) -> Element {
-        todo!()
-    }
-
-    // takes_f(app as fn(Scope) -> Element);
-}
-
-// pub struct AsyncMarker;
-// impl<'a, T, F: Future<Output = Element<'a>>> IntoComponent<'a, T, F, AsyncMarker>
-//     for fn(Scope<'a, T>) -> F
-// {
-//     fn into_component(self) -> ComponentFn<'a, T, F> {
-//         ComponentFn::Async(self)
-//     }
-// }
-
-pub struct Dummy<'a>(PhantomData<&'a ()>);
-impl<'a> Future for Dummy<'a> {
-    type Output = Element<'a>;
-
-    fn poll(
-        self: std::pin::Pin<&mut Self>,
-        _cx: &mut std::task::Context<'_>,
-    ) -> std::task::Poll<Self::Output> {
-        unreachable!()
-    }
-}

+ 13 - 10
packages/core/src/create.rs

@@ -3,7 +3,7 @@ use crate::mutations::Mutation::*;
 use crate::nodes::VNode;
 use crate::nodes::VNode;
 use crate::nodes::{DynamicNode, TemplateNode};
 use crate::nodes::{DynamicNode, TemplateNode};
 use crate::virtualdom::VirtualDom;
 use crate::virtualdom::VirtualDom;
-use crate::{AttributeValue, TemplateAttribute};
+use crate::{AttributeValue, ElementId, TemplateAttribute};
 
 
 impl VirtualDom {
 impl VirtualDom {
     /// Create this template and write its mutations
     /// Create this template and write its mutations
@@ -106,7 +106,7 @@ impl VirtualDom {
         on_stack
         on_stack
     }
     }
 
 
-    fn create_static_node<'a>(
+    pub fn create_static_node<'a>(
         &mut self,
         &mut self,
         mutations: &mut Vec<Mutation<'a>>,
         mutations: &mut Vec<Mutation<'a>>,
         template: &'a VNode<'a>,
         template: &'a VNode<'a>,
@@ -114,7 +114,7 @@ impl VirtualDom {
     ) {
     ) {
         match *node {
         match *node {
             // Todo: create the children's template
             // Todo: create the children's template
-            TemplateNode::Dynamic(_) => mutations.push(CreatePlaceholder),
+            TemplateNode::Dynamic(_) => mutations.push(CreatePlaceholder { id: ElementId(0) }),
             TemplateNode::Text(value) => mutations.push(CreateText { value }),
             TemplateNode::Text(value) => mutations.push(CreateText { value }),
             TemplateNode::DynamicText { .. } => mutations.push(CreateText {
             TemplateNode::DynamicText { .. } => mutations.push(CreateText {
                 value: "placeholder",
                 value: "placeholder",
@@ -149,7 +149,7 @@ impl VirtualDom {
         }
         }
     }
     }
 
 
-    fn create_dynamic_node<'a>(
+    pub fn create_dynamic_node<'a>(
         &mut self,
         &mut self,
         mutations: &mut Vec<Mutation<'a>>,
         mutations: &mut Vec<Mutation<'a>>,
         template: &'a VNode<'a>,
         template: &'a VNode<'a>,
@@ -170,7 +170,7 @@ impl VirtualDom {
             }
             }
 
 
             DynamicNode::Component { props, .. } => {
             DynamicNode::Component { props, .. } => {
-                let id = self.new_scope(unsafe { std::mem::transmute(*props) });
+                let id = self.new_scope(unsafe { std::mem::transmute(props.get()) });
 
 
                 let template = self.run_scope(id);
                 let template = self.run_scope(id);
 
 
@@ -184,11 +184,14 @@ impl VirtualDom {
                 created
                 created
             }
             }
 
 
-            DynamicNode::Fragment { children } => {
-                //
-                children
-                    .iter()
-                    .fold(0, |acc, child| acc + self.create(mutations, child))
+            DynamicNode::Fragment(children) => children
+                .iter()
+                .fold(0, |acc, child| acc + self.create(mutations, child)),
+
+            DynamicNode::Placeholder(_) => {
+                let id = self.next_element(template);
+                mutations.push(CreatePlaceholder { id });
+                1
             }
             }
         }
         }
     }
     }

+ 569 - 7
packages/core/src/diff.rs

@@ -1,4 +1,7 @@
+use std::any::Any;
+
 use crate::virtualdom::VirtualDom;
 use crate::virtualdom::VirtualDom;
+use crate::{Attribute, AttributeValue, TemplateNode};
 
 
 use crate::any_props::VComponentProps;
 use crate::any_props::VComponentProps;
 
 
@@ -13,6 +16,7 @@ use crate::{
     nodes::VNode,
     nodes::VNode,
     scopes::{ScopeId, ScopeState},
     scopes::{ScopeId, ScopeState},
 };
 };
+use fxhash::{FxHashMap, FxHashSet};
 use slab::Slab;
 use slab::Slab;
 
 
 pub struct DirtyScope {
 pub struct DirtyScope {
@@ -20,16 +24,574 @@ pub struct DirtyScope {
     id: ScopeId,
     id: ScopeId,
 }
 }
 
 
-impl VirtualDom {
-    fn diff_scope<'a>(&'a mut self, mutations: &mut Vec<Mutation<'a>>, scope: ScopeId) {
+impl<'b> VirtualDom {
+    pub fn diff_scope(&mut self, mutations: &mut Vec<Mutation<'b>>, scope: ScopeId) {
         let scope_state = &mut self.scopes[scope.0];
         let scope_state = &mut self.scopes[scope.0];
     }
     }
 
 
-    fn diff_template<'a>(
-        &'a mut self,
-        mutations: &mut Vec<Mutation<'a>>,
-        left: &VNode,
-        right: &VNode,
+    pub fn diff_node(
+        &mut self,
+        muts: &mut Vec<Mutation<'b>>,
+        left_template: &'b VNode<'b>,
+        right_template: &'b VNode<'b>,
+    ) {
+        if left_template.template.id != right_template.template.id {
+            // do a light diff of the roots nodes.
+            return;
+        }
+
+        for (_idx, (left_attr, right_attr)) in left_template
+            .dynamic_attrs
+            .iter()
+            .zip(right_template.dynamic_attrs.iter())
+            .enumerate()
+        {
+            debug_assert!(left_attr.name == right_attr.name);
+            debug_assert!(left_attr.value == right_attr.value);
+
+            // Move over the ID from the old to the new
+            right_attr
+                .mounted_element
+                .set(left_attr.mounted_element.get());
+
+            if left_attr.value != right_attr.value {
+                let value = "todo!()";
+                muts.push(Mutation::SetAttribute {
+                    id: left_attr.mounted_element.get(),
+                    name: left_attr.name,
+                    value,
+                });
+            }
+        }
+
+        for (idx, (left_node, right_node)) in left_template
+            .dynamic_nodes
+            .iter()
+            .zip(right_template.dynamic_nodes.iter())
+            .enumerate()
+        {
+            #[rustfmt::skip]
+            match (left_node, right_node) {
+                (DynamicNode::Component { props: lprops, .. }, DynamicNode::Component {  is_static , props: rprops, .. }) => {
+                    let left_props = unsafe { &mut *lprops.get()};
+                    let right_props = unsafe { &mut *rprops.get()};
+
+                    // Ensure these two props are of the same component type
+                    match left_props.as_ptr() == right_props.as_ptr()  {
+                        true => {
+                            //
+
+                            if *is_static {
+                                let props_are_same = unsafe { left_props.memoize(right_props)  };
+
+                                if props_are_same{
+                                    //
+                                } else {
+                                    //
+                                }
+                            } else {
+
+                            }
+
+                        },
+                        false => todo!(),
+                    }
+                    //
+                },
+
+                // Make sure to drop the component properly
+                (DynamicNode::Component { .. }, right) => {
+                    // remove all the component roots except for the first
+                    // replace the first with the new node
+                    let m = self.create_dynamic_node(muts, right_template, right, idx);
+                    todo!()
+                },
+
+                (DynamicNode::Text { id: lid, value: lvalue }, DynamicNode::Text { id: rid, value: rvalue }) => {
+                    rid.set(lid.get());
+                    if lvalue != rvalue {
+                        muts.push(Mutation::SetText {
+                            id: lid.get(),
+                            value: rvalue,
+                        });
+                    }
+                },
+
+                (DynamicNode::Text { id: lid, .. }, right) => {
+                    let m = self.create_dynamic_node(muts, right_template, right, idx);
+                    muts.push(Mutation::Replace { id: lid.get(), m });
+                }
+
+                (DynamicNode::Placeholder(_), DynamicNode::Placeholder(_)) => todo!(),
+                (DynamicNode::Placeholder(_), _) => todo!(),
+
+
+                (DynamicNode::Fragment (l), DynamicNode::Fragment (r)) => {
+
+
+                    // match (old, new) {
+                    //     ([], []) => rp.set(lp.get()),
+                    //     ([], _) => {
+                    //         //
+                    //         todo!()
+                    //     },
+                    //     (_, []) => {
+                    //         todo!()
+                    //     },
+                    //     _ => {
+                    //         let new_is_keyed = new[0].key.is_some();
+                    //         let old_is_keyed = old[0].key.is_some();
+
+                    //         debug_assert!(
+                    //             new.iter().all(|n| n.key.is_some() == new_is_keyed),
+                    //             "all siblings must be keyed or all siblings must be non-keyed"
+                    //         );
+                    //         debug_assert!(
+                    //             old.iter().all(|o| o.key.is_some() == old_is_keyed),
+                    //             "all siblings must be keyed or all siblings must be non-keyed"
+                    //         );
+
+                    //         if new_is_keyed && old_is_keyed {
+                    //             self.diff_keyed_children(muts, old, new);
+                    //         } else {
+                    //             self.diff_non_keyed_children(muts, old, new);
+                    //         }
+                    //     }
+                    // }
+                },
+
+                // Make sure to drop all the fragment children properly
+                (DynamicNode::Fragment { .. }, right) => todo!(),
+            };
+        }
+    }
+
+    // Diff children that are not keyed.
+    //
+    // The parent must be on the top of the change list stack when entering this
+    // function:
+    //
+    //     [... parent]
+    //
+    // the change list stack is in the same state when this function returns.
+    fn diff_non_keyed_children(
+        &mut self,
+        muts: &mut Vec<Mutation<'b>>,
+        old: &'b [VNode<'b>],
+        new: &'b [VNode<'b>],
+    ) {
+        use std::cmp::Ordering;
+
+        // Handled these cases in `diff_children` before calling this function.
+        debug_assert!(!new.is_empty());
+        debug_assert!(!old.is_empty());
+
+        match old.len().cmp(&new.len()) {
+            Ordering::Greater => self.remove_nodes(muts, &old[new.len()..]),
+            Ordering::Less => todo!(),
+            // Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()),
+            Ordering::Equal => {}
+        }
+
+        for (new, old) in new.iter().zip(old.iter()) {
+            self.diff_node(muts, old, new);
+        }
+    }
+
+    // Diffing "keyed" children.
+    //
+    // With keyed children, we care about whether we delete, move, or create nodes
+    // versus mutate existing nodes in place. Presumably there is some sort of CSS
+    // transition animation that makes the virtual DOM diffing algorithm
+    // observable. By specifying keys for nodes, we know which virtual DOM nodes
+    // must reuse (or not reuse) the same physical DOM nodes.
+    //
+    // This is loosely based on Inferno's keyed patching implementation. However, we
+    // have to modify the algorithm since we are compiling the diff down into change
+    // list instructions that will be executed later, rather than applying the
+    // changes to the DOM directly as we compare virtual DOMs.
+    //
+    // https://github.com/infernojs/inferno/blob/36fd96/packages/inferno/src/DOM/patching.ts#L530-L739
+    //
+    // The stack is empty upon entry.
+    fn diff_keyed_children(
+        &mut self,
+        muts: &mut Vec<Mutation<'b>>,
+        old: &'b [VNode<'b>],
+        new: &'b [VNode<'b>],
     ) {
     ) {
+        // if cfg!(debug_assertions) {
+        //     let mut keys = fxhash::FxHashSet::default();
+        //     let mut assert_unique_keys = |children: &'b [VNode<'b>]| {
+        //         keys.clear();
+        //         for child in children {
+        //             let key = child.key;
+        //             debug_assert!(
+        //                 key.is_some(),
+        //                 "if any sibling is keyed, all siblings must be keyed"
+        //             );
+        //             keys.insert(key);
+        //         }
+        //         debug_assert_eq!(
+        //             children.len(),
+        //             keys.len(),
+        //             "keyed siblings must each have a unique key"
+        //         );
+        //     };
+        //     assert_unique_keys(old);
+        //     assert_unique_keys(new);
+        // }
+
+        // // First up, we diff all the nodes with the same key at the beginning of the
+        // // children.
+        // //
+        // // `shared_prefix_count` is the count of how many nodes at the start of
+        // // `new` and `old` share the same keys.
+        // let (left_offset, right_offset) = match self.diff_keyed_ends(muts, old, new) {
+        //     Some(count) => count,
+        //     None => return,
+        // };
+
+        // // 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.
+
+        // let old_middle = &old[left_offset..(old.len() - right_offset)];
+        // let new_middle = &new[left_offset..(new.len() - right_offset)];
+
+        // debug_assert!(
+        //     !((old_middle.len() == new_middle.len()) && old_middle.is_empty()),
+        //     "keyed children must have the same number of children"
+        // );
+
+        // if new_middle.is_empty() {
+        //     // remove the old elements
+        //     self.remove_nodes(muts, old_middle);
+        // } else if old_middle.is_empty() {
+        //     // there were no old elements, so just create the new elements
+        //     // we need to find the right "foothold" though - we shouldn't use the "append" at all
+        //     if left_offset == 0 {
+        //         // insert at the beginning of the old list
+        //         let foothold = &old[old.len() - right_offset];
+        //         self.create_and_insert_before(new_middle, foothold);
+        //     } else if right_offset == 0 {
+        //         // insert at the end  the old list
+        //         let foothold = old.last().unwrap();
+        //         self.create_and_insert_after(new_middle, foothold);
+        //     } else {
+        //         // inserting in the middle
+        //         let foothold = &old[left_offset - 1];
+        //         self.create_and_insert_after(new_middle, foothold);
+        //     }
+        // } else {
+        //     self.diff_keyed_middle(muts, old_middle, new_middle);
+        // }
+    }
+
+    // /// Diff both ends of the children that share keys.
+    // ///
+    // /// Returns a left offset and right offset of that indicates a smaller section to pass onto the middle diffing.
+    // ///
+    // /// If there is no offset, then this function returns None and the diffing is complete.
+    // fn diff_keyed_ends(
+    //     &mut self,
+    //     muts: &mut Vec<Mutation<'b>>,
+    //     old: &'b [VNode<'b>],
+    //     new: &'b [VNode<'b>],
+    // ) -> Option<(usize, usize)> {
+    //     let mut left_offset = 0;
+
+    //     for (old, new) in old.iter().zip(new.iter()) {
+    //         // abort early if we finally run into nodes with different keys
+    //         if old.key != new.key {
+    //             break;
+    //         }
+    //         self.diff_node(muts, old, new);
+    //         left_offset += 1;
+    //     }
+
+    //     // If that was all of the old children, then create and append the remaining
+    //     // new children and we're finished.
+    //     if left_offset == old.len() {
+    //         self.create_and_insert_after(&new[left_offset..], old.last().unwrap());
+    //         return None;
+    //     }
+
+    //     // And if that was all of the new children, then remove all of the remaining
+    //     // old children and we're finished.
+    //     if left_offset == new.len() {
+    //         self.remove_nodes(muts, &old[left_offset..]);
+    //         return None;
+    //     }
+
+    //     // if the shared prefix is less than either length, then we need to walk backwards
+    //     let mut right_offset = 0;
+    //     for (old, new) in old.iter().rev().zip(new.iter().rev()) {
+    //         // abort early if we finally run into nodes with different keys
+    //         if old.key != new.key {
+    //             break;
+    //         }
+    //         self.diff_node(muts, old, new);
+    //         right_offset += 1;
+    //     }
+
+    //     Some((left_offset, right_offset))
+    // }
+
+    // // The most-general, expensive code path for keyed children diffing.
+    // //
+    // // We find the longest subsequence within `old` of children that are relatively
+    // // ordered the same way in `new` (via finding a longest-increasing-subsequence
+    // // of the old child's index within `new`). The children that are elements of
+    // // this subsequence will remain in place, minimizing the number of DOM moves we
+    // // will have to do.
+    // //
+    // // Upon entry to this function, the change list stack must be empty.
+    // //
+    // // This function will load the appropriate nodes onto the stack and do diffing in place.
+    // //
+    // // Upon exit from this function, it will be restored to that same self.
+    // #[allow(clippy::too_many_lines)]
+    // fn diff_keyed_middle(
+    //     &mut self,
+    //     muts: &mut Vec<Mutation<'b>>,
+    //     old: &'b [VNode<'b>],
+    //     new: &'b [VNode<'b>],
+    // ) {
+    //     /*
+    //     1. Map the old keys into a numerical ordering based on indices.
+    //     2. Create a map of old key to its index
+    //     3. Map each new key to the old key, carrying over the old index.
+    //         - IE if we have ABCD becomes BACD, our sequence would be 1,0,2,3
+    //         - if we have ABCD to ABDE, our sequence would be 0,1,3,MAX because E doesn't exist
+
+    //     now, we should have a list of integers that indicates where in the old list the new items map to.
+
+    //     4. Compute the LIS of this list
+    //         - this indicates the longest list of new children that won't need to be moved.
+
+    //     5. Identify which nodes need to be removed
+    //     6. Identify which nodes will need to be diffed
+
+    //     7. Going along each item in the new list, create it and insert it before the next closest item in the LIS.
+    //         - if the item already existed, just move it to the right place.
+
+    //     8. Finally, generate instructions to remove any old children.
+    //     9. Generate instructions to finally diff children that are the same between both
+    //     */
+    //     // 0. Debug sanity checks
+    //     // Should have already diffed the shared-key prefixes and suffixes.
+    //     debug_assert_ne!(new.first().map(|i| i.key), old.first().map(|i| i.key));
+    //     debug_assert_ne!(new.last().map(|i| i.key), old.last().map(|i| i.key));
+
+    //     // 1. Map the old keys into a numerical ordering based on indices.
+    //     // 2. Create a map of old key to its index
+    //     // IE if the keys were A B C, then we would have (A, 1) (B, 2) (C, 3).
+    //     let old_key_to_old_index = old
+    //         .iter()
+    //         .enumerate()
+    //         .map(|(i, o)| (o.key.unwrap(), i))
+    //         .collect::<FxHashMap<_, _>>();
+
+    //     let mut shared_keys = FxHashSet::default();
+
+    //     // 3. Map each new key to the old key, carrying over the old index.
+    //     let new_index_to_old_index = new
+    //         .iter()
+    //         .map(|node| {
+    //             let key = node.key.unwrap();
+    //             if let Some(&index) = old_key_to_old_index.get(&key) {
+    //                 shared_keys.insert(key);
+    //                 index
+    //             } else {
+    //                 u32::MAX as usize
+    //             }
+    //         })
+    //         .collect::<Vec<_>>();
+
+    //     // If none of the old keys are reused by the new children, then we remove all the remaining old children and
+    //     // create the new children afresh.
+    //     if shared_keys.is_empty() {
+    //         if let Some(first_old) = old.get(0) {
+    //             self.remove_nodes(muts, &old[1..]);
+    //             let nodes_created = self.create_children(new);
+    //             self.replace_inner(first_old, nodes_created);
+    //         } else {
+    //             // I think this is wrong - why are we appending?
+    //             // only valid of the if there are no trailing elements
+    //             self.create_and_append_children(new);
+    //         }
+    //         return;
+    //     }
+
+    //     // remove any old children that are not shared
+    //     // todo: make this an iterator
+    //     for child in old {
+    //         let key = child.key.unwrap();
+    //         if !shared_keys.contains(&key) {
+    //             todo!("remove node");
+    //             // self.remove_nodes(muts, [child]);
+    //         }
+    //     }
+
+    //     // 4. Compute the LIS of this list
+    //     let mut lis_sequence = Vec::default();
+    //     lis_sequence.reserve(new_index_to_old_index.len());
+
+    //     let mut predecessors = vec![0; new_index_to_old_index.len()];
+    //     let mut starts = vec![0; new_index_to_old_index.len()];
+
+    //     longest_increasing_subsequence::lis_with(
+    //         &new_index_to_old_index,
+    //         &mut lis_sequence,
+    //         |a, b| a < b,
+    //         &mut predecessors,
+    //         &mut starts,
+    //     );
+
+    //     // the lis comes out backwards, I think. can't quite tell.
+    //     lis_sequence.sort_unstable();
+
+    //     // if a new node gets u32 max and is at the end, then it might be part of our LIS (because u32 max is a valid LIS)
+    //     if lis_sequence.last().map(|f| new_index_to_old_index[*f]) == Some(u32::MAX as usize) {
+    //         lis_sequence.pop();
+    //     }
+
+    //     for idx in &lis_sequence {
+    //         self.diff_node(muts, &old[new_index_to_old_index[*idx]], &new[*idx]);
+    //     }
+
+    //     let mut nodes_created = 0;
+
+    //     // add mount instruction for the first items not covered by the lis
+    //     let last = *lis_sequence.last().unwrap();
+    //     if last < (new.len() - 1) {
+    //         for (idx, new_node) in new[(last + 1)..].iter().enumerate() {
+    //             let new_idx = idx + last + 1;
+    //             let old_index = new_index_to_old_index[new_idx];
+    //             if old_index == u32::MAX as usize {
+    //                 nodes_created += self.create(muts, new_node);
+    //             } else {
+    //                 self.diff_node(muts, &old[old_index], new_node);
+    //                 nodes_created += self.push_all_real_nodes(new_node);
+    //             }
+    //         }
+
+    //         self.mutations.insert_after(
+    //             self.find_last_element(&new[last]).unwrap(),
+    //             nodes_created as u32,
+    //         );
+    //         nodes_created = 0;
+    //     }
+
+    //     // for each spacing, generate a mount instruction
+    //     let mut lis_iter = lis_sequence.iter().rev();
+    //     let mut last = *lis_iter.next().unwrap();
+    //     for next in lis_iter {
+    //         if last - next > 1 {
+    //             for (idx, new_node) in new[(next + 1)..last].iter().enumerate() {
+    //                 let new_idx = idx + next + 1;
+    //                 let old_index = new_index_to_old_index[new_idx];
+    //                 if old_index == u32::MAX as usize {
+    //                     nodes_created += self.create(muts, new_node);
+    //                 } else {
+    //                     self.diff_node(muts, &old[old_index], new_node);
+    //                     nodes_created += self.push_all_real_nodes(new_node);
+    //                 }
+    //             }
+
+    //             self.mutations.insert_before(
+    //                 self.find_first_element(&new[last]).unwrap(),
+    //                 nodes_created as u32,
+    //             );
+
+    //             nodes_created = 0;
+    //         }
+    //         last = *next;
+    //     }
+
+    //     // add mount instruction for the last items not covered by the lis
+    //     let first_lis = *lis_sequence.first().unwrap();
+    //     if first_lis > 0 {
+    //         for (idx, new_node) in new[..first_lis].iter().enumerate() {
+    //             let old_index = new_index_to_old_index[idx];
+    //             if old_index == u32::MAX as usize {
+    //                 nodes_created += self.create_node(new_node);
+    //             } else {
+    //                 self.diff_node(muts, &old[old_index], new_node);
+    //                 nodes_created += self.push_all_real_nodes(new_node);
+    //             }
+    //         }
+
+    //         self.mutations.insert_before(
+    //             self.find_first_element(&new[first_lis]).unwrap(),
+    //             nodes_created as u32,
+    //         );
+    //     }
+    // }
+
+    /// Remove these nodes from the dom
+    /// Wont generate mutations for the inner nodes
+    fn remove_nodes(&mut self, muts: &mut Vec<Mutation<'b>>, nodes: &'b [VNode<'b>]) {
+        //
     }
     }
 }
 }
+
+// /// Lightly diff the two templates and apply their edits to the dom
+// fn light_diff_template_roots(
+//     &'a mut self,
+//     mutations: &mut Vec<Mutation<'a>>,
+//     left: &VNode,
+//     right: &VNode,
+// ) {
+//     match right.template.roots.len().cmp(&left.template.roots.len()) {
+//         std::cmp::Ordering::Less => {
+//             // remove the old nodes at the end
+//         }
+//         std::cmp::Ordering::Greater => {
+//             // add the extra nodes.
+//         }
+//         std::cmp::Ordering::Equal => {}
+//     }
+
+//     for (left_node, right_node) in left.template.roots.iter().zip(right.template.roots.iter()) {
+//         if let (TemplateNode::Dynamic(lidx), TemplateNode::Dynamic(ridx)) =
+//             (left_node, right_node)
+//         {
+//             let left_node = &left.dynamic_nodes[*lidx];
+//             let right_node = &right.dynamic_nodes[*ridx];
+
+//             // match (left_node, right_node) {
+//             //     (
+//             //         DynamicNode::Component {
+//             //             name,
+//             //             can_memoize,
+//             //             props,
+//             //         },
+//             //         DynamicNode::Component {
+//             //             name,
+//             //             can_memoize,
+//             //             props,
+//             //         },
+//             //     ) => todo!(),
+//             //     (
+//             //         DynamicNode::Component {
+//             //             name,
+//             //             can_memoize,
+//             //             props,
+//             //         },
+//             //         DynamicNode::Fragment { children },
+//             //     ) => todo!(),
+//             //     (
+//             //         DynamicNode::Fragment { children },
+//             //         DynamicNode::Component {
+//             //             name,
+//             //             can_memoize,
+//             //             props,
+//             //         },
+//             //     ) => todo!(),
+//             //     _ => {}
+//             // }
+//         }
+//     }
+// }

+ 26 - 8
packages/core/src/factory.rs

@@ -6,7 +6,6 @@ use futures_util::Future;
 use crate::{
 use crate::{
     any_props::{AnyProps, VComponentProps},
     any_props::{AnyProps, VComponentProps},
     arena::ElementId,
     arena::ElementId,
-    component::IntoComponent,
     innerlude::DynamicNode,
     innerlude::DynamicNode,
     Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
     Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode,
 };
 };
@@ -47,8 +46,9 @@ impl ScopeState {
             bump_vec.push(item.into_dynamic_node(self));
             bump_vec.push(item.into_dynamic_node(self));
         }
         }
 
 
-        DynamicNode::Fragment {
-            children: bump_vec.into_bump_slice(),
+        match bump_vec.len() {
+            0 => DynamicNode::Placeholder(Cell::new(ElementId(0))),
+            _ => DynamicNode::Fragment(bump_vec.into_bump_slice()),
         }
         }
     }
     }
 
 
@@ -70,9 +70,9 @@ impl ScopeState {
     }
     }
 
 
     /// Create a new [`VNode::Component`]
     /// Create a new [`VNode::Component`]
-    pub fn component<'a, P, F: Future<Output = Element<'a>>>(
+    pub fn component<'a, P, A, F: ReturnType<'a, A>>(
         &'a self,
         &'a self,
-        component: impl IntoComponent<'a, P, F>,
+        component: fn(Scope<'a, P>) -> F,
         props: P,
         props: P,
         fn_name: &'static str,
         fn_name: &'static str,
     ) -> DynamicNode<'a>
     ) -> DynamicNode<'a>
@@ -80,7 +80,7 @@ impl ScopeState {
         P: Properties + 'a,
         P: Properties + 'a,
     {
     {
         let props = self.bump().alloc(props);
         let props = self.bump().alloc(props);
-        let as_component = component.into_component();
+        let as_component = component;
         let vcomp = VComponentProps::new(as_component, P::memoize, props);
         let vcomp = VComponentProps::new(as_component, P::memoize, props);
         let as_dyn = self.bump().alloc(vcomp) as &mut dyn AnyProps;
         let as_dyn = self.bump().alloc(vcomp) as &mut dyn AnyProps;
         let detached_dyn: *mut dyn AnyProps = unsafe { std::mem::transmute(as_dyn) };
         let detached_dyn: *mut dyn AnyProps = unsafe { std::mem::transmute(as_dyn) };
@@ -94,12 +94,30 @@ impl ScopeState {
 
 
         DynamicNode::Component {
         DynamicNode::Component {
             name: fn_name,
             name: fn_name,
-            can_memoize: P::IS_STATIC,
-            props: detached_dyn,
+            is_static: P::IS_STATIC,
+            props: Cell::new(detached_dyn),
         }
         }
     }
     }
 }
 }
 
 
+pub trait ReturnType<'a, A = ()> {}
+impl<'a> ReturnType<'a> for Element<'a> {}
+
+pub struct AsyncMarker;
+impl<'a, F> ReturnType<'a, AsyncMarker> for F where F: Future<Output = Element<'a>> + 'a {}
+
+#[test]
+fn takes_it() {
+    fn demo(cx: Scope) -> Element {
+        todo!()
+    }
+}
+
+enum RenderReturn<'a> {
+    Sync(Element<'a>),
+    Async(*mut dyn Future<Output = Element<'a>>),
+}
+
 pub trait IntoVnode<'a, A = ()> {
 pub trait IntoVnode<'a, A = ()> {
     fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a>;
     fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a>;
 }
 }

+ 2 - 1
packages/core/src/garbage.rs

@@ -19,7 +19,8 @@ impl VirtualDom {
                     todo!()
                     todo!()
                 }
                 }
 
 
-                DynamicNode::Fragment { children } => {}
+                DynamicNode::Fragment(children) => {}
+                DynamicNode::Placeholder(_) => todo!(),
             }
             }
         }
         }
     }
     }

+ 4 - 1
packages/core/src/mutations.rs

@@ -46,6 +46,7 @@ pub enum Mutation<'a> {
     // Take the current element and replace it with the element with the given id.
     // Take the current element and replace it with the element with the given id.
     Replace {
     Replace {
         id: ElementId,
         id: ElementId,
+        m: usize,
     },
     },
 
 
     CreateElement {
     CreateElement {
@@ -58,7 +59,9 @@ pub enum Mutation<'a> {
         value: &'a str,
         value: &'a str,
     },
     },
 
 
-    CreatePlaceholder,
+    CreatePlaceholder {
+        id: ElementId,
+    },
 
 
     AppendChildren {
     AppendChildren {
         m: usize,
         m: usize,

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

@@ -1,6 +1,6 @@
 use crate::{any_props::AnyProps, arena::ElementId};
 use crate::{any_props::AnyProps, arena::ElementId};
 use std::{
 use std::{
-    any::Any,
+    any::{Any, TypeId},
     cell::{Cell, RefCell},
     cell::{Cell, RefCell},
     hash::Hasher,
     hash::Hasher,
 };
 };
@@ -77,8 +77,8 @@ pub enum DynamicNode<'a> {
     // IE in caps or with underscores
     // IE in caps or with underscores
     Component {
     Component {
         name: &'static str,
         name: &'static str,
-        can_memoize: bool,
-        props: *mut dyn AnyProps<'a>,
+        is_static: bool,
+        props: Cell<*mut dyn AnyProps<'a>>,
     },
     },
 
 
     // Comes in with string interpolation or from format_args, include_str, etc
     // Comes in with string interpolation or from format_args, include_str, etc
@@ -88,9 +88,9 @@ pub enum DynamicNode<'a> {
     },
     },
 
 
     // Anything that's coming in as an iterator
     // Anything that's coming in as an iterator
-    Fragment {
-        children: &'a [VNode<'a>],
-    },
+    Fragment(&'a [VNode<'a>]),
+
+    Placeholder(Cell<ElementId>),
 }
 }
 
 
 #[derive(Debug)]
 #[derive(Debug)]
@@ -122,26 +122,71 @@ pub enum AttributeValue<'a> {
     None,
     None,
 }
 }
 
 
+impl<'a> std::fmt::Debug for AttributeValue<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Text(arg0) => f.debug_tuple("Text").field(arg0).finish(),
+            Self::Float(arg0) => f.debug_tuple("Float").field(arg0).finish(),
+            Self::Int(arg0) => f.debug_tuple("Int").field(arg0).finish(),
+            Self::Bool(arg0) => f.debug_tuple("Bool").field(arg0).finish(),
+            Self::Listener(arg0) => f.debug_tuple("Listener").finish(),
+            Self::Any(arg0) => f.debug_tuple("Any").finish(),
+            Self::None => write!(f, "None"),
+        }
+    }
+}
+
+impl<'a> PartialEq for AttributeValue<'a> {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (Self::Text(l0), Self::Text(r0)) => l0 == r0,
+            (Self::Float(l0), Self::Float(r0)) => l0 == r0,
+            (Self::Int(l0), Self::Int(r0)) => l0 == r0,
+            (Self::Bool(l0), Self::Bool(r0)) => l0 == r0,
+            (Self::Listener(l0), Self::Listener(r0)) => true,
+            (Self::Any(l0), Self::Any(r0)) => l0.any_cmp(*r0),
+            _ => core::mem::discriminant(self) == core::mem::discriminant(other),
+        }
+    }
+}
+
 impl<'a> AttributeValue<'a> {
 impl<'a> AttributeValue<'a> {
+    pub fn matches_type(&self, other: &'a AttributeValue<'a>) -> bool {
+        match (self, other) {
+            (Self::Text(_), Self::Text(_)) => true,
+            (Self::Float(_), Self::Float(_)) => true,
+            (Self::Int(_), Self::Int(_)) => true,
+            (Self::Bool(_), Self::Bool(_)) => true,
+            (Self::Listener(_), Self::Listener(_)) => true,
+            (Self::Any(_), Self::Any(_)) => true,
+            _ => return false,
+        }
+    }
+
     fn is_listener(&self) -> bool {
     fn is_listener(&self) -> bool {
         matches!(self, AttributeValue::Listener(_))
         matches!(self, AttributeValue::Listener(_))
     }
     }
 }
 }
 
 
 pub trait AnyValue {
 pub trait AnyValue {
-    fn any_cmp(&self, other: &dyn Any) -> bool;
+    fn any_cmp(&self, other: &dyn AnyValue) -> bool;
+    fn our_typeid(&self) -> TypeId;
 }
 }
 impl<T> AnyValue for T
 impl<T> AnyValue for T
 where
 where
     T: PartialEq + Any,
     T: PartialEq + Any,
 {
 {
-    fn any_cmp(&self, other: &dyn Any) -> bool {
-        if self.type_id() != other.type_id() {
+    fn any_cmp(&self, other: &dyn AnyValue) -> bool {
+        if self.type_id() != other.our_typeid() {
             return false;
             return false;
         }
         }
 
 
         self == unsafe { &*(other as *const _ as *const T) }
         self == unsafe { &*(other as *const _ as *const T) }
     }
     }
+
+    fn our_typeid(&self) -> TypeId {
+        self.type_id()
+    }
 }
 }
 
 
 #[test]
 #[test]

+ 1 - 1
packages/core/src/properties.rs

@@ -165,6 +165,6 @@ impl EmptyBuilder {
 
 
 /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
 /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
 /// to initialize a component's props.
 /// to initialize a component's props.
-pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
+pub fn fc_to_builder<'a, A, T: Properties + 'a>(_: fn(Scope<'a, T>) -> A) -> T::Builder {
     T::builder()
     T::builder()
 }
 }

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

@@ -10,6 +10,7 @@ use crate::{
     arena::ElementId,
     arena::ElementId,
     scopes::{ScopeId, ScopeState},
     scopes::{ScopeId, ScopeState},
 };
 };
+use crate::{Element, Scope};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
 use slab::Slab;
 use slab::Slab;
 use std::collections::{BTreeSet, HashMap};
 use std::collections::{BTreeSet, HashMap};
@@ -27,7 +28,7 @@ pub struct VirtualDom {
 }
 }
 
 
 impl VirtualDom {
 impl VirtualDom {
-    pub fn new<'a>(app: Component<'a, ()>) -> Self {
+    pub fn new(app: fn(Scope) -> Element) -> Self {
         let (sender, receiver) = futures_channel::mpsc::unbounded();
         let (sender, receiver) = futures_channel::mpsc::unbounded();
 
 
         let mut res = Self {
         let mut res = Self {
@@ -43,7 +44,7 @@ impl VirtualDom {
         };
         };
 
 
         let props = Box::into_raw(Box::new(VComponentProps::new_empty(app)));
         let props = Box::into_raw(Box::new(VComponentProps::new_empty(app)));
-        let props: *mut VComponentProps<()> = unsafe { std::mem::transmute(props) };
+        let props: *mut VComponentProps<(), ()> = unsafe { std::mem::transmute(props) };
 
 
         let root = res.new_scope(props);
         let root = res.new_scope(props);
 
 

+ 13 - 16
packages/dioxus/tests/rsx_syntax.rs

@@ -24,28 +24,20 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element {
 }
 }
 
 
 fn basic_template(cx: Scope) -> Element {
 fn basic_template(cx: Scope) -> Element {
-    let val = 123;
-
-    cx.component(basic_child, (), "fn_name");
-
-    todo!()
-    // cx.render(rsx! {
-    // div { class: "{val}", class: "{val}", class: "{val}", class: "{val}",
-    // (0..2).map(|i| rsx! { div { "asd {i}" } })
-    // basic_child { }
-    // }
-    // })
+    cx.render(rsx! {
+        div {
+            basic_child { }
+            async_child { }
+        }
+    })
 }
 }
 
 
-/// A beautiful component
 fn basic_child(cx: Scope) -> Element {
 fn basic_child(cx: Scope) -> Element {
     todo!()
     todo!()
 }
 }
 
 
-async fn async_component(cx: Scope<'_>) -> Element {
-    cx.render(rsx! {
-        div { class: "asd" }
-    })
+async fn async_child(cx: Scope<'_>) -> Element {
+    todo!()
 }
 }
 
 
 #[test]
 #[test]
@@ -63,4 +55,9 @@ fn basic_prints() {
     // let renderer = dioxus_edit_stream::Mutations::default();
     // let renderer = dioxus_edit_stream::Mutations::default();
     //
     //
     // dbg!(renderer.edits);
     // dbg!(renderer.edits);
+
+    // takes_it(basic_child);
 }
 }
+
+// fn takes_it(f: fn(Scope) -> Element) {}
+// fn takes_it(f: fn(Scope) -> Element) {}

+ 0 - 9
packages/edit-stream/Cargo.toml

@@ -1,9 +0,0 @@
-[package]
-name = "dioxus-edit-stream"
-version = "0.0.0"
-edition = "2018"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-dioxus-core = { path = "../core" }

+ 0 - 358
packages/edit-stream/src/lib.rs

@@ -1,358 +0,0 @@
-use dioxus_core::*;
-
-/// ## Mutations
-///
-/// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
-/// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
-/// applied the edits.
-///
-/// Mutations are the only link between the RealDOM and the VirtualDOM.
-#[derive(Default)]
-pub struct Mutations<'a> {
-    /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
-    pub edits: Vec<DomEdit<'a>>,
-
-    /// The list of Scopes that were diffed, created, and removed during the Diff process.
-    pub dirty_scopes: Vec<ScopeId>,
-}
-
-/// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
-/// network or through FFI boundaries.
-#[derive(Debug, PartialEq)]
-#[cfg_attr(
-    feature = "serialize",
-    derive(serde::Serialize, serde::Deserialize),
-    serde(tag = "type")
-)]
-pub enum DomEdit<'bump> {
-    /// Push the given root node onto our stack.
-    PushRoot {
-        /// The ID of the root node to push.
-        root: ElementId,
-    },
-
-    /// Pop the topmost node from our stack and append them to the node
-    /// at the top of the stack.
-    AppendChildren {
-        /// How many nodes should be popped from the stack.
-        /// The node remaining on the stack will be the target for the append.
-        many: u32,
-    },
-
-    /// Replace a given (single) node with a handful of nodes currently on the stack.
-    ReplaceWith {
-        /// The ID of the node to be replaced.
-        root: ElementId,
-
-        /// How many nodes should be popped from the stack to replace the target node.
-        m: u32,
-    },
-
-    /// Insert a number of nodes after a given node.
-    InsertAfter {
-        /// The ID of the node to insert after.
-        root: ElementId,
-
-        /// How many nodes should be popped from the stack to insert after the target node.
-        n: u32,
-    },
-
-    /// Insert a number of nodes before a given node.
-    InsertBefore {
-        /// The ID of the node to insert before.
-        root: ElementId,
-
-        /// How many nodes should be popped from the stack to insert before the target node.
-        n: u32,
-    },
-
-    /// Remove a particular node from the DOM
-    Remove {
-        /// The ID of the node to remove.
-        root: ElementId,
-    },
-
-    /// Create a new purely-text node
-    CreateTextNode {
-        /// The ID the new node should have.
-        root: ElementId,
-
-        /// The textcontent of the node
-        text: &'bump str,
-    },
-
-    /// Create a new purely-element node
-    CreateElement {
-        /// The ID the new node should have.
-        root: ElementId,
-
-        /// The tagname of the node
-        tag: &'bump str,
-    },
-
-    /// Create a new purely-comment node with a given namespace
-    CreateElementNs {
-        /// The ID the new node should have.
-        root: ElementId,
-
-        /// The namespace of the node
-        tag: &'bump str,
-
-        /// The namespace of the node (like `SVG`)
-        ns: &'static str,
-    },
-
-    /// Create a new placeholder node.
-    /// In most implementations, this will either be a hidden div or a comment node.
-    CreatePlaceholder {
-        /// The ID the new node should have.
-        root: ElementId,
-    },
-
-    /// Create a new Event Listener.
-    NewEventListener {
-        /// The name of the event to listen for.
-        event_name: &'static str,
-
-        /// The ID of the node to attach the listener to.
-        scope: ScopeId,
-
-        /// The ID of the node to attach the listener to.
-        root: ElementId,
-    },
-
-    /// Remove an existing Event Listener.
-    RemoveEventListener {
-        /// The ID of the node to remove.
-        root: ElementId,
-
-        /// The name of the event to remove.
-        event: &'static str,
-    },
-
-    /// Set the textcontent of a node.
-    SetText {
-        /// The ID of the node to set the textcontent of.
-        root: ElementId,
-
-        /// The textcontent of the node
-        text: &'bump str,
-    },
-
-    /// Set the value of a node's attribute.
-    SetAttribute {
-        /// The ID of the node to set the attribute of.
-        root: ElementId,
-
-        /// The name of the attribute to set.
-        field: &'static str,
-
-        /// The value of the attribute.
-        value: AttributeValue<'bump>,
-
-        // value: &'bump str,
-        /// The (optional) namespace of the attribute.
-        /// For instance, "style" is in the "style" namespace.
-        ns: Option<&'bump str>,
-    },
-
-    /// Remove an attribute from a node.
-    RemoveAttribute {
-        /// The ID of the node to remove.
-        root: ElementId,
-
-        /// The name of the attribute to remove.
-        name: &'static str,
-
-        /// The namespace of the attribute.
-        ns: Option<&'bump str>,
-    },
-
-    /// Manually pop a root node from the stack.
-    PopRoot {
-        /// The amount of nodes to pop
-        count: u32,
-    },
-
-    /// Remove all the children of an element
-    RemoveChildren {
-        /// The root
-        root: ElementId,
-    },
-
-    /*
-
-    Template stuff
-
-    - load into scratch space
-    - dump nodes into stack
-    - assign ids of nodes in template
-
-    */
-    /// Create a template using the nodes on the stack
-    Save {
-        /// The ID of the template
-        name: &'static str,
-
-        /// The amount of nodes to pop from the stack into the template
-        num_children: u32,
-    },
-
-    /// Load the template into a scratch space on the stack
-    ///
-    /// The template body now lives on the stack, but needs to be finished before its nodes can be appended to the DOM.
-    Load {
-        /// The ID of the template
-        name: &'static str,
-
-        id: u32,
-    },
-
-    AssignId {
-        index: &'static str,
-        id: ElementId,
-    },
-
-    ReplaceDescendant {
-        index: &'static str,
-        m: u32,
-    },
-}
-
-use DomEdit::*;
-
-impl<'a> dioxus_core::Renderer<'a> for Mutations<'a> {
-    // Navigation
-    fn push_root(&mut self, root: ElementId) {
-        self.edits.push(PushRoot { root });
-    }
-
-    // Navigation
-    fn pop_root(&mut self) {
-        self.edits.push(PopRoot { count: 1 });
-    }
-
-    fn replace_with(&mut self, root: ElementId, m: u32) {
-        self.edits.push(ReplaceWith { m, root });
-    }
-
-    fn replace_descendant(&mut self, descendent: &'static [u8], m: u32) {
-        self.edits.push(ReplaceDescendant {
-            // serializing is just hijacking ascii
-            index: unsafe { std::str::from_utf8_unchecked(descendent) },
-            m,
-        });
-    }
-
-    fn insert_after(&mut self, root: ElementId, n: u32) {
-        self.edits.push(InsertAfter { n, root });
-    }
-
-    fn insert_before(&mut self, root: ElementId, n: u32) {
-        self.edits.push(InsertBefore { n, root });
-    }
-
-    fn append_children(&mut self, n: u32) {
-        self.edits.push(AppendChildren { many: n });
-    }
-
-    // Create
-    fn create_text_node(&mut self, text: &'a str, root: ElementId) {
-        self.edits.push(CreateTextNode { text, root });
-    }
-
-    fn create_element(&mut self, tag: &'static str, ns: Option<&'static str>, id: ElementId) {
-        match ns {
-            Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
-            None => self.edits.push(CreateElement { root: id, tag }),
-        }
-    }
-
-    // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
-    fn create_placeholder(&mut self, id: ElementId) {
-        self.edits.push(CreatePlaceholder { root: id });
-    }
-
-    fn assign_id(&mut self, descendent: &'static [u8], id: ElementId) {
-        self.edits.push(AssignId {
-            index: unsafe { std::str::from_utf8_unchecked(descendent) },
-            id,
-        });
-    }
-
-    // Remove Nodes from the dom
-    fn remove(&mut self, root: ElementId) {
-        self.edits.push(Remove { root });
-    }
-
-    fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId) {
-        self.edits.push(RemoveAttribute {
-            name: attribute.name,
-            ns: attribute.namespace,
-            root,
-        });
-    }
-
-    // events
-    fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
-        let Listener {
-            event,
-            mounted_node,
-            ..
-        } = listener;
-
-        let element_id = mounted_node.get();
-
-        self.edits.push(NewEventListener {
-            scope,
-            event_name: event,
-            root: element_id,
-        });
-    }
-
-    fn remove_event_listener(&mut self, event: &'static str, root: ElementId) {
-        self.edits.push(RemoveEventListener { event, root });
-    }
-
-    // modify
-    fn set_text(&mut self, text: &'a str, root: ElementId) {
-        self.edits.push(SetText { text, root });
-    }
-
-    fn save(&mut self, id: &'static str, num: u32) {
-        self.edits.push(Save {
-            name: id,
-            num_children: num,
-        });
-    }
-
-    fn load(&mut self, id: &'static str, index: u32) {
-        self.edits.push(Load {
-            name: id,
-            id: index,
-        });
-    }
-
-    fn mark_dirty_scope(&mut self, scope: ScopeId) {
-        self.dirty_scopes.push(scope);
-    }
-
-    fn set_attribute(
-        &mut self,
-        name: &'static str,
-        value: AttributeValue<'a>,
-        namespace: Option<&'a str>,
-        root: ElementId,
-    ) {
-        self.edits.push(SetAttribute {
-            field: name,
-            value: value.clone(),
-            ns: namespace,
-            root,
-        });
-    }
-
-    fn remove_children(&mut self, element: ElementId) {
-        todo!()
-    }
-}