Răsfoiți Sursa

wip: staticify?

Jonathan Kelley 4 ani în urmă
părinte
comite
5ad81885e4

+ 1 - 1
Cargo.toml

@@ -4,7 +4,7 @@ members = [
     "packages/dioxus",
     "packages/core-macro",
     "packages/core",
-    # "packages/web",
+    "packages/web",
     # "packages/router",
     # "packages/ssr",
     # "packages/webview",

+ 1 - 1
packages/core-macro/Cargo.toml

@@ -16,7 +16,7 @@ once_cell = "1.7.2"
 proc-macro-hack = "0.5.19"
 proc-macro2 = "1.0.6"
 quote = "1.0"
-syn = { version = "1.0.11", features = ["full"] }
+syn = { version = "1.0.11", features = ["full",  "extra-traits"] }
 
 # testing
 [dev-dependencies]

+ 0 - 1
packages/core-macro/src/props/mod.rs

@@ -11,7 +11,6 @@ use proc_macro2::TokenStream;
 use syn::parse::Error;
 use syn::spanned::Spanned;
 
-
 use quote::quote;
 
 pub fn impl_my_derive(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {

+ 2 - 10
packages/core/Cargo.toml

@@ -10,10 +10,8 @@ description = "Core functionality for Dioxus - a concurrent renderer-agnostic Vi
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-# dodrio-derive = { path = "../html-macro-2", version = "0.1.0" }
-# dioxus-html-macro = { path = "../html-macro", version = "0.1.0" }
-# dioxus-html-2 = { path = "../html-macro-2", version = "0.1.0" }
 dioxus-core-macro = { path = "../core-macro", version = "0.1.1" }
+
 # Backs some static data
 once_cell = "1.5.2"
 
@@ -22,18 +20,12 @@ generational-arena = { version = "0.2.8", features = ["serde"] }
 # Bumpalo backs the VNode creation
 bumpalo = { version = "3.6.0", features = ["collections"] }
 
-owning_ref = "0.4.1"
-
 # all the arenas 👿
 typed-arena = "2.0.1"
-toolshed = "0.8.1"
 id-arena = "2.2.1"
 thiserror = "1.0.23"
 fxhash = "0.2.1"
 longest-increasing-subsequence = "0.1.0"
 serde = { version = "1.0.123", features = ["derive"] }
 log = "0.4.14"
-pretty_env_logger = "0.4.0"
-fstrings = "0.2.3"
-# ouroboros = "0.8.0"
-# hashbrown = { version = "0.9.1", features = ["bumpalo"] }
+uuid = {version = "0.8.2", features=["serde", "v4"]}

+ 5 - 2
packages/core/examples/borrowed.rs

@@ -7,8 +7,6 @@
 
 fn main() {}
 
-
-
 use dioxus_core::prelude::*;
 
 struct Props {
@@ -73,9 +71,14 @@ impl PartialEq for ChildProps<'_> {
 
 impl<'a> Properties for ChildProps<'a> {
     type Builder = ChildPropsBuilder<'a, ((), ())>;
+    type StaticOutput = ChildProps<'static>;
     fn builder() -> <Self as Properties>::Builder {
         ChildProps::builder()
     }
+
+    unsafe fn into_static(self) -> Self::StaticOutput {
+        unsafe { std::mem::transmute(self) }
+    }
 }
 
 fn ChildItem(_ctx: Context, _props: &ChildProps) -> DomTree {

+ 5 - 3
packages/core/examples/fc.rs

@@ -1,9 +1,6 @@
 use dioxus_core::component::fc_to_builder;
 use dioxus_core::prelude::*;
 
-
-
-
 static BLAH: FC<()> = |ctx, _props| {
     let g = "asd".to_string();
     ctx.render(rsx! {
@@ -30,7 +27,12 @@ fn main() {}
 
 impl Properties for ExampleProps {
     type Builder = ExamplePropsBuilder<((),)>;
+    type StaticOutput = ExampleProps;
     fn builder() -> Self::Builder {
         ExampleProps::builder()
     }
+
+    unsafe fn into_static(self) -> Self {
+        self
+    }
 }

+ 0 - 4
packages/core/examples/fmter.rs

@@ -1,12 +1,8 @@
 // #[macro_use]
-extern crate fstrings;
-
-
 
 // use dioxus_core::ifmt;
 // use fstrings::format_args_f;
 
-
 fn main() {
     let bump = bumpalo::Bump::new();
     let _b = &bump;

+ 5 - 0
packages/core/examples/step.rs

@@ -27,7 +27,12 @@ static Example: FC<SomeProps> = |ctx, _props| {
 // toodo: derive this
 impl Properties for SomeProps {
     type Builder = SomePropsBuilder<((),)>;
+    type StaticOutput = SomeProps;
     fn builder() -> Self::Builder {
         SomeProps::builder()
     }
+
+    unsafe fn into_static(self) -> Self {
+        self
+    }
 }

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

@@ -11,7 +11,9 @@ pub type ScopeIdx = generational_arena::Index;
 
 pub trait Properties: PartialEq {
     type Builder;
+    type StaticOutput: Properties + 'static;
     fn builder() -> Self::Builder;
+    unsafe fn into_static(self) -> Self::StaticOutput;
 }
 
 pub struct EmptyBuilder;
@@ -23,10 +25,15 @@ impl EmptyBuilder {
 
 impl Properties for () {
     type Builder = EmptyBuilder;
+    type StaticOutput = ();
 
     fn builder() -> Self::Builder {
         EmptyBuilder {}
     }
+
+    unsafe fn into_static(self) -> Self::StaticOutput {
+        std::mem::transmute(self)
+    }
 }
 
 pub fn fc_to_builder<T: Properties>(_f: FC<T>) -> T::Builder {

+ 136 - 102
packages/core/src/diff.rs

@@ -32,13 +32,18 @@
 //!
 //! More info on how to improve this diffing algorithm:
 //!  - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/
-use crate::{
-    innerlude::*,
-};
+use crate::{innerlude::*, scope::Scoped};
 use bumpalo::Bump;
 use fxhash::{FxHashMap, FxHashSet};
-
-use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
+use generational_arena::Arena;
+use uuid::Uuid;
+
+use std::{
+    cell::{RefCell, RefMut},
+    cmp::Ordering,
+    collections::VecDeque,
+    rc::Rc,
+};
 
 /// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
 /// diffing trees of components. This means we can "re-enter" a subtree of a component by queuing a "NeedToDiff" event.
@@ -50,20 +55,29 @@ use std::{cell::RefCell, cmp::Ordering, collections::VecDeque, rc::Rc};
 /// different cursor position.
 ///
 /// The order of these re-entrances is stored in the DiffState itself. The DiffState comes pre-loaded with a set of components
-/// that were modified by the eventtrigger. This prevents doubly evaluating components if they wereboth updated via
+/// that were modified by the eventtrigger. This prevents doubly evaluating components if they were both updated via
 /// subscriptions and props changes.
 pub struct DiffMachine<'a> {
     pub change_list: EditMachine<'a>,
     pub diffed: FxHashSet<ScopeIdx>,
-    pub need_to_diff: FxHashSet<ScopeIdx>,
+    pub lifecycle_events: VecDeque<LifeCycleEvent>,
+}
+
+#[derive(Debug)]
+pub enum LifeCycleEvent {
+    Mount { caller: Caller, id: Uuid },
+    PropsChanged,
+    SameProps,
+    Remove,
 }
 
 impl<'a> DiffMachine<'a> {
-    pub fn new(bump: &'a Bump) -> Self {
+    pub fn new() -> Self {
+        // pub fn new(bump: &'a Bump) -> Self {
         Self {
-            change_list: EditMachine::new(bump),
+            lifecycle_events: VecDeque::new(),
+            change_list: EditMachine::new(),
             diffed: FxHashSet::default(),
-            need_to_diff: FxHashSet::default(),
         }
     }
 
@@ -111,18 +125,31 @@ impl<'a> DiffMachine<'a> {
                 self.diff_children(eold.children, enew.children);
             }
 
-            (VNode::Component(_cold), VNode::Component(_cnew)) => {
-                // if cold.comp != cnew.comp {
-                //     // queue an event to mount this new component
+            (VNode::Component(cold), VNode::Component(cnew)) => {
+                todo!("should not happen")
+                // if cold.caller_ref != cnew.caller_ref {
+                //     // todo: create a stable addr
+                //     self.lifecycle_events.push_back(LifeCycleEvent::Mount);
                 //     return;
                 // }
 
-                // compare props.... somehow.....
+                // let comparator = &cnew.comparator.0;
+                // let old_props = cold.raw_props.as_ref();
+                // let has_changed = comparator(old_props);
 
-                todo!("Usage of component VNode not currently supported");
+                // if has_changed {
+                //     self.lifecycle_events
+                //         .push_back(LifeCycleEvent::PropsChanged);
+                //     return;
+                // }
+
+                // the component is the same and hasn't changed
+                // migrate props over (so the addr remains stable) but don't rerun the component
             }
 
+            // todo: knock out any listeners
             (_, VNode::Component(_new)) => {
+                // self.lifecycle_events.push_back(LifeCycleEvent::Mount);
                 // we have no stable reference to work from
                 // push the lifecycle event onto the queue
                 // self.lifecycle_events
@@ -151,6 +178,100 @@ impl<'a> DiffMachine<'a> {
         }
     }
 
+    // Emit instructions to create the given virtual node.
+    //
+    // The change list stack may have any shape upon entering this function:
+    //
+    //     [...]
+    //
+    // When this function returns, the new node is on top of the change list stack:
+    //
+    //     [... node]
+    fn create(&mut self, node: &VNode<'a>) {
+        debug_assert!(self.change_list.traversal_is_committed());
+        match node {
+            VNode::Text(VText { text }) => {
+                self.change_list.create_text_node(text);
+            }
+            VNode::Element(&VElement {
+                key: _,
+                tag_name,
+                listeners,
+                attributes,
+                children,
+                namespace,
+            }) => {
+                // log::info!("Creating {:#?}", node);
+                if let Some(namespace) = namespace {
+                    self.change_list.create_element_ns(tag_name, namespace);
+                } else {
+                    self.change_list.create_element(tag_name);
+                }
+
+                listeners.iter().enumerate().for_each(|(_id, listener)| {
+                    self.change_list
+                        .new_event_listener(listener.event, listener.scope, listener.id)
+                });
+
+                for attr in attributes {
+                    self.change_list
+                        .set_attribute(&attr.name, &attr.value, namespace.is_some());
+                }
+
+                // Fast path: if there is a single text child, it is faster to
+                // create-and-append the text node all at once via setting the
+                // parent's `textContent` in a single change list instruction than
+                // to emit three instructions to (1) create a text node, (2) set its
+                // text content, and finally (3) append the text node to this
+                // parent.
+                if children.len() == 1 {
+                    if let VNode::Text(VText { text }) = children[0] {
+                        self.change_list.set_text(text);
+                        return;
+                    }
+                }
+
+                for child in children {
+                    self.create(child);
+                    self.change_list.append_child();
+                }
+            }
+
+            /*
+            todo: integrate re-entrace
+            */
+            // NodeKind::Cached(ref c) => {
+            //     cached_roots.insert(c.id);
+            //     let (node, template) = cached_set.get(c.id);
+            //     if let Some(template) = template {
+            //         create_with_template(
+            //             cached_set,
+            //             self.change_list,
+            //             registry,
+            //             template,
+            //             node,
+            //             cached_roots,
+            //         );
+            //     } else {
+            //         create(cached_set, change_list, registry, node, cached_roots);
+            //     }
+            // }
+            VNode::Component(component) => {
+                self.change_list
+                    .create_text_node("placeholder for vcomponent");
+                let id = uuid::Uuid::new_v4();
+                self.change_list.save_known_root(id);
+                self.lifecycle_events.push_back(LifeCycleEvent::Mount {
+                    caller: component.caller.clone(),
+                    id,
+                });
+            }
+            VNode::Suspended => {
+                todo!("Creation of VNode::Suspended not yet supported")
+            }
+        }
+    }
+
     // Diff event listeners between `old` and `new`.
     //
     // The listeners' node must be on top of the change list stack:
@@ -787,93 +908,6 @@ impl<'a> DiffMachine<'a> {
     // Support methods
     // ======================
 
-    // Emit instructions to create the given virtual node.
-    //
-    // The change list stack may have any shape upon entering this function:
-    //
-    //     [...]
-    //
-    // When this function returns, the new node is on top of the change list stack:
-    //
-    //     [... node]
-    fn create(&mut self, node: &VNode<'a>) {
-        debug_assert!(self.change_list.traversal_is_committed());
-        match node {
-            VNode::Text(VText { text }) => {
-                self.change_list.create_text_node(text);
-            }
-            VNode::Element(&VElement {
-                key: _,
-                tag_name,
-                listeners,
-                attributes,
-                children,
-                namespace,
-            }) => {
-                // log::info!("Creating {:#?}", node);
-                if let Some(namespace) = namespace {
-                    self.change_list.create_element_ns(tag_name, namespace);
-                } else {
-                    self.change_list.create_element(tag_name);
-                }
-
-                listeners.iter().enumerate().for_each(|(_id, listener)| {
-                    self.change_list
-                        .new_event_listener(listener.event, listener.scope, listener.id)
-                });
-
-                for attr in attributes {
-                    self.change_list
-                        .set_attribute(&attr.name, &attr.value, namespace.is_some());
-                }
-
-                // Fast path: if there is a single text child, it is faster to
-                // create-and-append the text node all at once via setting the
-                // parent's `textContent` in a single change list instruction than
-                // to emit three instructions to (1) create a text node, (2) set its
-                // text content, and finally (3) append the text node to this
-                // parent.
-                if children.len() == 1 {
-                    if let VNode::Text(VText { text }) = children[0] {
-                        self.change_list.set_text(text);
-                        return;
-                    }
-                }
-
-                for child in children {
-                    self.create(child);
-                    self.change_list.append_child();
-                }
-            }
-
-            /*
-            todo: integrate re-entrace
-            */
-            // NodeKind::Cached(ref c) => {
-            //     cached_roots.insert(c.id);
-            //     let (node, template) = cached_set.get(c.id);
-            //     if let Some(template) = template {
-            //         create_with_template(
-            //             cached_set,
-            //             self.change_list,
-            //             registry,
-            //             template,
-            //             node,
-            //             cached_roots,
-            //         );
-            //     } else {
-            //         create(cached_set, change_list, registry, node, cached_roots);
-            //     }
-            // }
-            VNode::Suspended => {
-                todo!("Creation of VNode::Suspended not yet supported")
-            }
-            VNode::Component(_) => {
-                todo!("Creation of VNode::Component not yet supported")
-            }
-        }
-    }
-
     // Remove all of a node's children.
     //
     // The change list stack must have this shape upon entry to this function:

+ 23 - 3
packages/core/src/hooks.rs

@@ -10,7 +10,11 @@ pub use use_state_def::use_state;
 
 mod use_state_def {
     use crate::innerlude::*;
-    use std::{cell::RefCell, ops::DerefMut, rc::Rc};
+    use std::{
+        cell::RefCell,
+        ops::{Deref, DerefMut},
+        rc::Rc,
+    };
 
     struct UseState<T: 'static> {
         new_val: Rc<RefCell<Option<T>>>,
@@ -18,6 +22,22 @@ mod use_state_def {
         caller: Box<dyn Fn(T) + 'static>,
     }
 
+    struct UseStateHandle<'a, T> {
+        inner: &'a T,
+    }
+    impl<'a, T> UseStateHandle<'a, T> {
+        fn set(&self, new_val: T) {}
+        fn modify(&self, f: impl FnOnce(&mut T)) {}
+    }
+
+    impl<'a, T> Deref for UseStateHandle<'a, T> {
+        type Target = T;
+
+        fn deref(&self) -> &Self::Target {
+            self.inner
+        }
+    }
+
     /// Store state between component renders!
     /// When called, this hook retrives a stored value and provides a setter to update that value.
     /// When the setter is called, the component is re-ran with the new value.
@@ -175,9 +195,9 @@ mod use_reducer_def {
 
     // #[cfg(test)]
     mod tests {
-        
+
         use crate::prelude::*;
-        
+
         enum Actions {
             Incr,
             Decr,

+ 8 - 5
packages/core/src/lib.rs

@@ -91,14 +91,17 @@ pub(crate) mod innerlude {
     // pub(crate) use crate::component::Properties;
     pub(crate) use crate::component::Properties;
     pub(crate) use crate::context::Context;
-    pub(crate) use crate::error::{Result};
-    pub use crate::events::EventTrigger;
+    pub use crate::diff::LifeCycleEvent;
+    pub(crate) use crate::error::Result;
+    pub use crate::events::{EventTrigger, VirtualEvent};
     use crate::nodes;
-    
-    
-    pub(crate) use nodes::*;
 
     pub use crate::component::ScopeIdx;
+    pub use crate::context::hooks::Hook;
+    pub use crate::nodes::VNode;
+
+    pub(crate) use nodes::*;
+
     pub use crate::diff::DiffMachine;
     pub use crate::patch::{EditList, EditMachine};
     // pub use crate::patchdx;

+ 8 - 13
packages/core/src/nodebuilder.rs

@@ -1,18 +1,15 @@
 //! Helpers for building virtual DOM VNodes.
 
-use std::{borrow::BorrowMut};
+use std::{borrow::BorrowMut, intrinsics::transmute};
 
 use crate::{
     context::NodeCtx,
     events::VirtualEvent,
-    innerlude::{VComponent, FC},
+    innerlude::{Properties, VComponent, FC},
     nodes::{Attribute, Listener, NodeKey, VNode},
     prelude::VElement,
 };
 
-
-
-
 /// A virtual DOM element builder.
 ///
 /// Typically constructed with element-specific constructors, eg the `div`
@@ -526,12 +523,10 @@ pub fn attr<'a>(name: &'static str, value: &'a str) -> Attribute<'a> {
 
 // _f: crate::innerlude::FC<T>,
 // _props: T
-pub fn virtual_child<'a, 'b, T: crate::innerlude::Properties>(
-    _ctx: &'b NodeCtx<'a>,
-    _f: FC<T>,
-    _p: T,
-) -> VNode<'a> {
-    // crate::nodes::VComponent
-    todo!()
-    // VNode::Component()
+pub fn virtual_child<'a, T: Properties>(ctx: &NodeCtx<'a>, f: FC<T>, p: T) -> VNode<'a> {
+    // alloc props into the bump;
+
+    // erase the lifetime
+    let p_static = unsafe { p.into_static() };
+    VNode::Component(crate::nodes::VComponent::new(f, p_static))
 }

+ 38 - 25
packages/core/src/nodes.rs

@@ -10,12 +10,7 @@ use crate::{
 
 use bumpalo::Bump;
 use std::fmt::Debug;
-use std::{
-    any::{Any},
-    cell::RefCell,
-    marker::PhantomData,
-    rc::Rc,
-};
+use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
 
 /// A domtree represents the result of "Viewing" the context
 /// It's a placeholder over vnodes, to make working with lifetimes easier
@@ -271,21 +266,28 @@ impl<'a> VText<'a> {
 /// Only supports the functional syntax
 pub type StableScopeAddres = Rc<RefCell<Option<usize>>>;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct VComponent<'src> {
     pub stable_addr: StableScopeAddres,
+    pub raw_props: Rc<dyn Any>,
     pub comparator: Comparator,
     pub caller: Caller,
+    pub caller_ref: *const (),
     _p: PhantomData<&'src ()>,
 }
-pub struct Comparator(Box<dyn Fn(&dyn Any) -> bool>);
+pub trait PropsComparator {}
+
+#[derive(Clone)]
+pub struct Comparator(pub Rc<dyn Fn(&dyn PropsComparator) -> bool>);
+// pub struct Comparator(pub Rc<dyn Fn(&dyn Any) -> bool>);
 impl std::fmt::Debug for Comparator {
     fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         Ok(())
     }
 }
 
-pub struct Caller(Box<dyn Fn(Context) -> DomTree>);
+#[derive(Clone)]
+pub struct Caller(pub Rc<dyn Fn(Context) -> DomTree>);
 impl std::fmt::Debug for Caller {
     fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         Ok(())
@@ -298,30 +300,41 @@ impl<'a> VComponent<'a> {
     // - perform comparisons when diffing (memoization)
     // TODO: lift the requirement that props need to be static
     // we want them to borrow references... maybe force implementing a "to_static_unsafe" trait
-    pub fn new<P: Properties + 'static>(caller: FC<P>, props: P) -> Self {
+    pub fn new<P0: Properties, P: Properties + 'static>(caller: FC<P0>, props: P) -> Self {
+        let caller_ref = caller as *const ();
+
         let props = Rc::new(props);
 
         // used for memoization
         let p1 = props.clone();
-        let props_comparator = move |new_props: &dyn Any| -> bool {
-            new_props
-                .downcast_ref::<P>()
-                .map(|new_p| p1.as_ref() == new_p)
-                .unwrap_or_else(|| {
-                    log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL");
-                    false
-                })
+        let props_comparator = move |new_props: ()| -> bool {
+            false
+            // let props_comparator = move |new_props: &dyn Any| -> bool {
+            // new_props
+            //     .downcast_ref::<P>()
+            //     .map(|new_p| p1.as_ref() == new_p)
+            //     .unwrap_or_else(|| {
+            //         log::debug!("downcasting failed, this receovered but SHOULD NOT FAIL");
+            //         false
+            //     })
         };
 
         // used for actually rendering the custom component
         let p2 = props.clone();
-        let caller = move |ctx: Context| -> DomTree { caller(ctx, p2.as_ref()) };
+        let caller = move |ctx: Context| -> DomTree {
+            //
+            // cast back into the right lifetime
+            caller(ctx, p2.as_ref())
+        };
 
-        Self {
-            _p: PhantomData,
-            comparator: Comparator(Box::new(props_comparator)),
-            caller: Caller(Box::new(caller)),
-            stable_addr: Rc::new(RefCell::new(None)),
-        }
+        todo!()
+        // Self {
+        //     caller_ref,
+        //     raw_props: props,
+        //     _p: PhantomData,
+        //     comparator: Comparator(Rc::new(props_comparator)),
+        //     caller: Caller(Rc::new(caller)),
+        //     stable_addr: Rc::new(RefCell::new(None)),
+        // }
     }
 }

+ 15 - 6
packages/core/src/patch.rs

@@ -83,11 +83,13 @@ pub enum Edit<'d> {
     // ========================================================
     // push a known node on to the stack
     TraverseToKnown {
-        node: ScopeIdx,
+        node: uuid::Uuid,
+        // node: ScopeIdx,
     },
     // Add the current top of the stack to the known nodes
     MakeKnown {
-        node: ScopeIdx,
+        node: uuid::Uuid,
+        // node: ScopeIdx,
     },
     // Remove the current top of the stack from the known nodes
     RemoveKnown,
@@ -126,18 +128,18 @@ pub struct EditMachine<'src> {
     pub traversal: Traversal,
     next_temporary: u32,
     forcing_new_listeners: bool,
-
     pub emitter: EditList<'src>,
 }
 
 impl<'b> EditMachine<'b> {
-    pub fn new(_bump: &'b bumpalo::Bump) -> Self {
+    pub fn new() -> Self {
+        // pub fn new(_bump: &'b bumpalo::Bump) -> Self {
         // todo: see if bumpalo is needed for edit list
         Self {
             traversal: Traversal::new(),
             next_temporary: 0,
             forcing_new_listeners: false,
-            emitter: EditList::default(),
+            emitter: EditList::<'b>::default(),
         }
     }
 }
@@ -364,11 +366,18 @@ impl<'a> EditMachine<'a> {
         // debug!("emit: remove_event_listener({:?})", event);
     }
 
-    pub fn save_known_root(&mut self, id: ScopeIdx) {
+    pub fn save_known_root(&mut self, id: uuid::Uuid) {
+        // pub fn save_known_root(&mut self, id: ScopeIdx) {
+        // pub fn save_known_root(&mut self, id: ScopeIdx) {
         log::debug!("emit: save_known_root({:?})", id);
         self.emitter.push(Edit::MakeKnown { node: id })
     }
 
+    pub fn load_known_root(&mut self, id: uuid::Uuid) {
+        log::debug!("emit: TraverseToKnown({:?})", id);
+        self.emitter.push(Edit::TraverseToKnown { node: id })
+    }
+
     // pub fn save_template(&mut self, id: CacheId) {
     //     debug_assert!(self.traversal_is_committed());
     //     debug_assert!(!self.has_template(id));

+ 45 - 50
packages/core/src/scope.rs

@@ -1,21 +1,12 @@
-use crate::component::ScopeIdx;
-use crate::context::hooks::Hook;
 use crate::innerlude::*;
-use crate::nodes::VNode;
 use bumpalo::Bump;
 
-use std::{
-    any::{Any},
-    cell::RefCell,
-    marker::PhantomData,
-    ops::Deref,
-};
+use std::{any::Any, cell::RefCell, collections::HashSet, marker::PhantomData, ops::Deref};
 
 pub trait Scoped {
     fn run(&mut self);
-    fn compare_props(&self, new: &dyn std::any::Any) -> bool;
+    // fn compare_props(&self, new: &dyn std::any::Any) -> bool;
     fn call_listener(&mut self, trigger: EventTrigger);
-
     fn new_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
     fn old_frame<'bump>(&'bump self) -> &'bump VNode<'bump>;
 }
@@ -33,11 +24,16 @@ pub struct Scope<P: Properties> {
     // our own index
     pub myidx: ScopeIdx,
 
-    // the props
-    pub props: P,
+    //
+    pub children: HashSet<ScopeIdx>,
+
+    // // the props
+    // pub props: P,
 
     // and the actual render function
-    pub caller: FC<P>,
+    pub caller: Caller,
+    _p: std::marker::PhantomData<P>,
+    // pub raw_caller: FC<P>,
 
     // ==========================
     // slightly unsafe stuff
@@ -56,43 +52,31 @@ pub struct Scope<P: Properties> {
     // - is self-refenrential and therefore needs to point into the bump
     // Stores references into the listeners attached to the vnodes
     // NEEDS TO BE PRIVATE
-    listeners: RefCell<Vec<*const dyn Fn(crate::events::VirtualEvent)>>,
+    listeners: RefCell<Vec<*const dyn Fn(VirtualEvent)>>,
 }
 
 // instead of having it as a trait method, we use a single function
 // todo: do the unsafety magic stuff to erase the type of p
-pub fn create_scoped<P: Properties + 'static>(
-    caller: FC<P>,
-    props: P,
+pub fn create_scoped(
+    // raw_f: FC<P>,
+    caller: Caller,
+
+    // props: P,
     myidx: ScopeIdx,
     parent: Option<ScopeIdx>,
 ) -> Box<dyn Scoped> {
-    let hook_arena = typed_arena::Arena::new();
-    let hooks = RefCell::new(Vec::new());
-
-    let listeners = Default::default();
-
-    let old_frame = BumpFrame {
-        bump: Bump::new(),
-        head_node: VNode::text(""),
-    };
-
-    let new_frame = BumpFrame {
-        bump: Bump::new(),
-        head_node: VNode::text(""),
-    };
-
-    let frames = ActiveFrame::from_frames(old_frame, new_frame);
-
-    Box::new(Scope {
+    Box::new(Scope::<()> {
+        // raw_caller: raw_f,
+        _p: Default::default(),
         caller,
         myidx,
-        hook_arena,
-        hooks,
-        frames,
-        listeners,
+        hook_arena: typed_arena::Arena::new(),
+        hooks: RefCell::new(Vec::new()),
+        frames: ActiveFrame::new(),
+        children: HashSet::new(),
+        listeners: Default::default(),
         parent,
-        props,
+        // props,
     })
 }
 
@@ -122,9 +106,8 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
         };
 
         // Note that the actual modification of the vnode head element occurs during this call
-        // let _: DomTree = caller(ctx, props);
-        // let _: DomTree = P::render (ctx, &self.props);
-        let _: DomTree = (self.caller)(ctx, &self.props);
+        let _: DomTree = (self.caller.0.as_ref())(ctx);
+        // let _: DomTree = (self.raw_caller)(ctx, &self.props);
 
         /*
         SAFETY ALERT
@@ -145,11 +128,11 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
             .expect("Viewing did not happen");
     }
 
-    fn compare_props(&self, new: &dyn Any) -> bool {
-        new.downcast_ref::<P>()
-            .map(|f| &self.props == f)
-            .expect("Props should not be of a different type")
-    }
+    // fn compare_props(&self, new: &dyn Any) -> bool {
+    //     new.downcast_ref::<P>()
+    //         .map(|f| &self.props == f)
+    //         .expect("Props should not be of a different type")
+    // }
 
     // A safe wrapper around calling listeners
     // calling listeners will invalidate the list of listeners
@@ -194,7 +177,6 @@ impl<P: Properties + 'static> Scoped for Scope<P> {
 // ==========================
 // Active-frame related code
 // ==========================
-
 // todo, do better with the active frame stuff
 // somehow build this vnode with a lifetime tied to self
 // This root node has  "static" lifetime, but it's really not static.
@@ -212,6 +194,19 @@ pub struct BumpFrame {
 }
 
 impl ActiveFrame {
+    pub fn new() -> Self {
+        Self::from_frames(
+            BumpFrame {
+                bump: Bump::new(),
+                head_node: VNode::text(""),
+            },
+            BumpFrame {
+                bump: Bump::new(),
+                head_node: VNode::text(""),
+            },
+        )
+    }
+
     fn from_frames(a: BumpFrame, b: BumpFrame) -> Self {
         Self {
             idx: 0.into(),

+ 84 - 79
packages/core/src/virtual_dom.rs

@@ -7,7 +7,7 @@ use crate::{
 };
 use bumpalo::Bump;
 use generational_arena::Arena;
-
+use std::{any::TypeId, cell::RefCell, rc::Rc};
 
 /// 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.
@@ -17,8 +17,9 @@ pub struct VirtualDom {
     ///
     /// eventually, come up with a better datastructure that reuses boxes for known P types
     /// like a generational typemap bump arena
-    /// -> IE a cache line for each P type with soem heuristics on optimizing layout
+    /// -> IE a cache line for each P type with some heuristics on optimizing layout
     pub(crate) components: Arena<Box<dyn Scoped>>,
+    // pub(crate) components: RefCell<Arena<Box<dyn Scoped>>>,
     // pub(crate) components: Rc<RefCell<Arena<Box<dyn Scoped>>>>,
     /// The index of the root component.
     /// Will not be ready if the dom is fresh
@@ -59,53 +60,87 @@ impl VirtualDom {
     ///
     /// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
     /// to toss out the entire tree.
-    pub fn new_with_props<P: Properties + 'static>(_root: FC<P>, _root_props: P) -> Self {
-        // let mut components = Arena::new();
-        // let mut components = Arena::new();
+    pub fn new_with_props<P: Properties + 'static>(root: FC<P>, root_props: P) -> Self {
+        let mut components = Arena::new();
 
         // Create a reference to the component in the arena
         // Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist
-        // This puts the dom in a usable state on creation, rather than being potentially invalid
+        let caller = Caller(Rc::new(move |ctx| {
+            //
+            root(ctx, &root_props)
+            // DomTree {}
+        }));
+        let base_scope = components.insert_with(|id| create_scoped(caller, id, None));
         // let base_scope = components.insert_with(|id| create_scoped(root, root_props, id, None));
 
-        todo!()
-        // Self {
-        //     // components: RefCell::new(components),
-        //     components: components,
-        //     // components: Rc::new(RefCell::new(components)),
-        //     base_scope,
-        //     // event_queue: RefCell::new(VecDeque::new()),
-        //     diff_bump: Bump::new(),
-        //     _root_prop_type: TypeId::of::<P>(),
-        // }
+        Self {
+            components,
+            base_scope,
+            diff_bump: Bump::new(),
+            _root_prop_type: TypeId::of::<P>(),
+        }
     }
 
     /// Performs a *full* rebuild of the virtual dom, returning every edit required to generate the actual dom.
-
-    // pub fn rebuild<'s>(&'s mut self) -> Result<> {
-    // pub fn rebuild<'s>(&'s mut self) -> Result<std::cell::Ref<'_, Arena<Box<dyn Scoped>>>> {
     pub fn rebuild<'s>(&'s mut self) -> Result<EditList<'s>> {
+        log::debug!("rebuilding...");
         // Reset and then build a new diff machine
         // The previous edit list cannot be around while &mut is held
         // Make sure variance doesnt break this
-        self.diff_bump.reset();
-
-        self.components
-            .get_mut(self.base_scope)
-            .expect("Root should always exist")
-            .run();
-
-        let _b = Bump::new();
-
-        let mut diff_machine = DiffMachine::new(&self.diff_bump);
-        // let mut diff_machine = DiffMachine::new(self, &self.diff_bump, self.base_scope);
+        // bump.reset();
+
+        // Diff from the top
+        let mut diff_machine = DiffMachine::new(); // partial borrow
+        {
+            let component = self
+                .components
+                .get_mut(self.base_scope)
+                .expect("failed to acquire base scope");
+
+            component.run();
+        }
+
+        {
+            let component = self
+                .components
+                .get(self.base_scope)
+                .expect("failed to acquire base scope");
+
+            diff_machine.diff_node(component.old_frame(), component.new_frame());
+        }
+
+        // 'render: loop {
+        //     for event in &mut diff_machine.lifecycle_events.drain(..) {
+        //         log::debug!("event is {:#?}", event);
+        //         match event {
+        //             LifeCycleEvent::Mount { caller, id } => {
+        //                 diff_machine.change_list.load_known_root(id);
+        //                 let idx = self
+        //                     .components
+        //                     .insert_with(|f| create_scoped(caller, f, None));
+        //                 // .insert_with(|f| create_scoped(caller, props, myidx, parent));
+        //             }
+        //             LifeCycleEvent::PropsChanged => {
+        //                 //
+        //                 break 'render;
+        //             }
+        //             LifeCycleEvent::SameProps => {
+        //                 //
+        //                 break 'render;
+        //             }
+        //             LifeCycleEvent::Remove => {
+        //                 //
+        //                 break 'render;
+        //             }
+        //         }
+        //     }
 
-        // todo!()
+        //     if diff_machine.lifecycle_events.is_empty() {
+        //         break 'render;
+        //     }
+        // }
 
-        let component = self.components.get(self.base_scope).unwrap();
-        diff_machine.diff_node(component.old_frame(), component.new_frame());
-        let edits = diff_machine.consume();
-        // self.diff_bump = b;
+        let edits: Vec<Edit<'s>> = diff_machine.consume();
         Ok(edits)
     }
 
@@ -133,50 +168,20 @@ impl VirtualDom {
     /// }
     ///
     /// ```
-    pub fn progress_with_event(&mut self, _event: EventTrigger) -> Result<EditList<'_>> {
-        // self.components
-        //     .borrow_mut()
-        //     .get_mut(event.component_id)
-        //     .map(|f| {
-        //         f.call_listener(event);
-        //         f
-        //     })
-        //     .map(|f| f.run())
-        //     .expect("Borrowing should not fail");
-
-        // component.call_listener(event);
-
-        // .expect("Component should exist if an event was triggered");
-        // Reset and then build a new diff machine
-        // The previous edit list cannot be around while &mut is held
-        // Make sure variance doesnt break this
-        // self.diff_bump.reset();
-        // let mut diff_machine = DiffMachine::new(&mut self, event.component_id);
-        // let mut diff_machine =
-        //     DiffMachine::new(&self.diff_bump, &mut self.components, event.component_id);
-
-        // component.run();
-        // diff_machine.diff_node(component.old_frame(), component.new_frame());
-
-        todo!()
-        // Ok(diff_machine.consume())
-        // Err(crate::error::Error::NoEvent)
-        // Mark dirty components. Descend from the highest node until all dirty nodes are updated.
-        // let mut affected_components = Vec::new();
-
-        // while let Some(event) = self.pop_event() {
-        //     if let Some(component_idx) = event.index() {
-        //         affected_components.push(component_idx);
-        //     }
-        //     self.process_lifecycle(event)?;
-        // }
+    pub fn progress_with_event(&mut self, event: EventTrigger) -> Result<EditList<'_>> {
+        let component = self
+            .components
+            .get_mut(event.component_id)
+            .expect("Borrowing should not fail");
 
-        // todo!()
-    }
-}
+        component.call_listener(event);
+        component.run();
 
-enum LifeCycleEvent {
-    Mount {},
-}
+        let mut diff_machine = DiffMachine::new();
+        // let mut diff_machine = DiffMachine::new(&self.diff_bump);
+
+        diff_machine.diff_node(component.old_frame(), component.new_frame());
 
-// todo: add some "handle" to the vdom. Or a way of giving out closures that can mutate the vdoms internal data.
+        Ok(diff_machine.consume())
+    }
+}

+ 23 - 22
packages/web/examples/basic.rs

@@ -13,26 +13,27 @@ fn main() {
 }
 
 static App: FC<()> = |ctx, _| {
-    log::info!("Ran component");
-    use dioxus::builder::*;
-    todo!()
-    // ctx.render(|b| {
-    //     div(b)
-    //         .child(text("hello"))
-    //         .listeners([on(b, "click", |_| {
-    //             //
-    //             log::info!("button1 clicked!");
-    //         })])
-    //         .finish()
-    // })
-    // ctx.render(html! {
-    //     <div onclick={move |_| log::info!("button1 clicked!")}>
-    //         "Hello"
-    //         // <div class="flex items-center justify-center flex-col">
-    //         //     <div class="font-bold text-xl"> "Count is ..." </div>
-    //         //     <button onclick={move |_| log::info!("button1 clicked!")}> "increment" </button>
-    //         //     <button onclick={move |_| log::info!("button2 clicked!")}> "decrement" </button>
-    //         // </div>
-    //     </div>
-    // })
+    ctx.render(rsx! {
+        div {
+            h1 {"hello"}
+            C1 {}
+            C2 {}
+        }
+    })
+};
+
+static C1: FC<()> = |ctx, props| {
+    ctx.render(rsx! {
+        button {
+            "numba 1"
+        }
+    })
+};
+
+static C2: FC<()> = |ctx, props| {
+    ctx.render(rsx! {
+        button {
+            "numba 2"
+        }
+    })
 };

+ 21 - 4
packages/web/examples/infer.rs

@@ -6,6 +6,7 @@ fn main() {
     wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
     console_error_panic_hook::set_once();
 
+    log::info!("hello world");
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example));
 }
 
@@ -18,6 +19,8 @@ static Example: FC<()> = |ctx, _props| {
         set_event(Some(evt));
     };
 
+    log::info!("hello world");
+
     ctx.render(rsx! {
         div {  
             
@@ -35,21 +38,35 @@ static Example: FC<()> = |ctx, _props| {
                 id: "json"
                 "{event}"
             }
-            Example2 { name: "{event}" }
+            // Example2 { name: "{event}".to_string() }
         }
     })
 };
 
 
-#[derive(Debug, PartialEq)]
-struct Props {
+#[derive(Debug, PartialEq, Props)]
+struct ExampleProps {
     name: String
 }
 
-static Example2: FC<Props> = |ctx, props| {
+static Example2: FC<ExampleProps> = |ctx, props| {
     ctx.render(rsx!{
         div {
             h1 {"hello {props.name}"}
         }
     })
 };
+
+
+impl Properties for ExampleProps {
+    type Builder = ExamplePropsBuilder<((),)>;
+    type StaticOutput = ExampleProps;
+    fn builder() -> Self::Builder {
+        ExampleProps::builder()
+    }
+
+
+    unsafe fn into_static(self) -> Self::StaticOutput {
+        todo!()
+    }
+}

+ 5 - 17
packages/web/examples/rsxt.rs

@@ -1,23 +1,17 @@
-use bumpalo::Bump;
-use dioxus::{events::EventTrigger, prelude::*};
+#![allow(non_snake_case)]
 use dioxus_core as dioxus;
+use dioxus::prelude::*;
 use dioxus_web::WebsysRenderer;
 
-// creates a proxy address that gets transpiled at compile time
-// all asset ids are shuttled through the renderer
-// static IMG: ImgAsset = dioxus_asset!("");
-
 fn main() {
-    // wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    // console_error_panic_hook::set_once();
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Trace));
+    console_error_panic_hook::set_once();
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
 }
 
-static Example: FC<()> = |ctx, props| {
+static Example: FC<()> = |ctx, _props| {
     let (name, set_name) = use_state(&ctx, || "...?");
 
-    
-
     ctx.render(rsx! {
         div { 
             class: "py-12 px-4 text-center w-full max-w-2xl mx-auto"
@@ -47,9 +41,3 @@ static Example: FC<()> = |ctx, props| {
         }
     })
 };
-
-
-
-
-
-// onclick: {move |_| set_name("jill")}

+ 8 - 5
packages/web/src/lib.rs

@@ -2,6 +2,7 @@
 //! --------------
 //! This crate implements a renderer of the Dioxus Virtual DOM for the web browser using Websys.
 
+use dioxus::prelude::Properties;
 use fxhash::FxHashMap;
 use web_sys::{window, Document, Element, Event, Node};
 
@@ -48,7 +49,7 @@ impl WebsysRenderer {
     /// Automatically progresses the creation of the VNode tree to completion.
     ///
     /// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
-    pub fn new_with_props<T: 'static>(root: FC<T>, root_props: T) -> Self {
+    pub fn new_with_props<T: Properties + 'static>(root: FC<T>, root_props: T) -> Self {
         Self::from_vdom(VirtualDom::new_with_props(root, root_props))
     }
 
@@ -59,7 +60,7 @@ impl WebsysRenderer {
     }
 
     pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
-        let (mut sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
+        let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
 
         let body_element = prepare_websys_dom();
 
@@ -75,8 +76,10 @@ impl WebsysRenderer {
 
         // todo: initialize the event registry properly on the root
 
-        self.internal_dom.rebuild()?.iter().for_each(|edit| {
-            // log::debug!("patching with  {:?}", edit);
+        let edits = self.internal_dom.rebuild()?;
+        log::debug!("Received edits: {:#?}", edits);
+        edits.iter().for_each(|edit| {
+            log::debug!("patching with  {:?}", edit);
             patch_machine.handle_edit(edit);
         });
         // log::debug!("patch stack size {:?}", patch_machine.stack);
@@ -92,7 +95,7 @@ impl WebsysRenderer {
                 .progress_with_event(event)?
                 .iter()
                 .for_each(|edit| {
-                    // log::debug!("edit stream {:?}", edit);
+                    log::debug!("edit stream {:?}", edit);
                     patch_machine.handle_edit(edit);
                 });