Browse Source

fix: messed up how lifetimes worked, need to render once per component

Jonathan Kelley 3 years ago
parent
commit
ba9e1db

+ 4 - 4
examples/borrowed.rs

@@ -20,7 +20,7 @@ fn main() {
     dioxus::desktop::launch(App, |c| c);
 }
 
-fn App((cx, props): Component<()>) -> Element {
+fn App((cx, props): Scope<()>) -> Element {
     let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f, |_| {});
 
     let first = text.get_mut(0).unwrap();
@@ -43,7 +43,7 @@ impl<'a> Drop for C1Props<'a> {
     fn drop(&mut self) {}
 }
 
-fn Child1<'a>((cx, props): Component<'a, C1Props>) -> Element<'a> {
+fn Child1<'a>((cx, props): Scope<'a, C1Props>) -> Element<'a> {
     let (left, right) = props.text.split_once("=").unwrap();
 
     cx.render(rsx! {
@@ -59,7 +59,7 @@ struct C2Props<'a> {
     text: &'a str,
 }
 
-fn Child2<'a>((cx, props): Component<'a, C2Props>) -> Element<'a> {
+fn Child2<'a>((cx, props): Scope<'a, C2Props>) -> Element<'a> {
     cx.render(rsx! {
         Child3 {
             text: props.text
@@ -72,7 +72,7 @@ struct C3Props<'a> {
     text: &'a str,
 }
 
-fn Child3<'a>((cx, props): Component<'a, C3Props>) -> Element<'a> {
+fn Child3<'a>((cx, props): Scope<'a, C3Props>) -> Element<'a> {
     cx.render(rsx! {
         div { "{props.text}"}
     })

+ 1 - 1
examples/calculator.rs

@@ -116,7 +116,7 @@ struct CalculatorKeyProps<'a> {
     onclick: &'a dyn Fn(MouseEvent),
 }
 
