1
0
Эх сурвалжийг харах

Merge pull request #33 from DioxusLabs/jk/no_borrow_lifetimes

Jk/no borrow lifetimes
Jonathan Kelley 3 жил өмнө
parent
commit
dc3204bb8d

+ 2 - 2
packages/core/benches/jsframework.rs

@@ -24,7 +24,7 @@ criterion_group!(mbenches, create_rows);
 criterion_main!(mbenches);
 
 fn create_rows(c: &mut Criterion) {
-    static App: FC<()> = |(cx, _)| {
+    static App: FC<()> = |cx, _| {
         let mut rng = SmallRng::from_entropy();
         let rows = (0..10_000_usize).map(|f| {
             let label = Label::new(&mut rng);
@@ -58,7 +58,7 @@ struct RowProps {
     row_id: usize,
     label: Label,
 }
-fn Row((cx, props): Scope<RowProps>) -> Element {
+fn Row(cx: Context, props: &RowProps) -> Element {
     let [adj, col, noun] = props.label.0;
     cx.render(rsx! {
         tr {

+ 9 - 0
packages/core/examples/props_expand.rs

@@ -0,0 +1,9 @@
+use dioxus_core as dioxus;
+use dioxus_core_macro::*;
+
+fn main() {}
+
+#[derive(Props)]
+struct ChildProps<'a> {
+    name: &'a str,
+}

+ 4 - 3
packages/core/examples/works.rs

@@ -7,12 +7,13 @@ fn main() {
     let _ = VirtualDom::new(Parent);
 }
 
-fn Parent((cx, _): Scope<()>) -> Element {
+fn Parent(cx: Context, props: &()) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| &*f);
 
     cx.render(rsx! {
         div {
             Child { name: value }
+            Fragment { "asd" }
         }
     })
 }
@@ -22,7 +23,7 @@ struct ChildProps<'a> {
     name: &'a str,
 }
 
-fn Child((cx, props): Scope<ChildProps>) -> Element {
+fn Child(cx: Context, props: &ChildProps) -> Element {
     cx.render(rsx! {
         div {
             h1 { "it's nested" }
@@ -36,7 +37,7 @@ struct Grandchild<'a> {
     name: &'a str,
 }
 
-fn Child2((cx, props): Scope<Grandchild>) -> Element {
+fn Child2(cx: Context, props: &Grandchild) -> Element {
     cx.render(rsx! {
         div { "Hello {props.name}!" }
     })

+ 3 - 35
packages/core/src/component.rs

@@ -6,39 +6,6 @@
 //! that ensures compile-time required and optional fields on cx.
 
 use crate::innerlude::{Context, Element, LazyNodes, ScopeChildren};
-/// A component is a wrapper around a Context and some Props that share a lifetime
-///
-///
-/// # Example
-///
-/// With memoized state:
-/// ```rust
-/// struct State {}
-///
-/// fn Example((cx, props): Scope<State>) -> DomTree {
-///     // ...
-/// }
-/// ```
-///
-/// With borrowed state:
-/// ```rust
-/// struct State<'a> {
-///     name: &'a str
-/// }
-///
-/// fn Example<'a>((cx, props): Scope<'a, State>) -> DomTree<'a> {
-///     // ...
-/// }
-/// ```
-///
-/// With owned state as a closure:
-/// ```rust
-/// static Example: FC<()> = |(cx, props)| {
-///     // ...
-/// };
-/// ```
-///
-pub type Scope<'a, T> = (Context<'a>, &'a T);
 
 pub struct FragmentProps<'a> {
     children: ScopeChildren<'a>,
@@ -99,7 +66,7 @@ impl<'a> Properties for FragmentProps<'a> {
 /// You want to use this free-function when your fragment needs a key and simply returning multiple nodes from rsx! won't cut it.
 ///
 #[allow(non_upper_case_globals, non_snake_case)]
-pub fn Fragment<'a>((cx, props): Scope<'a, FragmentProps<'a>>) -> Element {
+pub fn Fragment<'a>(cx: Context<'a>, props: &'a FragmentProps<'a>) -> Element {
     cx.render(Some(LazyNodes::new(|f| {
         f.fragment_from_iter(&props.children)
     })))
@@ -170,6 +137,7 @@ impl EmptyBuilder {
 
 /// This utility function launches the builder method so rsx! and html! macros can use the typed-builder pattern
 /// 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, T: Properties + 'a>(_: fn(Context<'a>, &'a T) -> Element) -> T::Builder {
+    // pub fn fc_to_builder<'a, T: Properties + 'a>(_: fn(Scope<'a, T>) -> Element) -> T::Builder {
     T::builder()
 }

+ 16 - 13
packages/core/src/diff.rs

@@ -90,6 +90,7 @@
 
 use crate::innerlude::*;
 use fxhash::{FxHashMap, FxHashSet};
+use slab::Slab;
 use DomEdit::*;
 
 /// Our DiffMachine is an iterative tree differ.
@@ -124,11 +125,13 @@ impl<'bump> DiffState<'bump> {
 
 impl<'bump> ScopeArena {
     pub fn diff_scope(&'bump self, state: &mut DiffState<'bump>, id: &ScopeId) {
-        if let Some(component) = self.get_scope(id) {
-            let (old, new) = (component.wip_head(), component.fin_head());
-            state.stack.push(DiffInstruction::Diff { new, old });
-            self.work(state, || false);
-        }
+        // if let Some(component) = self.get_scope(id) {
+
+        let (old, new) = (self.wip_head(id), self.fin_head(id));
+
+        state.stack.push(DiffInstruction::Diff { old, new });
+        self.work(state, || false);
+        // }
     }
 
     /// Progress the diffing for this "fiber"
@@ -609,7 +612,7 @@ impl<'bump> ScopeArena {
 
             // make sure the component's caller function is up to date
             let scope = self.get_scope(&scope_addr).unwrap();
-            scope.update_vcomp(new);
+            let mut items = scope.items.borrow_mut();
 
             // React doesn't automatically memoize, but we do.
             let props_are_the_same = todo!("reworking component memoization");
@@ -1145,8 +1148,8 @@ impl<'bump> ScopeArena {
                 }
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.get_scope(&scope_id).unwrap();
-                    search_node = Some(scope.root_node());
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    search_node = Some(self.root_node(&scope_id));
                 }
             }
         }
@@ -1163,8 +1166,8 @@ impl<'bump> ScopeArena {
                 }
                 VNode::Component(el) => {
                     let scope_id = el.associated_scope.get().unwrap();
-                    let scope = self.get_scope(&scope_id).unwrap();
-                    search_node = Some(scope.root_node());
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    search_node = Some(self.root_node(&scope_id));
                 }
                 VNode::Linked(link) => {
                     todo!("linked")
@@ -1248,8 +1251,8 @@ impl<'bump> ScopeArena {
 
                 VNode::Component(c) => {
                     let scope_id = c.associated_scope.get().unwrap();
-                    let scope = self.get_scope(&scope_id).unwrap();
-                    let root = scope.root_node();
+                    // let scope = self.get_scope(&scope_id).unwrap();
+                    let root = self.root_node(&scope_id);
                     self.remove_nodes(state, Some(root), gen_muts);
 
                     log::debug!("Destroying scope {:?}", scope_id);
@@ -1280,7 +1283,7 @@ impl<'bump> ScopeArena {
     }
 
     /// Adds a listener closure to a scope during diff.
-    fn attach_listener_to_scope(&'bump self, listener: &'bump Listener<'bump>, scope: &ScopeState) {
+    fn attach_listener_to_scope(&'bump self, listener: &'bump Listener<'bump>, scope: &Scope) {
         let long_listener = unsafe { std::mem::transmute(listener) };
         scope.items.borrow_mut().listeners.push(long_listener)
     }

+ 0 - 4
packages/core/src/diff_stack.rs

@@ -49,10 +49,6 @@ impl<'bump> DiffStack<'bump> {
         }
     }
 
-    pub fn is_empty(&self) -> bool {
-        self.instructions.is_empty()
-    }
-
     pub fn pop(&mut self) -> Option<DiffInstruction<'bump>> {
         self.instructions.pop()
     }

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

@@ -40,7 +40,7 @@ pub(crate) mod innerlude {
     pub use crate::virtual_dom::*;
 
     pub type Element = Option<NodeLink>;
-    pub type FC<P> = for<'a> fn(Scope<'a, P>) -> Element;
+    pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> Element;
 }
 
 pub use crate::innerlude::{
@@ -50,9 +50,11 @@ pub use crate::innerlude::{
 };
 
 pub mod prelude {
-    pub use crate::component::{fc_to_builder, Fragment, Properties, Scope};
+    pub use crate::component::{fc_to_builder, Fragment, Properties};
     pub use crate::innerlude::Context;
-    pub use crate::innerlude::{DioxusElement, Element, LazyNodes, NodeFactory, ScopeChildren, FC};
+    pub use crate::innerlude::{
+        DioxusElement, Element, LazyNodes, NodeFactory, Scope, ScopeChildren, FC,
+    };
     pub use crate::nodes::VNode;
     pub use crate::VirtualDom;
 }

+ 91 - 103
packages/core/src/nodes.rs

@@ -4,7 +4,7 @@
 //! cheap and *very* fast to construct - building a full tree should be quick.
 
 use crate::{
-    innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId, ScopeState},
+    innerlude::{empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId},
     lazynodes::LazyNodes,
 };
 use bumpalo::{boxed::Box as BumpBox, Bump};
@@ -20,7 +20,9 @@ use std::{
 ///
 /// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
 /// is essentially a unique key to guarantee safe usage of the Node.
+#[derive(Clone, Debug)]
 pub struct NodeLink {
+    pub(crate) link_idx: usize,
     pub(crate) gen_id: u32,
     pub(crate) scope_id: ScopeId,
 }
@@ -198,6 +200,7 @@ impl<'src> VNode<'src> {
             VNode::Linked(c) => VNode::Linked(NodeLink {
                 gen_id: c.gen_id,
                 scope_id: c.scope_id,
+                link_idx: c.link_idx,
             }),
         }
     }
@@ -207,20 +210,22 @@ impl Debug for VNode<'_> {
     fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
         match &self {
             VNode::Element(el) => s
-                .debug_struct("VElement")
+                .debug_struct("VNode::VElement")
                 .field("name", &el.tag_name)
                 .field("key", &el.key)
                 .finish(),
 
-            VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
-            VNode::Anchor(_) => write!(s, "VAnchor"),
+            VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
+            VNode::Anchor(_) => write!(s, "VNode::VAnchor"),
 
-            VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
-            VNode::Suspended { .. } => write!(s, "VSuspended"),
-            VNode::Component(comp) => write!(s, "VComponent {{ fc: {:?}}}", comp.user_fc),
+            VNode::Fragment(frag) => {
+                write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
+            }
+            VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
+            VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
             VNode::Linked(c) => write!(
                 s,
-                "VCached {{ gen_id: {}, scope_id: {:?} }}",
+                "VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
                 c.gen_id, c.scope_id
             ),
         }
@@ -344,31 +349,29 @@ pub struct Listener<'bump> {
     pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(Box<dyn Any + Send>) + 'bump>>>,
 }
 
+pub type VCompCaller<'src> = BumpBox<'src, dyn Fn(Context) -> Element + 'src>;
 /// Virtual Components for custom user-defined components
 /// Only supports the functional syntax
 pub struct VComponent<'src> {
     pub key: Option<&'src str>,
 
     pub associated_scope: Cell<Option<ScopeId>>,
-    // pub associated_scope: Cell<Option<*mut ScopeInner>>,
 
     // Function pointer to the FC that was used to generate this component
     pub user_fc: *const (),
+
     pub(crate) can_memoize: bool,
 
+    pub(crate) hard_allocation: Cell<Option<*const ()>>,
+
     // Raw pointer into the bump arena for the props of the component
-    pub(crate) raw_props: *const (),
+    pub(crate) bump_props: *const (),
 
     // during the "teardown" process we'll take the caller out so it can be dropped properly
     pub(crate) caller: Option<VCompCaller<'src>>,
     pub(crate) comparator: Option<BumpBox<'src, dyn Fn(&VComponent) -> bool + 'src>>,
 }
 
-pub enum VCompCaller<'src> {
-    Borrowed(BumpBox<'src, dyn for<'b> Fn(&'b ScopeState) -> Element + 'src>),
-    Owned(Box<dyn for<'b> Fn(&'b ScopeState) -> Element>),
-}
-
 pub struct VSuspended<'a> {
     pub task_id: u64,
     pub dom_id: Cell<Option<ElementId>>,
@@ -511,7 +514,7 @@ impl<'a> NodeFactory<'a> {
 
     pub fn component<P>(
         &self,
-        component: fn(Scope<'a, P>) -> Element,
+        component: fn(Context<'a>, &'a P) -> Element,
         props: P,
         key: Option<Arguments>,
     ) -> VNode<'a>
@@ -528,99 +531,84 @@ impl<'a> NodeFactory<'a> {
         - if the props aren't static, then we convert them into a box which we pass off between renders
         */
 
-        let bump = self.bump();
-
-        // let p = BumpBox::new_in(x, a)
-
-        // the best place to allocate the props are the other component's arena
-        // the second best place is the global allocator
-
-        // // if the props are static
-        // let boxed = if P::IS_STATIC {
-        //     todo!()
-        // } else {
-        //     todo!()
-        // }
-
-        // let caller = Box::new(|f: &ScopeInner| -> Element {
-        //     //
-        //     component((f, &props))
-        // });
+        // let bump = self.bump();
 
+        // // later, we'll do a hard allocation
+        // let raw_ptr = bump.alloc(props);
+        let bump = self.bump();
+        let props = bump.alloc(props);
+        let bump_props = props as *mut P as *mut ();
         let user_fc = component as *const ();
 
-        // let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
-        //     move |other: &VComponent| {
-        //         if user_fc == other.user_fc {
-        //             // Safety
-        //             // - We guarantee that FC<P> is the same by function pointer
-        //             // - Because FC<P> is the same, then P must be the same (even with generics)
-        //             // - Non-static P are autoderived to memoize as false
-        //             // - This comparator is only called on a corresponding set of bumpframes
-        //             let props_memoized = unsafe {
-        //                 let real_other: &P = &*(other.raw_props as *const _ as *const P);
-        //                 props.memoize(real_other)
-        //             };
-
-        //             // It's only okay to memoize if there are no children and the props can be memoized
-        //             // Implementing memoize is unsafe and done automatically with the props trait
-        //             props_memoized
-        //         } else {
-        //             false
-        //         }
-        //     }
-        // });
-        // let comparator = Some(unsafe { BumpBox::from_raw(comparator) });
-
-        let key = key.map(|f| self.raw_text(f).0);
-
-        let caller = match P::IS_STATIC {
-            true => {
-                // it just makes sense to box the props
-                let boxed_props: Box<P> = Box::new(props);
-                let props_we_know_are_static = todo!();
-                VCompCaller::Owned(Box::new(|f| {
-                    //
-
-                    let p = todo!();
-
-                    todo!()
-                }))
+        let comparator: &mut dyn Fn(&VComponent) -> bool = bump.alloc_with(|| {
+            move |other: &VComponent| {
+                if user_fc == other.user_fc {
+                    // Safety
+                    // - We guarantee that FC<P> is the same by function pointer
+                    // - Because FC<P> is the same, then P must be the same (even with generics)
+                    // - Non-static P are autoderived to memoize as false
+                    // - This comparator is only called on a corresponding set of bumpframes
+                    let props_memoized = unsafe {
+                        let real_other: &P = &*(other.bump_props as *const _ as *const P);
+                        props.memoize(real_other)
+                    };
+
+                    // It's only okay to memoize if there are no children and the props can be memoized
+                    // Implementing memoize is unsafe and done automatically with the props trait
+                    props_memoized
+                } else {
+                    false
+                }
             }
-            false => VCompCaller::Borrowed({
-                //
-
-                todo!()
-                // let caller = bump.alloc()
-            }),
+        });
+
+        let drop_props = {
+            // create a closure to drop the props
+            let mut has_dropped = false;
+
+            let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
+                move || unsafe {
+                    log::debug!("dropping props!");
+                    if !has_dropped {
+                        let real_other = bump_props as *mut _ as *mut P;
+                        let b = BumpBox::from_raw(real_other);
+                        std::mem::drop(b);
+
+                        has_dropped = true;
+                    } else {
+                        panic!("Drop props called twice - this is an internal failure of Dioxus");
+                    }
+                }
+            });
+
+            let drop_props = unsafe { BumpBox::from_raw(drop_props) };
+
+            RefCell::new(Some(drop_props))
         };
 
-        todo!()
-        // let caller: &'a mut dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> =
-        //     bump.alloc(move |scope: &ScopeInner| -> Element {
-        //         log::debug!("calling component renderr {:?}", scope.our_arena_idx);
-        //         let props: &'_ P = unsafe { &*(raw_props as *const P) };
-
-        //         let scp: &'a ScopeInner = unsafe { std::mem::transmute(scope) };
-        //         let s: Scope<'a, P> = (scp, props);
-
-        //         let res: Element = component(s);
-        //         unsafe { std::mem::transmute(res) }
-        //     });
-
-        // let caller = unsafe { BumpBox::from_raw(caller) };
-
-        // VNode::Component(bump.alloc(VComponent {
-        //     user_fc,
-        //     comparator,
-        //     raw_props,
-        //     caller,
-        //     is_static: P::IS_STATIC,
-        //     key,
-        //     can_memoize: P::IS_STATIC,
-        //     drop_props,
-        //     associated_scope: Cell::new(None),
-        // }))
+        let key = key.map(|f| self.raw_text(f).0);
+
+        let caller: &'a mut dyn Fn(&Scope) -> Element =
+            bump.alloc(move |scope: &Scope| -> Element {
+                log::debug!("calling component renderr {:?}", scope.our_arena_idx);
+                let props: &'_ P = unsafe { &*(bump_props as *const P) };
+                let res = component(scope, props);
+                // let res = component((Context { scope }, props));
+                unsafe { std::mem::transmute(res) }
+            });
+
+        let can_memoize = P::IS_STATIC;
+
+        VNode::Component(bump.alloc(VComponent {
+            user_fc,
+            comparator,
+            bump_props,
+            caller,
+            key,
+            can_memoize,
+            drop_props,
+            associated_scope: Cell::new(None),
+        }))
     }
 
     pub fn listener(

+ 55 - 72
packages/core/src/scope.rs

@@ -30,7 +30,7 @@ use bumpalo::{boxed::Box as BumpBox, Bump};
 ///     cx.render(rsx!{ div {"Hello, {props.name}"} })
 /// }
 /// ```
-pub type Context<'a> = &'a ScopeState;
+pub type Context<'a> = &'a Scope;
 
 /// Every component in Dioxus is represented by a `Scope`.
 ///
@@ -41,7 +41,7 @@ pub type Context<'a> = &'a ScopeState;
 ///
 /// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
 /// use case they might have.
-pub struct ScopeState {
+pub struct Scope {
     // Book-keeping about our spot in the arena
 
     // safety:
@@ -49,7 +49,7 @@ pub struct ScopeState {
     // pointers to scopes are *always* valid since they are bump allocated and never freed until this scope is also freed
     // this is just a bit of a hack to not need an Rc to the ScopeArena.
     // todo: replace this will ScopeId and provide a connection to scope arena directly
-    pub(crate) parent_scope: Option<*mut ScopeState>,
+    pub(crate) parent_scope: Option<*mut Scope>,
 
     pub(crate) our_arena_idx: ScopeId,
 
@@ -59,14 +59,16 @@ pub struct ScopeState {
 
     pub(crate) is_subtree_root: Cell<bool>,
 
-    // The double-buffering situation that we will use
-    pub(crate) frames: [Bump; 2],
+    pub(crate) generation: Cell<u32>,
 
-    pub(crate) vcomp: *const VComponent<'static>,
+    // The double-buffering situation that we will use
+    pub(crate) frames: [BumpFrame; 2],
 
     pub(crate) old_root: RefCell<Option<NodeLink>>,
     pub(crate) new_root: RefCell<Option<NodeLink>>,
 
+    pub(crate) caller: *mut dyn Fn(&Scope) -> Element,
+
     /*
     we care about:
     - listeners (and how to call them when an event is triggered)
@@ -85,12 +87,6 @@ pub struct ScopeState {
 }
 
 pub struct SelfReferentialItems<'a> {
-    // nodes stored by "cx.render"
-    pub(crate) cached_nodes_old: Vec<VNode<'a>>,
-    pub(crate) cached_nodes_new: Vec<VNode<'a>>,
-
-    pub(crate) generation: Cell<u32>,
-
     pub(crate) listeners: Vec<&'a Listener<'a>>,
     pub(crate) borrowed_props: Vec<&'a VComponent<'a>>,
     pub(crate) suspended_nodes: FxHashMap<u64, &'a VSuspended<'a>>,
@@ -99,26 +95,7 @@ pub struct SelfReferentialItems<'a> {
 }
 
 // Public methods exposed to libraries and components
-impl ScopeState {
-    /// Get the root VNode for this Scope.
-    ///
-    /// This VNode is the "entrypoint" VNode. If the component renders multiple nodes, then this VNode will be a fragment.
-    ///
-    /// # Example
-    /// ```rust
-    /// let mut dom = VirtualDom::new(|(cx, props)|cx.render(rsx!{ div {} }));
-    /// dom.rebuild();
-    ///
-    /// let base = dom.base_scope();
-    ///
-    /// if let VNode::VElement(node) = base.root_node() {
-    ///     assert_eq!(node.tag_name, "div");
-    /// }
-    /// ```
-    pub fn root_node(&self) -> &VNode {
-        self.fin_head()
-    }
-
+impl Scope {
     /// Get the subtree ID that this scope belongs to.
     ///
     /// Each component has its own subtree ID - the root subtree has an ID of 0. This ID is used by the renderer to route
@@ -239,7 +216,7 @@ impl ScopeState {
     ///
     /// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
     pub fn bump(&self) -> &Bump {
-        &self.wip_frame()
+        &self.wip_frame().bump
     }
 
     /// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
@@ -258,18 +235,18 @@ impl ScopeState {
     /// }
     ///```
     pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
-        let bump = &self.wip_frame();
+        let bump = &self.wip_frame().bump;
         let factory = NodeFactory { bump };
         let node = lazy_nodes.map(|f| f.call(factory))?;
 
-        self.items
-            .borrow_mut()
-            .cached_nodes_old
-            .push(unsafe { std::mem::transmute(node) });
+        let idx = self
+            .wip_frame()
+            .add_node(unsafe { std::mem::transmute(node) });
 
         Some(NodeLink {
-            gen_id: self.items.borrow().generation.get(),
+            gen_id: self.generation.get(),
             scope_id: self.our_arena_idx,
+            link_idx: idx,
         })
     }
 
@@ -427,11 +404,7 @@ impl ScopeState {
             self.hooks.push_hook(initializer(self.hooks.len()));
         }
 
-        runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
-    }
-}
-
-const HOOK_ERR_MSG: &str = r###"
+        const HOOK_ERR_MSG: &str = r###"
 Unable to retrieve the hook that was initialized at this index.
 Consult the `rules of hooks` to understand how to use hooks properly.
 
@@ -439,29 +412,24 @@ You likely used the hook in a conditional. Hooks rely on consistent ordering bet
 Functions prefixed with "use" should never be called conditionally.
 "###;
 
-// Important internal methods
-impl ScopeState {
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub(crate) fn fin_head<'b>(&'b self) -> &'b VNode<'b> {
-        todo!()
-        // let cur_head = &self.finished_frame().head_node;
-        // unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
-    }
-
-    /// Give out our self-referential item with our own borrowed lifetime
-    pub(crate) fn wip_head<'b>(&'b self) -> &'b VNode<'b> {
-        todo!()
-        // let cur_head = &self.wip_frame().head_node;
-        // unsafe { std::mem::transmute::<&VNode<'static>, &VNode<'b>>(cur_head) }
+        runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
     }
+}
 
+// Important internal methods
+impl Scope {
     /// The "work in progress frame" represents the frame that is currently being worked on.
-    pub(crate) fn wip_frame(&self) -> &Bump {
-        todo!()
-        // match self.cur_generation.get() & 1 == 0 {
-        //     true => &self.frames[0],
-        //     false => &self.frames[1],
-        // }
+    pub(crate) fn wip_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 0 {
+            true => &self.frames[0],
+            false => &self.frames[1],
+        }
+    }
+    pub(crate) fn fin_frame(&self) -> &BumpFrame {
+        match self.generation.get() & 1 == 1 {
+            true => &self.frames[0],
+            false => &self.frames[1],
+        }
     }
 
     pub unsafe fn reset_wip_frame(&self) {
@@ -469,8 +437,10 @@ impl ScopeState {
         let bump = self.wip_frame() as *const _ as *mut Bump;
         let g = &mut *bump;
         g.reset();
+    }
 
-        // self.wip_frame_mut().bump.reset()
+    pub fn cycle_frame(&self) {
+        self.generation.set(self.generation.get() + 1);
     }
 
     /// A safe wrapper around calling listeners
@@ -527,14 +497,27 @@ impl ScopeState {
         //     Some(cur)
         // }
     }
+}
 
-    pub(crate) fn update_vcomp(&self, vcomp: &VComponent) {
-        let f: *const _ = vcomp;
-        todo!()
-        // self.vcomp = unsafe { std::mem::transmute(f) };
+pub struct BumpFrame {
+    pub bump: Bump,
+    pub nodes: RefCell<Vec<VNode<'static>>>,
+}
+impl BumpFrame {
+    pub fn new() -> Self {
+        let bump = Bump::new();
+
+        let node = &*bump.alloc(VText {
+            text: "asd",
+            dom_id: Default::default(),
+            is_static: false,
+        });
+        let nodes = RefCell::new(vec![VNode::Text(unsafe { std::mem::transmute(node) })]);
+        Self { bump, nodes }
     }
-
-    pub(crate) fn load_vcomp<'a>(&'a mut self) -> &'a VComponent<'a> {
-        unsafe { std::mem::transmute(&*self.vcomp) }
+    fn add_node(&self, node: VNode<'static>) -> usize {
+        let mut nodes = self.nodes.borrow_mut();
+        nodes.push(node);
+        nodes.len() - 1
     }
 }

+ 153 - 20
packages/core/src/scopearena.rs

@@ -1,4 +1,8 @@
-use std::cell::{Cell, RefCell};
+use slab::Slab;
+use std::{
+    borrow::BorrowMut,
+    cell::{Cell, RefCell},
+};
 
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use futures_channel::mpsc::UnboundedSender;
@@ -19,8 +23,9 @@ pub struct Heuristic {
 // has an internal heuristics engine to pre-allocate arenas to the right size
 pub(crate) struct ScopeArena {
     bump: Bump,
-    scopes: Vec<*mut ScopeState>,
+    scopes: Vec<*mut Scope>,
     free_scopes: Vec<ScopeId>,
+    nodes: RefCell<Slab<*const VNode<'static>>>,
     pub(crate) sender: UnboundedSender<SchedulerMsg>,
 }
 
@@ -30,19 +35,20 @@ impl ScopeArena {
             bump: Bump::new(),
             scopes: Vec::new(),
             free_scopes: Vec::new(),
+            nodes: RefCell::new(Slab::new()),
             sender,
         }
     }
 
-    pub fn get_scope(&self, id: &ScopeId) -> Option<&ScopeState> {
+    pub fn get_scope(&self, id: &ScopeId) -> Option<&Scope> {
         unsafe { Some(&*self.scopes[id.0]) }
     }
 
     pub fn new_with_key(
         &mut self,
         fc_ptr: *const (),
-        vcomp: &VComponent,
-        parent_scope: Option<*mut ScopeState>,
+        caller: *mut dyn Fn(&Scope) -> Element,
+        parent_scope: Option<*mut Scope>,
         height: u32,
         subtree: u32,
     ) -> ScopeId {
@@ -53,50 +59,71 @@ impl ScopeArena {
             todo!("override the scope contents");
             id
         } else {
-            let id = ScopeId(self.scopes.len());
+            let scope_id = ScopeId(self.scopes.len());
 
-            let vcomp = unsafe { std::mem::transmute(vcomp as *const VComponent) };
+            let old_root = NodeLink {
+                link_idx: 0,
+                gen_id: 0,
+                scope_id,
+            };
+            let new_root = NodeLink {
+                link_idx: 0,
+                gen_id: 0,
+                scope_id,
+            };
 
-            let new_scope = ScopeState {
+            let new_scope = Scope {
                 sender: self.sender.clone(),
                 parent_scope,
-                our_arena_idx: id,
+                our_arena_idx: scope_id,
                 height,
                 subtree: Cell::new(subtree),
                 is_subtree_root: Cell::new(false),
-                frames: [Bump::default(), Bump::default()],
-                vcomp,
+                frames: [BumpFrame::new(), BumpFrame::new()],
 
                 hooks: Default::default(),
                 shared_contexts: Default::default(),
+                caller,
+                generation: 0.into(),
 
+                old_root: RefCell::new(Some(old_root)),
+                new_root: RefCell::new(Some(new_root)),
+                // old_root: RefCell::new(Some(old_root)),
+                // new_root: RefCell::new(None),
                 items: RefCell::new(SelfReferentialItems {
                     listeners: Default::default(),
                     borrowed_props: Default::default(),
                     suspended_nodes: Default::default(),
                     tasks: Default::default(),
                     pending_effects: Default::default(),
-                    cached_nodes_old: Default::default(),
-                    generation: Default::default(),
-                    cached_nodes_new: todo!(),
                 }),
-                old_root: todo!(),
-                new_root: todo!(),
             };
 
             let stable = self.bump.alloc(new_scope);
             self.scopes.push(stable);
-            id
+            scope_id
         }
     }
 
-    pub fn try_remove(&self, id: &ScopeId) -> Option<ScopeState> {
+    pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> {
         todo!()
     }
 
     pub fn reserve_node(&self, node: &VNode) -> ElementId {
-        todo!()
-        // self.node_reservations.insert(id);
+        let mut els = self.nodes.borrow_mut();
+        let entry = els.vacant_entry();
+        let key = entry.key();
+        let id = ElementId(key);
+        let node = node as *const _;
+        let node = unsafe { std::mem::transmute(node) };
+        entry.insert(node);
+        id
+
+        // let nodes = self.nodes.borrow_mut();
+        // let id = nodes.insert(());
+        // let node_id = ElementId(id);
+        // node = Some(node_id);
+        // node_id
     }
 
     pub fn collect_garbage(&self, id: ElementId) {
@@ -154,4 +181,110 @@ impl ScopeArena {
             .map(|li| unsafe { &*li })
             .for_each(|listener| drop(listener.callback.borrow_mut().take()));
     }
+
+    pub(crate) fn run_scope(&self, id: &ScopeId) -> bool {
+        let scope = self
+            .get_scope(id)
+            .expect("The base scope should never be moved");
+
+        // Cycle to the next frame and then reset it
+        // This breaks any latent references, invalidating every pointer referencing into it.
+        // Remove all the outdated listeners
+        self.ensure_drop_safety(id);
+
+        // Safety:
+        // - We dropped the listeners, so no more &mut T can be used while these are held
+        // - All children nodes that rely on &mut T are replaced with a new reference
+        unsafe { scope.hooks.reset() };
+
+        // Safety:
+        // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
+        unsafe { scope.reset_wip_frame() };
+
+        {
+            let mut items = scope.items.borrow_mut();
+
+            // just forget about our suspended nodes while we're at it
+            items.suspended_nodes.clear();
+
+            // guarantee that we haven't screwed up - there should be no latent references anywhere
+            debug_assert!(items.listeners.is_empty());
+            debug_assert!(items.suspended_nodes.is_empty());
+            debug_assert!(items.borrowed_props.is_empty());
+
+            log::debug!("Borrowed stuff is successfully cleared");
+
+            // temporarily cast the vcomponent to the right lifetime
+            // let vcomp = scope.load_vcomp();
+        }
+
+        let render: &dyn Fn(&Scope) -> Element = unsafe { &*scope.caller };
+
+        // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
+        scope.wip_frame().nodes.borrow_mut().clear();
+        if let Some(key) = render(scope) {
+            dbg!(key);
+
+            dbg!(&scope.wip_frame().nodes.borrow_mut());
+            // let mut old = scope.old_root.borrow_mut();
+            // let mut new = scope.new_root.borrow_mut();
+
+            // let new_old = new.clone();
+            // *old = new_old;
+            // *new = Some(key);
+
+            // dbg!(&old);
+            // dbg!(&new);
+
+            // the user's component succeeded. We can safely cycle to the next frame
+            // scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
+            scope.cycle_frame();
+
+            true
+        } else {
+            false
+        }
+    }
+
+    pub fn wip_head(&self, id: &ScopeId) -> &VNode {
+        let scope = self.get_scope(id).unwrap();
+        let wip_frame = scope.wip_frame();
+        let nodes = wip_frame.nodes.borrow();
+        let node = nodes.get(0).unwrap();
+        unsafe { std::mem::transmute(node) }
+        // let root = scope.old_root.borrow();
+        // let link = root.as_ref().unwrap();
+        // dbg!(link);
+
+        // // for now, components cannot pass portals through each other
+        // assert_eq!(link.scope_id, *id);
+        // // assert_eq!(link.gen_id, scope.generation.get() - 1);
+
+        // // let items = scope.items.borrow();
+        // let nodes = scope.wip_frame().nodes.borrow();
+        // let node = nodes.get(link.link_idx).unwrap();
+        // unsafe { std::mem::transmute(node) }
+    }
+
+    pub fn fin_head(&self, id: &ScopeId) -> &VNode {
+        let scope = self.get_scope(id).unwrap();
+        let wip_frame = scope.fin_frame();
+        let nodes = wip_frame.nodes.borrow();
+        let node = nodes.get(0).unwrap();
+        unsafe { std::mem::transmute(node) }
+        // let scope = self.get_scope(id).unwrap();
+        // let root = scope.new_root.borrow();
+        // let link = root.as_ref().unwrap();
+
+        // // for now, components cannot pass portals through each other
+        // assert_eq!(link.scope_id, *id);
+        // // assert_eq!(link.gen_id, scope.generation.get());
+
+        // let nodes = scope.fin_frame().nodes.borrow();
+        // let node = nodes.get(link.link_idx).unwrap();
+        // unsafe { std::mem::transmute(node) }
+    }
+    pub fn root_node(&self, id: &ScopeId) -> &VNode {
+        self.wip_head(id)
+    }
 }

+ 0 - 4
packages/core/src/util.rs

@@ -7,10 +7,6 @@ pub fn empty_cell() -> Cell<Option<ElementId>> {
     Cell::new(None)
 }
 
-pub fn type_name_of<T>(_: T) -> &'static str {
-    std::any::type_name::<T>()
-}
-
 /// A component's unique identifier.
 ///
 /// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is

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

@@ -20,21 +20,13 @@
 //! Additional functionality is defined in the respective files.
 
 use crate::innerlude::*;
-use bumpalo::Bump;
 use futures_channel::mpsc::{UnboundedReceiver, UnboundedSender};
-use futures_util::{pin_mut, stream::FuturesUnordered, Future, FutureExt, StreamExt};
-use fxhash::FxHashMap;
+use futures_util::{Future, StreamExt};
 use fxhash::FxHashSet;
 use indexmap::IndexSet;
-use slab::Slab;
 use std::pin::Pin;
 use std::task::Poll;
-use std::{
-    any::{Any, TypeId},
-    cell::{Cell, UnsafeCell},
-    collections::{HashSet, VecDeque},
-    rc::Rc,
-};
+use std::{any::Any, collections::VecDeque};
 
 /// An integrated virtual node system that progresses events and diffs UI trees.
 ///
@@ -69,10 +61,7 @@ use std::{
 pub struct VirtualDom {
     base_scope: ScopeId,
 
-    _root_props: Rc<dyn Any>,
-
-    // we need to keep the allocation around, but we don't necessarily use it
-    _root_caller: Box<dyn Any>,
+    _root_caller: *mut dyn Fn(&Scope) -> Element,
 
     pub(crate) scopes: ScopeArena,
 
@@ -156,30 +145,22 @@ impl VirtualDom {
         sender: UnboundedSender<SchedulerMsg>,
         receiver: UnboundedReceiver<SchedulerMsg>,
     ) -> Self {
-        let mut scopes = ScopeArena::new(sender);
-
-        let base_scope = scopes.new_with_key(
-            //
-            root as _,
-            todo!(),
-            // boxed_comp.as_ref(),
-            None,
-            0,
-            0,
-        );
+        let mut scopes = ScopeArena::new(sender.clone());
+
+        let caller = Box::new(move |f: &Scope| -> Element { root(f, &root_props) });
+        let caller_ref: *mut dyn Fn(&Scope) -> Element = Box::into_raw(caller);
+        let base_scope = scopes.new_with_key(root as _, caller_ref, None, 0, 0);
 
         Self {
             scopes,
             base_scope,
             receiver,
-            sender,
-
-            _root_props: todo!(),
-            _root_caller: todo!(),
-
+            // todo: clean this up manually?
+            _root_caller: caller_ref,
             pending_messages: VecDeque::new(),
             pending_futures: Default::default(),
             dirty_scopes: Default::default(),
+            sender,
         }
     }
 
@@ -189,7 +170,7 @@ impl VirtualDom {
     /// directly.
     ///
     /// # Example
-    pub fn base_scope(&self) -> &ScopeState {
+    pub fn base_scope(&self) -> &Scope {
         self.get_scope(&self.base_scope).unwrap()
     }
 
@@ -199,7 +180,7 @@ impl VirtualDom {
     ///
     ///
     ///
-    pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a ScopeState> {
+    pub fn get_scope<'a>(&'a self, id: &ScopeId) -> Option<&'a Scope> {
         self.scopes.get_scope(id)
     }
 
@@ -402,9 +383,11 @@ impl VirtualDom {
 
                     log::debug!("about to run scope {:?}", scopeid);
 
-                    if self.run_scope(&scopeid) {
-                        let scope = self.scopes.get_scope(&scopeid).unwrap();
-                        let (old, new) = (scope.wip_head(), scope.fin_head());
+                    if self.scopes.run_scope(&scopeid) {
+                        let (old, new) = (
+                            self.scopes.wip_head(&scopeid),
+                            self.scopes.fin_head(&scopeid),
+                        );
                         diff_state.stack.scope_stack.push(scopeid);
                         diff_state.stack.push(DiffInstruction::Diff { new, old });
                     }
@@ -457,13 +440,20 @@ impl VirtualDom {
     /// apply_edits(edits);
     /// ```
     pub fn rebuild(&mut self) -> Mutations {
-        // todo: I think we need to append a node or something
-        //     diff_machine
-        //         .stack
-        //         .create_node(cur_component.frames.fin_head(), MountType::Append);
+        let mut diff_machine = DiffState::new(Mutations::new());
+
+        let scope_id = self.base_scope;
+        if self.scopes.run_scope(&scope_id) {
+            diff_machine
+                .stack
+                .create_node(self.scopes.fin_head(&scope_id), MountType::Append);
 
-        let scope = self.base_scope;
-        self.hard_diff(&scope).unwrap()
+            diff_machine.stack.scope_stack.push(scope_id);
+
+            self.scopes.work(&mut diff_machine, || false);
+        }
+
+        diff_machine.mutations
     }
 
     /// Compute a manual diff of the VirtualDOM between states.
@@ -503,73 +493,20 @@ impl VirtualDom {
     pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
         log::debug!("hard diff {:?}", scope_id);
 
-        if self.run_scope(scope_id) {
+        if self.scopes.run_scope(scope_id) {
             let mut diff_machine = DiffState::new(Mutations::new());
 
             diff_machine.force_diff = true;
 
             self.scopes.diff_scope(&mut diff_machine, scope_id);
 
+            dbg!(&diff_machine.mutations);
+
             Some(diff_machine.mutations)
         } else {
             None
         }
     }
-
-    fn run_scope(&self, id: &ScopeId) -> bool {
-        let scope = self
-            .scopes
-            .get_scope(id)
-            .expect("The base scope should never be moved");
-
-        // Cycle to the next frame and then reset it
-        // This breaks any latent references, invalidating every pointer referencing into it.
-        // Remove all the outdated listeners
-        self.scopes.ensure_drop_safety(id);
-
-        // Safety:
-        // - We dropped the listeners, so no more &mut T can be used while these are held
-        // - All children nodes that rely on &mut T are replaced with a new reference
-        unsafe { scope.hooks.reset() };
-
-        // Safety:
-        // - We've dropped all references to the wip bump frame with "ensure_drop_safety"
-        unsafe { scope.reset_wip_frame() };
-
-        let mut items = scope.items.borrow_mut();
-
-        // just forget about our suspended nodes while we're at it
-        items.suspended_nodes.clear();
-
-        // guarantee that we haven't screwed up - there should be no latent references anywhere
-        debug_assert!(items.listeners.is_empty());
-        debug_assert!(items.suspended_nodes.is_empty());
-        debug_assert!(items.borrowed_props.is_empty());
-
-        log::debug!("Borrowed stuff is successfully cleared");
-
-        // temporarily cast the vcomponent to the right lifetime
-        // let vcomp = scope.load_vcomp();
-
-        let render: &dyn Fn(&ScopeState) -> Element = todo!();
-
-        // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
-        if let Some(key) = render(scope) {
-            // todo!("attach the niode");
-            // let new_head = builder.into_vnode(NodeFactory {
-            //     bump: &scope.frames.wip_frame().bump,
-            // });
-            // log::debug!("Render is successful");
-
-            // the user's component succeeded. We can safely cycle to the next frame
-            // scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) };
-            // scope.frames.cycle_frame();
-
-            true
-        } else {
-            false
-        }
-    }
 }
 
 pub enum SchedulerMsg {

+ 3 - 3
packages/core/tests/borrowedstate.rs

@@ -10,7 +10,7 @@ fn test_borrowed_state() {
     let _ = VirtualDom::new(Parent);
 }
 
-fn Parent((cx, _): Scope<()>) -> Element {
+fn Parent(cx: Context, props: &()) -> Element {
     let value = cx.use_hook(|_| String::new(), |f| &*f);
 
     cx.render(rsx! {
@@ -28,7 +28,7 @@ struct ChildProps<'a> {
     name: &'a str,
 }
 
-fn Child((cx, props): Scope<ChildProps>) -> Element {
+fn Child(cx: Context, props: &ChildProps) -> Element {
     cx.render(rsx! {
         div {
             h1 { "it's nested" }
@@ -42,7 +42,7 @@ struct Grandchild<'a> {
     name: &'a str,
 }
 
-fn Child2((cx, props): Scope<Grandchild>) -> Element {
+fn Child2(cx: Context, props: &Grandchild) -> Element {
     cx.render(rsx! {
         div { "Hello {props.name}!" }
     })

+ 22 - 21
packages/core/tests/create_dom.rs

@@ -21,7 +21,7 @@ fn new_dom<P: 'static + Send>(app: FC<P>, props: P) -> VirtualDom {
 
 #[test]
 fn test_original_diff() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
             div {
                 div {
@@ -57,17 +57,17 @@ fn test_original_diff() {
 
 #[test]
 fn create() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
             div {
                 div {
                     "Hello, world!"
                     div {
                         div {
-                            // Fragment {
-                            //     "hello"
-                            //     "world"
-                            // }
+                            Fragment {
+                                "hello"
+                                "world"
+                            }
                         }
                     }
                 }
@@ -120,7 +120,7 @@ fn create() {
 
 #[test]
 fn create_list() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
             {(0..3).map(|f| rsx!{ div {
                 "hello"
@@ -169,7 +169,7 @@ fn create_list() {
 
 #[test]
 fn create_simple() {
-    static APP: FC<()> = |(cx, props)| {
+    static APP: FC<()> = |cx, props| {
         cx.render(rsx! {
             div {}
             div {}
@@ -207,7 +207,7 @@ fn create_simple() {
 }
 #[test]
 fn create_components() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
             Child { "abc1" }
             Child { "abc2" }
@@ -220,7 +220,7 @@ fn create_components() {
         children: ScopeChildren<'a>,
     }
 
-    fn Child<'a>((cx, props): Scope<'a, ChildProps<'a>>) -> Element {
+    fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element {
         cx.render(rsx! {
             h1 {}
             div { {&props.children} }
@@ -273,7 +273,7 @@ fn create_components() {
 }
 #[test]
 fn anchors() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
             {true.then(|| rsx!{ div { "hello" } })}
             {false.then(|| rsx!{ div { "goodbye" } })}
@@ -302,17 +302,18 @@ fn anchors() {
 
 #[test]
 fn suspended() {
-    static App: FC<()> = |(cx, props)| {
-        let val = use_suspense(cx, || async {}, |p| todo!());
+    todo!()
+    // static App: FC<()> = |cx, props| {
+    //     let val = use_suspense(cx, || async {}, |p| todo!());
 
-        cx.render(rsx! { {val} })
-    };
+    //     cx.render(rsx! { {val} })
+    // };
 
-    let mut dom = new_dom(App, ());
-    let mutations = dom.rebuild();
+    // let mut dom = new_dom(App, ());
+    // let mutations = dom.rebuild();
 
-    assert_eq!(
-        mutations.edits,
-        [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
-    );
+    // assert_eq!(
+    //     mutations.edits,
+    //     [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
+    // );
 }

+ 2 - 4
packages/core/tests/display_vdom.rs

@@ -13,7 +13,7 @@ mod test_logging;
 
 #[test]
 fn please_work() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
             div {
                 hidden: "true"
@@ -27,7 +27,7 @@ fn please_work() {
         })
     };
 
-    static Child: FC<()> = |(cx, props)| {
+    static Child: FC<()> = |cx, props| {
         cx.render(rsx! {
             div { "child" }
         })
@@ -35,6 +35,4 @@ fn please_work() {
 
     let mut dom = VirtualDom::new(App);
     dom.rebuild();
-
-    println!("{}", dom);
 }

+ 2 - 2
packages/core/tests/lifecycle.rs

@@ -20,7 +20,7 @@ fn manual_diffing() {
         value: Shared<&'static str>,
     }
 
-    static App: FC<AppProps> = |(cx, props)| {
+    static App: FC<AppProps> = |cx, props| {
         let val = props.value.lock().unwrap();
         cx.render(rsx! { div { "{val}" } })
     };
@@ -37,7 +37,7 @@ fn manual_diffing() {
 
     *value.lock().unwrap() = "goodbye";
 
-    let edits = dom.diff();
+    let edits = dom.rebuild();
 
     log::debug!("edits: {:?}", edits);
 }

+ 2 - 2
packages/core/tests/sharedstate.rs

@@ -13,12 +13,12 @@ mod test_logging;
 fn shared_state_test() {
     struct MySharedState(&'static str);
 
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.provide_state(MySharedState("world!"));
         cx.render(rsx!(Child {}))
     };
 
-    static Child: FC<()> = |(cx, props)| {
+    static Child: FC<()> = |cx, props| {
         let shared = cx.consume_state::<MySharedState>()?;
         cx.render(rsx!("Hello, {shared.0}"))
     };

+ 15 - 14
packages/core/tests/vdom_rebuild.rs

@@ -17,7 +17,7 @@ use dioxus_html as dioxus_elements;
 
 #[test]
 fn app_runs() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         //
         cx.render(rsx!( div{"hello"} ))
     };
@@ -28,7 +28,7 @@ fn app_runs() {
 
 #[test]
 fn fragments_work() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
             div{"hello"}
             div{"goodbye"}
@@ -42,7 +42,7 @@ fn fragments_work() {
 
 #[test]
 fn lists_work() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
             h1 {"hello"}
             {(0..6).map(|f| rsx!(span{ "{f}" }))}
@@ -55,7 +55,7 @@ fn lists_work() {
 
 #[test]
 fn conditional_rendering() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
             h1 {"hello"}
             {true.then(|| rsx!(span{ "a" }))}
@@ -72,13 +72,13 @@ fn conditional_rendering() {
 
 #[test]
 fn child_components() {
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx!(
             {true.then(|| rsx!(Child { }))}
             {false.then(|| rsx!(Child { }))}
         ))
     };
-    static Child: FC<()> = |(cx, props)| {
+    static Child: FC<()> = |cx, props| {
         cx.render(rsx!(
             h1 {"hello"}
             h1 {"goodbye"}
@@ -91,13 +91,14 @@ fn child_components() {
 
 #[test]
 fn suspended_works() {
-    static App: FC<()> = |(cx, props)| {
-        let title = use_suspense(cx, || async { "bob" }, move |cx, f| todo!());
-        // let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
-        cx.render(rsx!("hello" { title }))
-    };
+    todo!()
+    // static App: FC<()> = |cx, props| {
+    //     let title = use_suspense(cx, || async { "bob" }, move |cx, f| todo!());
+    //     // let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
+    //     cx.render(rsx!("hello" { title }))
+    // };
 
-    let mut vdom = VirtualDom::new(App);
-    let edits = vdom.rebuild();
-    dbg!(edits);
+    // let mut vdom = VirtualDom::new(App);
+    // let edits = vdom.rebuild();
+    // dbg!(edits);
 }