-fn CalculatorKey<'a>((cx, props): Component<'a, CalculatorKeyProps>) -> Element<'a> {
+fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps>) -> Element<'a> {
     rsx!(cx, button {
         class: "calculator-key {props.name}"
         onclick: {props.onclick}

+ 2 - 2
examples/framework_benchmark.rs

@@ -80,7 +80,7 @@ struct ActionButtonProps<'a> {
     onclick: &'a dyn Fn(MouseEvent),
 }
 
-fn ActionButton<'a>((cx, props): Component<'a, ActionButtonProps>) -> Element<'a> {
+fn ActionButton<'a>((cx, props): Scope<'a, ActionButtonProps>) -> Element<'a> {
     rsx!(cx, div { class: "col-sm-6 smallpad"
         button { class:"btn btn-primary btn-block", r#type: "button", id: "{props.id}",  onclick: {props.onclick},
             "{props.name}"
@@ -93,7 +93,7 @@ struct RowProps {
     row_id: usize,
     label: Rc<str>,
 }
-fn Row((cx, props): Component<RowProps>) -> Element {
+fn Row((cx, props): Scope<RowProps>) -> Element {
     rsx!(cx, tr {
         td { class:"col-md-1", "{props.row_id}" }
         td { class:"col-md-1", onclick: move |_| { /* run onselect */ }

+ 1 - 1
examples/hello_world.rs

@@ -4,7 +4,7 @@ fn main() {
     dioxus::desktop::launch(App, |c| c);
 }
 
-fn App((cx, props): Component<()>) -> Element {
+fn App((cx, props): Scope<()>) -> Element {
     cx.render(rsx! (
         div { "Hello, world!" }
     ))

+ 1 - 1
examples/pattern_model.rs

@@ -76,7 +76,7 @@ struct CalculatorKeyProps<'a> {
     onclick: &'a dyn Fn(MouseEvent),
 }
 
-fn CalculatorKey<'a>((cx, props): Component<'a, CalculatorKeyProps>) -> Element<'a> {
+fn CalculatorKey<'a>((cx, props): Scope<'a, CalculatorKeyProps>) -> Element<'a> {
     cx.render(rsx! {
         button {
             class: "calculator-key {props.name}"

+ 2 - 2
examples/rsx_usage.rs

@@ -184,7 +184,7 @@ mod baller {
     pub struct BallerProps {}
 
     /// This component totally balls
-    pub fn Baller(_: Component<BallerProps>) -> Element {
+    pub fn Baller(_: Scope<BallerProps>) -> Element {
         todo!()
     }
 }
@@ -195,7 +195,7 @@ pub struct TallerProps {
 }
 
 /// This component is taller than most :)
-pub fn Taller(_: Component<TallerProps>) -> Element {
+pub fn Taller(_: Scope<TallerProps>) -> Element {
     let b = true;
     todo!()
 }

+ 1 - 1
examples/todomvc.rs

@@ -85,7 +85,7 @@ pub struct TodoEntryProps {
     todo: Rc<TodoItem>,
 }
 
-pub fn TodoEntry((cx, props): Component<TodoEntryProps>) -> Element {
+pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
     let is_editing = use_state(cx, || false);
     let contents = use_state(cx, || String::from(""));
     let todo = &props.todo;

+ 1 - 1
examples/web_tick.rs

@@ -45,7 +45,7 @@ struct RowProps {
     row_id: usize,
     label: Label,
 }
-fn Row((cx, props): Component<RowProps>) -> Element {
+fn Row((cx, props): Scope<RowProps>) -> Element {
     let [adj, col, noun] = props.label.0;
     cx.render(rsx! {
         tr {

+ 2 - 2
packages/core-macro/src/rsx/body.rs

@@ -57,11 +57,11 @@ impl ToTokens for CallBody {
             }),
             // Otherwise we just build the LazyNode wrapper
             None => out_tokens.append_all(quote! {
-                Some(dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
+                dioxus::prelude::LazyNodes::new(move |__cx: NodeFactory|{
                     use dioxus_elements::{GlobalAttributes, SvgAttributes};
 
                     #inner
-                 }))
+                 })
             }),
         };
     }

+ 0 - 3
packages/core/.vscode/settings.json

@@ -1,3 +0,0 @@
-{
-  "rust-analyzer.inlayHints.enable": true
-}

+ 0 - 2
packages/core/Cargo.toml

@@ -42,8 +42,6 @@ serde = { version = "1", features = ["derive"], optional = true }
 
 serde_repr = { version = "0.1.7", optional = true }
 
-stack_dst = "0.6.1"
-
 [dev-dependencies]
 anyhow = "1.0.42"
 dioxus-html = { path = "../html" }

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

@@ -34,13 +34,13 @@ fn create_rows(c: &mut Criterion) {
                 }
             }
         });
-        rsx! {
+        cx.render(rsx! {
             table {
                 tbody {
                     {rows}
                 }
             }
-        }
+        })
     };
 
     c.bench_function("create rows", |b| {
@@ -57,9 +57,9 @@ struct RowProps {
     row_id: usize,
     label: Label,
 }
-fn Row((cx, props): Component<RowProps>) -> Element {
+fn Row((cx, props): Scope<RowProps>) -> Element {
     let [adj, col, noun] = props.label.0;
-    rsx! {
+    cx.render(rsx! {
         tr {
             td { class:"col-md-1", "{props.row_id}" }
             td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
@@ -72,7 +72,7 @@ fn Row((cx, props): Component<RowProps>) -> Element {
             }
             td { class: "col-md-6" }
         }
-    }
+    })
 }
 
 #[derive(PartialEq)]

+ 1 - 1
packages/core/examples/alternative.rs

@@ -9,7 +9,7 @@ fn main() {
 pub static EXAMPLE: FC<()> = |(cx, _)| {
     let list = (0..10).map(|_f| LazyNodes::new(move |_f| todo!()));
 
-    Some(LazyNodes::new(move |cx| {
+    cx.render(LazyNodes::new(move |cx| {
         cx.raw_element(
             "div",
             None,

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

@@ -23,7 +23,7 @@ struct ListItem {
 fn app<'a>(cx: Context<'a>, props: &AppProps) -> Element<'a> {
     // let (val, set_val) = use_state_classic(cx, || 0);
 
-    Some(LazyNodes::new(move |_nodecx| {
+    cx.render(LazyNodes::new(move |_nodecx| {
         todo!()
         // builder::ElementBuilder::new(_nodecx, "div")
         //     .iter_child({
@@ -57,7 +57,7 @@ struct ChildProps {
 }
 
 fn ChildItem<'a>(cx: Context<'a>, props: &'a ChildProps) -> Element<'a> {
-    Some(LazyNodes::new(move |__cx| todo!()))
+    cx.render(LazyNodes::new(move |__cx| todo!()))
 }
 
 impl PartialEq for ChildProps {

+ 1 - 1
packages/core/examples/fragment_from_iter.rs

@@ -26,7 +26,7 @@ fn app<'a>(cx: Context<'a>, props: &()) -> Element<'a> {
         )
     }));
 
-    Some(LazyNodes::new(move |f| {
+    cx.render(LazyNodes::new(move |f| {
         f.raw_element(
             "div",
             None,

+ 7 - 7
packages/core/examples/jsframework.rs

@@ -1,6 +1,6 @@
 #![allow(non_snake_case)]
 
-use dioxus::component::Component;
+use dioxus::component::Scope;
 use dioxus::events::on::MouseEvent;
 use dioxus_core as dioxus;
 use dioxus_core::prelude::*;
@@ -15,7 +15,7 @@ fn main() {
     assert!(g.edits.len() > 1);
 }
 
-fn App((cx, props): Component<()>) -> Element {
+fn App((cx, props): Scope<()>) -> Element {
     let mut rng = SmallRng::from_entropy();
     let rows = (0..10_000_usize).map(|f| {
         let label = Label::new(&mut rng);
@@ -26,13 +26,13 @@ fn App((cx, props): Component<()>) -> Element {
             }
         }
     });
-    rsx! {
+    cx.render(rsx! {
         table {
             tbody {
                 {rows}
             }
         }
-    }
+    })
 }
 
 #[derive(PartialEq, Props)]
@@ -41,11 +41,11 @@ struct RowProps {
     label: Label,
 }
 
-fn Row((cx, props): Component<RowProps>) -> Element {
+fn Row((cx, props): Scope<RowProps>) -> Element {
     let handler = move |evt: MouseEvent| {
         let g = evt.button;
     };
-    rsx! {
+    cx.render(rsx! {
         tr {
             td { class:"col-md-1", "{props.row_id}" }
             td { class:"col-md-1", onclick: move |_| { /* run onselect */ }
@@ -58,7 +58,7 @@ fn Row((cx, props): Component<RowProps>) -> Element {
             }
             td { class: "col-md-6" }
         }
-    }
+    })
 }
 
 #[derive(PartialEq)]

+ 91 - 11
packages/core/examples/syntax.rs

@@ -1,5 +1,8 @@
-use dioxus::component::Component;
+use std::marker::PhantomData;
+
+use dioxus::component::Scope;
 use dioxus::events::on::MouseEvent;
+use dioxus::nodes::{IntoVNode, IntoVNodeList};
 use dioxus_core as dioxus;
 use dioxus_core::prelude::*;
 use dioxus_core_macro::*;
@@ -20,20 +23,97 @@ fn html_usage() {
 
     let items = ["bob", "bill", "jack"];
 
-    let f = items.iter().filter(|f| f.starts_with("b")).map(|f| {
-        rsx! {
-            "hello {f}"
-        }
-    });
+    let f = items
+        .iter()
+        .filter(|f| f.starts_with('b'))
+        .map(|f| rsx!("hello {f}"));
+
+    let p = rsx!(div { {f} });
+}
 
+static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
+
+static App: FC<()> = |(cx, props)| {
+    let name = cx.use_state(|| 0);
+
+    cx.render(rsx!(div {
+        h1 {}
+        h2 {}
+    }))
+};
+
+pub trait UseState<'a, T: 'static> {
+    fn use_state(self, f: impl FnOnce() -> T) -> &'a T;
+}
+impl<'a, T: 'static> UseState<'a, T> for Context<'a> {
+    fn use_state(self, f: impl FnOnce() -> T) -> &'a T {
+        todo!()
+    }
+}
+
+fn App3((cx, props): Scope<()>) -> Element {
     let p = rsx! {
-        div {
-            {f}
+        Child {
+            bame: 10,
         }
     };
+    cx.render(rsx!(Child {
+        bame: 102,
+        ..ChildProps { bame: 10 }
+    }))
 }
 
-static App: FC<()> = |(cx, props)| {
-    //
-    rsx!(div {})
+#[derive(Props, PartialEq, Debug)]
+struct ChildProps {
+    bame: i32, // children: Children<'a>,
+}
+
+fn Child<'a>((cx, props): Scope<'a, ChildProps>) -> Element<'a> {
+    cx.render(rsx!(div {
+        // {props.children}
+    }))
+}
+
+// Some(LazyNodes::new(|f| {
+//     //
+//     // let r = f.fragment_from_iter(&props.children);
+//     r
+//     // todo!()
+// }))
+// todo!()
+// rsx!({ Some(p) })
+// todo!()
+
+pub struct Children<'a> {
+    children: VNode<'static>,
+    _p: PhantomData<&'a ()>,
+}
+
+impl<'a> Children<'a> {
+    pub fn new(children: VNode<'a>) -> Self {
+        Self {
+            children: unsafe { std::mem::transmute(children) },
+            _p: PhantomData,
+        }
+    }
+}
+impl<'a> IntoVNodeList<'a> for &Children<'a> {
+    fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>] {
+        todo!()
+    }
+}
+
+static Bapp: FC<()> = |(cx, props)| {
+    let name = cx.use_state(|| 0);
+
+    cx.render(rsx!(
+        div {
+            div {
+
+            }
+            div {
+
+            }
+        }
+    ))
 };

+ 1 - 1
packages/core/examples/vdom_usage.rs

@@ -4,7 +4,7 @@ use dioxus_core::prelude::*;
 
 #[async_std::main]
 async fn main() {
-    static App: FC<()> = |(cx, props)| Some(LazyNodes::new(|f| f.text(format_args!("hello"))));
+    static App: FC<()> = |(cx, props)| cx.render(LazyNodes::new(|f| f.text(format_args!("hello"))));
 
     let mut dom = VirtualDom::new(App);
 

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

@@ -42,7 +42,7 @@ use crate::{
 /// };
 /// ```
 ///
-pub type Component<'a, T> = (Context<'a>, &'a T);
+pub type Scope<'a, T> = (Context<'a>, &'a T);
 
 /// Create inline fragments using Component syntax.
 ///
@@ -66,8 +66,8 @@ pub type Component<'a, T> = (Context<'a>, &'a T);
 /// 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((cx, _): Component<()>) -> Element {
-    Some(LazyNodes::new(|f| f.fragment_from_iter(cx.children())))
+pub fn Fragment((cx, _): Scope<()>) -> Element {
+    cx.render(LazyNodes::new(move |f| f.fragment_from_iter(cx.children())))
 }
 
 /// Every "Props" used for a component must implement the `Properties` trait. This trait gives some hints to Dioxus

+ 6 - 3
packages/core/src/context.rs

@@ -32,7 +32,7 @@ use std::{any::TypeId, ops::Deref, rc::Rc};
 /// }
 /// ```
 pub struct Context<'src> {
-    pub scope: &'src Scope,
+    pub scope: &'src ScopeInner,
 }
 
 impl<'src> Copy for Context<'src> {}
@@ -45,7 +45,7 @@ impl<'src> Clone for Context<'src> {
 // We currently deref to props, but it might make more sense to deref to Scope?
 // This allows for code that takes cx.xyz instead of cx.props.xyz
 impl<'a> Deref for Context<'a> {
-    type Target = &'a Scope;
+    type Target = &'a ScopeInner;
     fn deref(&self) -> &Self::Target {
         &self.scope
     }
@@ -137,7 +137,10 @@ impl<'src> Context<'src> {
     ///     cx.render(lazy_tree)
     /// }
     ///```
-    pub fn render(self, lazy_nodes: LazyNodes<'src>) -> Option<VNode<'src>> {
+    pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
+        self,
+        lazy_nodes: LazyNodes<'src, F>,
+    ) -> Option<VNode<'src>> {
         let bump = &self.scope.frames.wip_frame().bump;
         Some(lazy_nodes.into_vnode(NodeFactory { bump }))
     }

+ 2 - 2
packages/core/src/diff.rs

@@ -342,7 +342,7 @@ impl<'bump> DiffMachine<'bump> {
         let parent_scope = self.vdom.get_scope(parent_idx).unwrap();
 
         let new_idx = self.vdom.insert_scope_with_key(|new_idx| {
-            Scope::new(
+            ScopeInner::new(
                 caller,
                 new_idx,
                 Some(parent_idx),
@@ -1161,7 +1161,7 @@ impl<'bump> DiffMachine<'bump> {
     }
 
     /// Adds a listener closure to a scope during diff.
-    fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &Scope) {
+    fn attach_listener_to_scope<'a>(&mut self, listener: &'a Listener<'a>, scope: &ScopeInner) {
         let mut queue = scope.listeners.borrow_mut();
         let long_listener: &'a Listener<'static> = unsafe { std::mem::transmute(listener) };
         queue.push(long_listener as *const _)

+ 32 - 29
packages/core/src/hooks.rs

@@ -135,32 +135,35 @@ where
             None => {
                 let value = hook.value.clone();
 
-                Some(LazyNodes::new(|f| {
-                    let bump = f.bump();
-
-                    use bumpalo::boxed::Box as BumpBox;
-
-                    let f: &mut dyn FnMut(SuspendedContext<'src>) -> Element<'src> =
-                        bump.alloc(move |sus| {
-                            let val = value.borrow();
-
-                            let out = val
-                                .as_ref()
-                                .unwrap()
-                                .as_ref()
-                                .downcast_ref::<Out>()
-                                .unwrap();
-
-                            user_callback(sus, out)
-                        });
-                    let callback = unsafe { BumpBox::from_raw(f) };
-
-                    VNode::Suspended(bump.alloc(VSuspended {
-                        dom_id: empty_cell(),
-                        task_id: hook.handle.our_id,
-                        callback: RefCell::new(Some(callback)),
-                    }))
-                }))
+                let id = hook.handle.our_id;
+
+                todo!()
+                // Some(LazyNodes::new(move |f| {
+                //     let bump = f.bump();
+
+                //     use bumpalo::boxed::Box as BumpBox;
+
+                //     let f: &mut dyn FnMut(SuspendedContext<'src>) -> Element<'src> =
+                //         bump.alloc(move |sus| {
+                //             let val = value.borrow();
+
+                //             let out = val
+                //                 .as_ref()
+                //                 .unwrap()
+                //                 .as_ref()
+                //                 .downcast_ref::<Out>()
+                //                 .unwrap();
+
+                //             user_callback(sus, out)
+                //         });
+                //     let callback = unsafe { BumpBox::from_raw(f) };
+
+                //     VNode::Suspended(bump.alloc(VSuspended {
+                //         dom_id: empty_cell(),
+                //         task_id: id,
+                //         callback: RefCell::new(Some(callback)),
+                //     }))
+                // }))
             }
         },
         |_| {},
@@ -177,10 +180,10 @@ pub struct SuspendedContext<'a> {
 }
 
 impl<'src> SuspendedContext<'src> {
-    pub fn render(
-        // pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
+    // pub fn render(
+    pub fn render<F: FnOnce(NodeFactory<'src>) -> VNode<'src>>(
         self,
-        lazy_nodes: LazyNodes<'src>,
+        lazy_nodes: LazyNodes<'src, F>,
     ) -> Element<'src> {
         let bump = &self.inner.scope.frames.wip_frame().bump;
         todo!("suspense")

+ 113 - 0
packages/core/src/lazynodes.rs

@@ -0,0 +1,113 @@
+use std::marker::PhantomData;
+
+/*
+Remember: calls to rsx! are lazy - they are not evaluated immediately.
+
+They also using dynamic dispatch, so we can return multiple rsx!'s from match statements and such.
+
+If we allocated every rsx! call on the heap, it would be quite wasteful. Rsx! calls are FnOnce, so they can be stored in a stack.
+
+Solutions like stackdst are useful, but they only support 'static closures.
+
+All our closures are bound by the bump lifetime, so stack-dst will not work for us
+
+Our solution is to try and manually allocate the closure onto the stack.
+If it fails, then we default to Box.
+
+*/
+use crate::innerlude::{IntoVNode, NodeFactory, VNode};
+
+/// A concrete type provider for closures that build VNode structures.
+///
+/// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
+/// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
+///
+///
+/// ```rust
+/// LazyNodes::new(|f| f.element("div", [], [], [] None))
+/// ```
+pub struct LazyNodes<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> {
+    inner: Box<F>,
+    _p: PhantomData<&'a ()>,
+    // inner: StackNodeStorage<'a>,
+    // inner: StackNodeStorage<'a>,
+}
+
+type StackHeapSize = [usize; 12];
+
+enum StackNodeStorage<'a> {
+    Stack {
+        next_ofs: usize,
+        buf: StackHeapSize,
+        width: usize,
+    },
+    Heap(Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>>),
+}
+
+impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> LazyNodes<'a, F> {
+    pub fn new(f: F) -> Self {
+        // let width = std::mem?::size_of::<F>();
+        // let b: Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>> = Box::new(f);
+
+        todo!()
+        // Self { inner: b }
+        // todo!()
+
+        // if width > std::mem::size_of::<StackHeapSize>() {
+        //     let g: Box<dyn for<'b> FnOnce(NodeFactory<'b>) -> VNode<'b> + 'g> = Box::new(f);
+        //     LazyNodes {
+        //         inner: StackNodeStorage::Heap(g),
+        //     }
+        // } else {
+        //     let mut buf = [0; 12];
+        //     let mut next_ofs = 0;
+        //     next_ofs += 1;
+        //     LazyNodes {
+        //         inner: StackNodeStorage::Stack {
+        //             next_ofs,
+        //             buf,
+        //             width,
+        //         },
+        //     }
+        // }
+    }
+}
+
+// Our blanket impl
+impl<'a, F> IntoIterator for LazyNodes<'a, F>
+where
+    F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+{
+    type Item = Self;
+    type IntoIter = std::iter::Once<Self::Item>;
+    fn into_iter(self) -> Self::IntoIter {
+        std::iter::once(self)
+    }
+}
+
+// Our blanket impl
+impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> IntoVNode<'a> for LazyNodes<'a, F> {
+    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
+        todo!()
+        // match self.inner {
+        //     StackNodeStorage::Stack {
+        //         buf,
+        //         next_ofs,
+        //         width,
+        //     } => {
+        //         // get the start of the allocation
+        //         let r = &buf[0];
+
+        //         // recast the allocation as dyn FnOnce
+
+        //         // pretend the FnOnce is box
+        //         let g: Box<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>> = todo!();
+        //         // Box::from_raw(r as *const usize as *mut dyn FnOnce(NodeFactory<'a>));
+
+        //         // use Box's ability to act as FnOnce
+        //         g(cx)
+        //     }
+        //     StackNodeStorage::Heap(b) => b(cx),
+        // }
+    }
+}

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

@@ -23,6 +23,7 @@ pub mod events;
 pub mod heuristics;
 pub mod hooklist;
 pub mod hooks;
+pub mod lazynodes;
 pub mod mutations;
 pub mod nodes;
 pub mod resources;
@@ -45,6 +46,7 @@ pub(crate) mod innerlude {
     pub use crate::heuristics::*;
     pub(crate) use crate::hooklist::*;
     pub use crate::hooks::*;
+    pub use crate::lazynodes::LazyNodes;
     pub use crate::mutations::*;
     pub use crate::nodes::*;
     pub(crate) use crate::resources::*;
@@ -55,10 +57,9 @@ pub(crate) mod innerlude {
     pub use crate::threadsafe::*;
     pub use crate::util::*;
     pub use crate::virtual_dom::*;
-
     // pub type Element<'a> = Option<VNode<'a>>;
-    pub type Element<'a> = Option<LazyNodes<'a>>;
-    pub type FC<P> = for<'a> fn(Component<'a, P>) -> Element<'a>;
+    pub type Element<'a> = Option<VNode<'a>>;
+    pub type FC<P> = for<'a> fn(Scope<'a, P>) -> Element<'a>;
 }
 
 pub use crate::innerlude::{
@@ -68,7 +69,7 @@ pub use crate::innerlude::{
 };
 
 pub mod prelude {
-    pub use crate::component::{fc_to_builder, Component, Fragment, Properties};
+    pub use crate::component::{fc_to_builder, Fragment, Properties, Scope};
     pub use crate::context::Context;
     pub use crate::hooks::*;
     pub use crate::innerlude::{DioxusElement, Element, LazyNodes, Mutations, NodeFactory, FC};

+ 10 - 67
packages/core/src/nodes.rs

@@ -4,7 +4,8 @@
 //! cheap and *very* fast to construct - building a full tree should be quick.
 
 use crate::innerlude::{
-    empty_cell, Context, Element, ElementId, Properties, Scope, ScopeId, SuspendedContext, FC,
+    empty_cell, Context, Element, ElementId, LazyNodes, Properties, ScopeId, ScopeInner,
+    SuspendedContext, FC,
 };
 use bumpalo::{boxed::Box as BumpBox, Bump};
 use std::{
@@ -13,10 +14,6 @@ use std::{
     marker::PhantomData,
 };
 
-use stack_dst::Value as StackDST;
-
-pub type LazyNodeFactory<'a> = StackDST<dyn FnOnce(NodeFactory<'a>) -> VNode<'a>>;
-
 /// A composable "VirtualNode" to declare a User Interface in the Dioxus VirtualDOM.
 ///
 /// VNodes are designed to be lightweight and used with with a bump allocator. To create a VNode, you can use either of:
@@ -291,7 +288,7 @@ pub struct VComponent<'src> {
     // Function pointer to the FC that was used to generate this component
     pub user_fc: *const (),
 
-    pub(crate) caller: &'src dyn for<'b> Fn(&'b Scope) -> Element<'b>,
+    pub(crate) caller: &'src dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
 
     pub(crate) children: &'src [VNode<'src>],
 
@@ -332,11 +329,11 @@ impl<'a> NodeFactory<'a> {
         self.bump
     }
 
-    pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a>) -> Option<VNode<'a>>
-// pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> Element<'a>
-    // where
-    //     F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
+    pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> Element<'a>
+    where
+        F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
     {
+        // pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a>) -> Option<VNode<'a>> {
         Some(lazy_nodes.into_vnode(NodeFactory { bump: self.bump }))
     }
 
@@ -531,8 +528,8 @@ impl<'a> NodeFactory<'a> {
 
         let key = key.map(|f| self.raw_text(f).0);
 
-        let caller: &'a mut dyn for<'b> Fn(&'b Scope) -> Element<'b> =
-            bump.alloc(move |scope: &Scope| -> Element {
+        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 res: Element = component((Context { scope }, props));
@@ -687,60 +684,6 @@ impl<'a> IntoVNode<'a> for VNode<'a> {
     }
 }
 
-/// A concrete type provider for closures that build VNode structures.
-///
-/// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
-/// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
-///
-///
-/// ```rust
-/// LazyNodes::new(|f| f.element("div", [], [], [] None))
-/// ```
-pub struct LazyNodes<'a> {
-    inner: LazyNodeFactory<'a>,
-}
-// where
-//     G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-
-impl<'a> LazyNodes<'a>
-// where
-//     G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    pub fn new(f: impl FnOnce(NodeFactory<'a>) -> VNode<'a>) -> Self {
-        todo!()
-        // pub fn new<G: FnOnce(NodeFactory<'a>) -> VNode<'a>>(f: G) -> Self {
-        // let inner = LazyNodeFactory::new(f);
-        // Self { inner: f }
-    }
-}
-
-// Our blanket impl
-impl<'a> IntoVNode<'a> for LazyNodes<'a>
-// where
-//     G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
-        // let f = self.inner;
-        todo!("manually drop here")
-        // (self.inner)(cx)
-    }
-}
-
-// Our blanket impl
-impl<'a> IntoIterator for LazyNodes<'a>
-// where
-//     G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-// impl<'a, G> IntoIterator for LazyNodes<'a, G>
-// where
-//     G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
-{
-    type Item = Self;
-    type IntoIter = std::iter::Once<Self::Item>;
-    fn into_iter(self) -> Self::IntoIter {
-        std::iter::once(self)
-    }
-}
-
 // Conveniently, we also support "null" (nothing) passed in
 impl IntoVNode<'_> for () {
     fn into_vnode(self, cx: NodeFactory) -> VNode {
@@ -764,7 +707,7 @@ impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
     }
 }
 
-impl<'a> IntoVNode<'a> for Option<LazyNodes<'a>> {
+impl<'a, F: FnOnce(NodeFactory<'a>) -> VNode<'a>> IntoVNode<'a> for Option<LazyNodes<'a, F>> {
     fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
         match self {
             Some(n) => n.into_vnode(cx),

+ 5 - 5
packages/core/src/resources.rs

@@ -15,7 +15,7 @@ pub(crate) struct ResourcePool {
 
     Wrapped in Rc so the "get_shared_context" closure can walk the tree (immutably!)
     */
-    pub components: Rc<UnsafeCell<Slab<Scope>>>,
+    pub components: Rc<UnsafeCell<Slab<ScopeInner>>>,
 
     /*
     Yes, a slab of "nil". We use this for properly ordering ElementIDs - all we care about is the allocation strategy
@@ -32,18 +32,18 @@ pub(crate) struct ResourcePool {
 
 impl ResourcePool {
     /// this is unsafe because the caller needs to track which other scopes it's already using
-    pub fn get_scope(&self, idx: ScopeId) -> Option<&Scope> {
+    pub fn get_scope(&self, idx: ScopeId) -> Option<&ScopeInner> {
         let inner = unsafe { &*self.components.get() };
         inner.get(idx.0)
     }
 
     /// this is unsafe because the caller needs to track which other scopes it's already using
-    pub fn get_scope_mut(&self, idx: ScopeId) -> Option<&mut Scope> {
+    pub fn get_scope_mut(&self, idx: ScopeId) -> Option<&mut ScopeInner> {
         let inner = unsafe { &mut *self.components.get() };
         inner.get_mut(idx.0)
     }
 
-    pub fn try_remove(&self, id: ScopeId) -> Option<Scope> {
+    pub fn try_remove(&self, id: ScopeId) -> Option<ScopeInner> {
         let inner = unsafe { &mut *self.components.get() };
         Some(inner.remove(id.0))
         // .try_remove(id.0)
@@ -67,7 +67,7 @@ impl ResourcePool {
         els.remove(id.0);
     }
 
-    pub fn insert_scope_with_key(&self, f: impl FnOnce(ScopeId) -> Scope) -> ScopeId {
+    pub fn insert_scope_with_key(&self, f: impl FnOnce(ScopeId) -> ScopeInner) -> ScopeId {
         let g = unsafe { &mut *self.components.get() };
         let entry = g.vacant_entry();
         let id = ScopeId(entry.key());

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

@@ -214,7 +214,7 @@ impl Scheduler {
                 let components = components.clone();
                 Rc::new(move |id, ty| {
                     let components = unsafe { &*components.get() };
-                    let mut search: Option<&Scope> = components.get(id.0);
+                    let mut search: Option<&ScopeInner> = components.get(id.0);
                     while let Some(inner) = search.take() {
                         if let Some(shared) = inner.shared_contexts.borrow().get(&ty) {
                             return Some(shared.clone());

+ 8 - 8
packages/core/src/scope.rs

@@ -18,7 +18,7 @@ use std::{
 ///
 /// We expose the `Scope` type so downstream users can traverse the Dioxus VirtualDOM for whatever
 /// use case they might have.
-pub struct Scope {
+pub struct ScopeInner {
     // Book-keeping about our spot in the arena
     pub(crate) parent_idx: Option<ScopeId>,
     pub(crate) our_arena_idx: ScopeId,
@@ -28,7 +28,7 @@ pub struct Scope {
 
     // Nodes
     pub(crate) frames: ActiveFrame,
-    pub(crate) caller: *const dyn for<'b> Fn(&'b Scope) -> Element<'b>,
+    pub(crate) caller: *const dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
     pub(crate) child_nodes: ScopeChildren<'static>,
 
     /*
@@ -55,7 +55,7 @@ pub struct Scope {
 }
 
 /// Public interface for Scopes.
-impl Scope {
+impl ScopeInner {
     /// 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.
@@ -167,7 +167,7 @@ impl Scope {
 pub type FiberTask = Pin<Box<dyn Future<Output = ScopeId>>>;
 
 /// Private interface for Scopes.
-impl Scope {
+impl ScopeInner {
     // we are being created in the scope of an existing component (where the creator_node lifetime comes into play)
     // we are going to break this lifetime by force in order to save it on ourselves.
     // To make sure that the lifetime isn't truly broken, we receive a Weak RC so we can't keep it around after the parent dies.
@@ -176,7 +176,7 @@ impl Scope {
     // Scopes cannot be made anywhere else except for this file
     // Therefore, their lifetimes are connected exclusively to the virtual dom
     pub(crate) fn new(
-        caller: &dyn for<'b> Fn(&'b Scope) -> Element<'b>,
+        caller: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
         our_arena_idx: ScopeId,
         parent_idx: Option<ScopeId>,
         height: u32,
@@ -217,7 +217,7 @@ impl Scope {
 
     pub(crate) fn update_scope_dependencies<'creator_node>(
         &mut self,
-        caller: &'creator_node dyn for<'b> Fn(&'b Scope) -> Element<'b>,
+        caller: &'creator_node dyn for<'b> Fn(&'b ScopeInner) -> Element<'b>,
         child_nodes: ScopeChildren,
     ) {
         log::debug!("Updating scope dependencies {:?}", self.our_arena_idx);
@@ -350,7 +350,7 @@ impl Scope {
         log::debug!("Borrowed stuff is successfully cleared");
 
         // Cast the caller ptr from static to one with our own reference
-        let render: &dyn for<'b> Fn(&'b Scope) -> Element<'b> = unsafe { &*self.caller };
+        let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = unsafe { &*self.caller };
 
         // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
         //
@@ -376,7 +376,7 @@ impl Scope {
 pub(crate) struct ScopeRenderer<'a> {
     pub skip_components: bool,
     pub show_fragments: bool,
-    pub _scope: &'a Scope,
+    pub _scope: &'a ScopeInner,
     pub _pre_render: bool,
     pub _newline: bool,
     pub _indent: bool,

+ 11 - 10
packages/core/src/virtual_dom.rs

@@ -143,15 +143,16 @@ impl VirtualDom {
 
         let _p = root_props.clone();
         // Safety: this callback is only valid for the lifetime of the root props
-        let root_caller: Rc<dyn Fn(&Scope) -> Element> = Rc::new(move |scope: &Scope| unsafe {
-            let props = _p.downcast_ref::<P>().unwrap();
-            std::mem::transmute(root((Context { scope }, props)))
-        });
+        let root_caller: Rc<dyn Fn(&ScopeInner) -> Element> =
+            Rc::new(move |scope: &ScopeInner| unsafe {
+                let props = _p.downcast_ref::<P>().unwrap();
+                std::mem::transmute(root((Context { scope }, props)))
+            });
 
         let scheduler = Scheduler::new(sender, receiver);
 
         let base_scope = scheduler.pool.insert_scope_with_key(|myidx| {
-            Scope::new(
+            ScopeInner::new(
                 root_caller.as_ref(),
                 myidx,
                 None,
@@ -175,12 +176,12 @@ impl VirtualDom {
     ///
     /// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus
     /// directly.
-    pub fn base_scope(&self) -> &Scope {
+    pub fn base_scope(&self) -> &ScopeInner {
         self.scheduler.pool.get_scope(self.base_scope).unwrap()
     }
 
     /// Get the [`Scope`] for a component given its [`ScopeId`]
-    pub fn get_scope(&self, id: ScopeId) -> Option<&Scope> {
+    pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeInner> {
         self.scheduler.pool.get_scope(id)
     }
 
@@ -219,8 +220,8 @@ impl VirtualDom {
 
             let root = *self.root_fc.downcast_ref::<FC<P>>().unwrap();
 
-            let root_caller: Box<dyn Fn(&Scope) -> Element> =
-                Box::new(move |scope: &Scope| unsafe {
+            let root_caller: Box<dyn Fn(&ScopeInner) -> Element> =
+                Box::new(move |scope: &ScopeInner| unsafe {
                     let props: &'_ P = &*(props_ptr as *const P);
                     std::mem::transmute(root((Context { scope }, props)))
                 });
@@ -410,4 +411,4 @@ impl std::fmt::Display for VirtualDom {
 }
 
 // we never actually use the contents of this root caller
-struct RootCaller(Rc<dyn for<'b> Fn(&'b Scope) -> Element<'b> + 'static>);
+struct RootCaller(Rc<dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> + 'static>);

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

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

+ 1 - 1
packages/core/tests/vdom_rebuild.rs

@@ -92,7 +92,7 @@ fn child_components() {
 #[test]
 fn suspended_works() {
     static App: FC<()> = |(cx, props)| {
-        let title = use_suspense(cx, || async { "bob" }, |cx, f| rsx! { "{f}"});
+        let title = use_suspense(cx, || async { "bob" }, move |cx, f| rsx! { "{f}"});
         rsx!("hello" { title })
     };
 

+ 1 - 1
packages/desktop/examples/todomvc.rs

@@ -136,7 +136,7 @@ pub struct TodoEntryProps {
     id: u32,
 }
 
-pub fn TodoEntry((cx, props): Component<TodoEntryProps>) -> Element {
+pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
     let todos = use_shared_state::<Todos>(cx)?;
 
     let _todos = todos.read();

+ 2 - 2
packages/desktop/src/desktop_context.rs

@@ -1,6 +1,6 @@
 use std::cell::RefCell;
 
-use dioxus::prelude::Component;
+use dioxus::prelude::Scope;
 use dioxus_core as dioxus;
 use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties};
 use dioxus_core_macro::Props;
@@ -54,7 +54,7 @@ pub struct WebviewWindowProps<'a> {
 ///
 ///
 ///
-pub fn WebviewWindow<'a>((cx, props): Component<'a, WebviewWindowProps>) -> Element<'a> {
+pub fn WebviewWindow<'a>((cx, props): Scope<'a, WebviewWindowProps>) -> Element<'a> {
     let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
 
     cx.use_hook(