Explorar o código

feat: upgrade syntax

Jonathan Kelley %!s(int64=3) %!d(string=hai) anos
pai
achega
fd93ee8
Modificáronse 64 ficheiros con 522 adicións e 437 borrados
  1. 1 1
      examples/core/async.rs
  2. 1 1
      examples/core/contextapi.rs
  3. 3 3
      examples/core/syntax.rs
  4. 1 1
      examples/core/vdom_usage.rs
  5. 8 8
      examples/core_reference/antipatterns.rs
  6. 2 2
      examples/core_reference/basics.rs
  7. 2 2
      examples/core_reference/children.rs
  8. 4 4
      examples/core_reference/conditional_rendering.rs
  9. 3 3
      examples/core_reference/controlled_inputs.rs
  10. 1 1
      examples/core_reference/custom_elements.rs
  11. 1 1
      examples/core_reference/empty.rs
  12. 6 6
      examples/core_reference/errorhandling.rs
  13. 4 4
      examples/core_reference/fragments.rs
  14. 1 1
      examples/core_reference/global_css.rs
  15. 2 2
      examples/core_reference/inline_styles.rs
  16. 1 1
      examples/core_reference/iterators.rs
  17. 4 4
      examples/core_reference/listener.rs
  18. 3 3
      examples/core_reference/memo.rs
  19. 1 1
      examples/core_reference/noderefs.rs
  20. 1 1
      examples/core_reference/signals.rs
  21. 1 1
      examples/core_reference/spreadpattern.rs
  22. 1 1
      examples/core_reference/statemanagement.rs
  23. 1 1
      examples/core_reference/suspense.rs
  24. 1 1
      examples/core_reference/task.rs
  25. 1 1
      examples/core_reference/testing.rs
  26. 2 2
      examples/core_reference/tostring.rs
  27. 1 1
      examples/coroutine.rs
  28. 1 1
      examples/desktop/demo.rs
  29. 1 1
      examples/file_explorer.rs
  30. 1 1
      examples/hydration.rs
  31. 1 1
      examples/pattern_model.rs
  32. 1 1
      examples/readme.rs
  33. 1 1
      examples/router.rs
  34. 1 1
      examples/rsx_usage.rs
  35. 1 1
      examples/ssr.rs
  36. 1 1
      examples/ssr/basic.rs
  37. 1 1
      examples/ssr/tide.rs
  38. 6 6
      examples/ssr/tofile.rs
  39. 6 6
      examples/tailwind.rs
  40. 23 0
      examples/tasks.rs
  41. 2 2
      examples/todomvc.rs
  42. 2 2
      examples/weather_app.rs
  43. 1 1
      examples/web/blah.rs
  44. 2 2
      examples/web/btns.rs
  45. 6 6
      examples/web/demo.rs
  46. 1 1
      examples/web_tick.rs
  47. 1 1
      examples/webview_web.rs
  48. 1 1
      packages/core-macro/src/rsx/component.rs
  49. 22 24
      packages/core/src/diff.rs
  50. 1 1
      packages/core/src/lazynodes.rs
  51. 16 5
      packages/core/src/mutations.rs
  52. 114 32
      packages/core/src/nodes.rs
  53. 45 22
      packages/core/src/scope.rs
  54. 104 73
      packages/core/src/scopearena.rs
  55. 48 77
      packages/core/src/virtual_dom.rs
  56. 4 22
      packages/core/tests/create_dom.rs
  57. 3 22
      packages/core/tests/diffing.rs
  58. 3 4
      packages/desktop/src/desktop_context.rs
  59. 3 4
      packages/desktop/src/lib.rs
  60. 1 0
      packages/ssr/index.html
  61. 11 9
      packages/ssr/src/lib.rs
  62. 22 34
      packages/web/src/dom.rs
  63. 0 5
      packages/web/src/events.rs
  64. 6 7
      packages/web/src/lib.rs

+ 1 - 1
examples/core/async.rs

@@ -5,7 +5,7 @@ fn main() {
     dom.rebuild();
 }
 
-const App: FC<()> = |(cx, props)| {
+const App: FC<()> = |cx, props| {
     let id = cx.scope_id();
     // cx.submit_task(Box::pin(async move { id }));
 

+ 1 - 1
examples/core/contextapi.rs

@@ -6,7 +6,7 @@ struct SomeContext {
 }
 
 #[allow(unused)]
-static Example: FC<()> = |(cx, props)| {
+static Example: FC<()> = |cx, props| {
     todo!()
 
     // let value = cx.use_context(|c: &SomeContext| c.items.last().unwrap());

+ 3 - 3
examples/core/syntax.rs

@@ -33,7 +33,7 @@ fn html_usage() {
 
 static App2: FC<()> = |(cx, _)| cx.render(rsx!("hello world!"));
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let name = cx.use_state(|| 0);
 
     cx.render(rsx!(div {
@@ -99,7 +99,7 @@ impl<'a> Children<'a> {
     }
 }
 
-static Bapp: FC<()> = |(cx, props)| {
+static Bapp: FC<()> = |cx, props| {
     let name = cx.use_state(|| 0);
 
     cx.render(rsx!(
@@ -114,7 +114,7 @@ static Bapp: FC<()> = |(cx, props)| {
     ))
 };
 
-static Match: FC<()> = |(cx, props)| {
+static Match: FC<()> = |cx, props| {
     //
     let b: Box<dyn Fn(NodeFactory) -> VNode> = Box::new(|f| todo!());
 

+ 1 - 1
examples/core/vdom_usage.rs

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

+ 8 - 8
examples/core_reference/antipatterns.rs

@@ -32,7 +32,7 @@ use dioxus::prelude::*;
 struct NoKeysProps {
     data: std::collections::HashMap<u32, String>,
 }
-static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| {
+static AntipatternNoKeys: FC<NoKeysProps> = |cx, props| {
     // WRONG: Make sure to add keys!
     rsx!(cx, ul {
         {props.data.iter().map(|(k, v)| rsx!(li { "List item: {v}" }))}
@@ -54,7 +54,7 @@ static AntipatternNoKeys: FC<NoKeysProps> = |(cx, props)| {
 ///
 /// Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigates this with components by providing
 /// an API for registering shared state without the ContextProvider pattern.
-static AntipatternNestedFragments: FC<()> = |(cx, props)| {
+static AntipatternNestedFragments: FC<()> = |cx, props| {
     // Try to avoid heavily nesting fragments
     rsx!(cx,
         Fragment {
@@ -82,7 +82,7 @@ static AntipatternNestedFragments: FC<()> = |(cx, props)| {
 /// However, calling set_state will *not* update the current version of state in the component. This should be easy to
 /// recognize from the function signature, but Dioxus will not update the "live" version of state. Calling `set_state`
 /// merely places a new value in the queue and schedules the component for a future update.
-static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| {
+static AntipatternRelyingOnSetState: FC<()> = |cx, props| {
     let (state, set_state) = use_state(cx, || "Hello world").classic();
     set_state("New state");
     // This will return false! `state` will *still* be "Hello world"
@@ -99,7 +99,7 @@ static AntipatternRelyingOnSetState: FC<()> = |(cx, props)| {
 /// - All components must start with an uppercase character
 ///
 /// i.e.: the following component will be rejected when attempted to be used in the rsx! macro
-static antipattern_component: FC<()> = |(cx, props)| todo!();
+static antipattern_component: FC<()> = |cx, props| todo!();
 
 /// Antipattern: Misusing hooks
 /// ---------------------------
@@ -120,7 +120,7 @@ static antipattern_component: FC<()> = |(cx, props)| todo!();
 struct MisuedHooksProps {
     should_render_state: bool,
 }
-static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| {
+static AntipatternMisusedHooks: FC<MisuedHooksProps> = |cx, props| {
     if props.should_render_state {
         // do not place a hook in the conditional!
         // prefer to move it out of the conditional
@@ -153,7 +153,7 @@ static AntipatternMisusedHooks: FC<MisuedHooksProps> = |(cx, props)| {
 ///         }
 ///     }
 /// })
-static _example: FC<()> = |(cx, props)| todo!();
+static _example: FC<()> = |cx, props| todo!();
 
 /// Antipattern: publishing components and hooks with all features enabled
 /// ----------------------------------------------------------------------
@@ -171,9 +171,9 @@ static _example: FC<()> = |(cx, props)| todo!();
 ///
 /// This will only include the `core` dioxus crate which is relatively slim and fast to compile and avoids target-specific
 /// libraries.
-static __example: FC<()> = |(cx, props)| todo!();
+static __example: FC<()> = |cx, props| todo!();
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         AntipatternNoKeys { data: std::collections::HashMap::new() }
         AntipatternNestedFragments {}

+ 2 - 2
examples/core_reference/basics.rs

@@ -9,7 +9,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             Greeting {
@@ -25,7 +25,7 @@ struct GreetingProps {
     name: &'static str,
 }
 
-static Greeting: FC<GreetingProps> = |(cx, props)| {
+static Greeting: FC<GreetingProps> = |cx, props| {
     cx.render(rsx! {
         div {
             h1 { "Hello, {props.name}!" }

+ 2 - 2
examples/core_reference/children.rs

@@ -18,7 +18,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             Banner {
@@ -31,7 +31,7 @@ pub static Example: FC<()> = |(cx, props)| {
     })
 };
 
-pub static Banner: FC<()> = |(cx, props)| {
+pub static Banner: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             h1 { "This is a great banner!" }

+ 4 - 4
examples/core_reference/conditional_rendering.rs

@@ -16,7 +16,7 @@ use dioxus::prelude::*;
 pub struct MyProps {
     should_show: bool,
 }
-pub static Example0: FC<MyProps> = |(cx, props)| {
+pub static Example0: FC<MyProps> = |cx, props| {
     cx.render(rsx! {
         div {
             {props.should_show.then(|| rsx!{
@@ -39,7 +39,7 @@ pub static Example0: FC<MyProps> = |(cx, props)| {
 pub struct MyProps1 {
     should_show: bool,
 }
-pub static Example1: FC<MyProps1> = |(cx, props)| {
+pub static Example1: FC<MyProps1> = |cx, props| {
     cx.render(rsx! {
         div {
             // With matching
@@ -77,7 +77,7 @@ pub enum Color {
 pub struct MyProps2 {
     color: Color,
 }
-pub static Example2: FC<MyProps2> = |(cx, props)| {
+pub static Example2: FC<MyProps2> = |cx, props| {
     cx.render(rsx! {
         div {
             {match props.color {
@@ -89,7 +89,7 @@ pub static Example2: FC<MyProps2> = |(cx, props)| {
     })
 };
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let should_show = use_state(cx, || false);
     let mut color_index = use_state(cx, || 0);
     let color = match *color_index % 2 {

+ 3 - 3
examples/core_reference/controlled_inputs.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
 
@@ -10,7 +10,7 @@ pub static Example: FC<()> = |(cx, props)| {
 };
 
 // A controlled component:
-static ControlledSelect: FC<()> = |(cx, props)| {
+static ControlledSelect: FC<()> = |cx, props| {
     let value = use_state(cx, || String::from("Grapefruit"));
     cx.render(rsx! {
         select { value: "{value}", onchange: move |evt| value.set(evt.value()),
@@ -23,7 +23,7 @@ static ControlledSelect: FC<()> = |(cx, props)| {
 };
 
 // TODO - how do uncontrolled things work?
-static UncontrolledSelect: FC<()> = |(cx, props)| {
+static UncontrolledSelect: FC<()> = |cx, props| {
     let value = use_state(cx, || String::new());
 
     cx.render(rsx! {

+ 1 - 1
examples/core_reference/custom_elements.rs

@@ -11,7 +11,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             custom_element {

+ 1 - 1
examples/core_reference/empty.rs

@@ -5,4 +5,4 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| cx.render(rsx! { Fragment {} });
+pub static Example: FC<()> = |cx, props| cx.render(rsx! { Fragment {} });

+ 6 - 6
examples/core_reference/errorhandling.rs

@@ -23,14 +23,14 @@ fn main() {}
 /// This is one way to go about error handling (just toss things away with unwrap).
 /// However, if you get it wrong, the whole app will crash.
 /// This is pretty flimsy.
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let data = get_data().unwrap();
     cx.render(rsx!( div { "{data}" } ))
 };
 
 /// This is a pretty verbose way of error handling
 /// However, it's still pretty good since we don't panic, just fail to render anything
-static App1: FC<()> = |(cx, props)| {
+static App1: FC<()> = |cx, props| {
     let data = match get_data() {
         Some(data) => data,
         None => return None,
@@ -46,7 +46,7 @@ static App1: FC<()> = |(cx, props)| {
 /// a user is logged in.
 ///
 /// Dioxus will throw an error in the console if the None-path is ever taken.
-static App2: FC<()> = |(cx, props)| {
+static App2: FC<()> = |cx, props| {
     let data = get_data()?;
     cx.render(rsx!( div { "{data}" } ))
 };
@@ -54,14 +54,14 @@ static App2: FC<()> = |(cx, props)| {
 /// This is top-tier error handling since it displays a failure state.
 ///
 /// However, the error is lacking in context.
-static App3: FC<()> = |(cx, props)| match get_data() {
+static App3: FC<()> = |cx, props| match get_data() {
     Some(data) => cx.render(rsx!( div { "{data}" } )),
     None => cx.render(rsx!( div { "Failed to load data :(" } )),
 };
 
 /// For errors that return results, it's possible to short-circuit the match-based error handling with `.ok()` which converts
 /// a Result<T, V> into an Option<T> and lets you abort rendering by early-returning `None`
-static App4: FC<()> = |(cx, props)| {
+static App4: FC<()> = |cx, props| {
     let data = get_data_err().ok()?;
     cx.render(rsx!( div { "{data}" } ))
 };
@@ -69,7 +69,7 @@ static App4: FC<()> = |(cx, props)| {
 /// This is great error handling since it displays a failure state... with context!
 ///
 /// Hopefully you'll never need to display a screen like this. It's rather bad taste
-static App5: FC<()> = |(cx, props)| match get_data_err() {
+static App5: FC<()> = |cx, props| match get_data_err() {
     Ok(data) => cx.render(rsx!( div { "{data}" } )),
     Err(c) => cx.render(rsx!( div { "Failed to load data: {c}" } )),
 };

+ 4 - 4
examples/core_reference/fragments.rs

@@ -11,7 +11,7 @@
 use dioxus::prelude::*;
 
 // Returning multiple elements with rsx! or html!
-static App1: FC<()> = |(cx, props)| {
+static App1: FC<()> = |cx, props| {
     cx.render(rsx! {
         h1 { }
         h2 { }
@@ -20,7 +20,7 @@ static App1: FC<()> = |(cx, props)| {
 };
 
 // Using the Fragment component
-static App2: FC<()> = |(cx, props)| {
+static App2: FC<()> = |cx, props| {
     cx.render(rsx! {
         Fragment {
             div {}
@@ -31,7 +31,7 @@ static App2: FC<()> = |(cx, props)| {
 };
 
 // Using the `fragment` method on the NodeFactory
-static App3: FC<()> = |(cx, props)| {
+static App3: FC<()> = |cx, props| {
     cx.render(LazyNodes::new(move |fac| {
         fac.fragment_from_iter([
             fac.text(format_args!("A")),
@@ -42,7 +42,7 @@ static App3: FC<()> = |(cx, props)| {
     }))
 };
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         App1 {}
         App2 {}

+ 1 - 1
examples/core_reference/global_css.rs

@@ -19,7 +19,7 @@ h1   {color: blue;}
 p    {color: red;}
 "#;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         head { style { "{STYLE}" } }
         body {

+ 2 - 2
examples/core_reference/inline_styles.rs

@@ -10,7 +10,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         head {
             style: { background_color: "powderblue" }
@@ -29,7 +29,7 @@ pub static Example: FC<()> = |(cx, props)| {
 // .... technically the rsx! macro is slightly broken at the moment and allows styles not wrapped in style {}
 // I haven't noticed any name collisions yet, and am tentatively leaving this behavior in..
 // Don't rely on it.
-static Example2: FC<()> = |(cx, props)| {
+static Example2: FC<()> = |cx, props| {
     cx.render(rsx! {
         div { color: "red"
             "hello world!"

+ 1 - 1
examples/core_reference/iterators.rs

@@ -12,7 +12,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let example_data = use_state(cx, || 0);
 
     let v = (0..10).map(|f| {

+ 4 - 4
examples/core_reference/listener.rs

@@ -7,7 +7,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         ButtonList {}
         NonUpdatingEvents {}
@@ -16,7 +16,7 @@ pub static Example: FC<()> = |(cx, props)| {
 };
 
 /// We can use `set_name` in multiple closures; the closures automatically *copy* the reference to set_name.
-static ButtonList: FC<()> = |(cx, props)| {
+static ButtonList: FC<()> = |cx, props| {
     let name = use_state(cx, || "...?");
 
     let names = ["jack", "jill", "john", "jane"]
@@ -33,7 +33,7 @@ static ButtonList: FC<()> = |(cx, props)| {
 
 /// This shows how listeners may be without a visible change in the display.
 /// Check the console.
-static NonUpdatingEvents: FC<()> = |(cx, props)| {
+static NonUpdatingEvents: FC<()> = |cx, props| {
     rsx!(cx, div {
         button {
             onclick: move |_| log::info!("Did not cause any updates!")
@@ -42,7 +42,7 @@ static NonUpdatingEvents: FC<()> = |(cx, props)| {
     })
 };
 
-static DisablePropagation: FC<()> = |(cx, props)| {
+static DisablePropagation: FC<()> = |cx, props| {
     rsx!(cx,
         div {
             onclick: move |_| log::info!("event propagated to the div!")

+ 3 - 3
examples/core_reference/memo.rs

@@ -21,7 +21,7 @@ use dioxus::prelude::*;
 
 // By default, components with no props are always memoized.
 // A props of () is considered empty.
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div { "100% memoized!" }
     })
@@ -35,7 +35,7 @@ pub struct MyProps1 {
     name: String,
 }
 
-pub static Example1: FC<MyProps1> = |(cx, props)| {
+pub static Example1: FC<MyProps1> = |cx, props| {
     cx.render(rsx! {
         div { "100% memoized! {props.name}" }
     })
@@ -49,7 +49,7 @@ pub struct MyProps2 {
     name: std::rc::Rc<str>,
 }
 
-pub static Example2: FC<MyProps2> = |(cx, props)| {
+pub static Example2: FC<MyProps2> = |cx, props| {
     cx.render(rsx! {
         div { "100% memoized! {props.name}" }
     })

+ 1 - 1
examples/core_reference/noderefs.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let p = 10;
 
     cx.render(rsx! {

+ 1 - 1
examples/core_reference/signals.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
 

+ 1 - 1
examples/core_reference/spreadpattern.rs

@@ -9,7 +9,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let props = MyProps {
         count: 0,
         live: true,

+ 1 - 1
examples/core_reference/statemanagement.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 fn main() {}
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
 

+ 1 - 1
examples/core_reference/suspense.rs

@@ -14,7 +14,7 @@ struct DogApi {
 }
 const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let doggo = use_suspense(
         cx,
         || surf::get(ENDPOINT).recv_json::<DogApi>(),

+ 1 - 1
examples/core_reference/task.rs

@@ -24,7 +24,7 @@
 
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let count = use_state(cx, || 0);
     let mut direction = use_state(cx, || 1);
 

+ 1 - 1
examples/core_reference/testing.rs

@@ -1,6 +1,6 @@
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
 

+ 2 - 2
examples/core_reference/tostring.rs

@@ -1,7 +1,7 @@
 use dioxus::prelude::*;
 use dioxus::ssr;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let as_string = use_state(cx, || {
         // Currently, SSR is only supported for whole VirtualDOMs
         // This is an easy/low hanging fruit to improve upon
@@ -15,7 +15,7 @@ pub static Example: FC<()> = |(cx, props)| {
     })
 };
 
-static SomeApp: FC<()> = |(cx, props)| {
+static SomeApp: FC<()> = |cx, props| {
     cx.render(rsx! {
         div { style: {background_color: "blue"}
             h1 {"Some amazing app or component"}

+ 1 - 1
examples/coroutine.rs

@@ -27,7 +27,7 @@ fn main() {
 
 use dioxus::prelude::*;
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let p1 = use_state(cx, || 0);
     let p2 = use_state(cx, || 0);
 

+ 1 - 1
examples/desktop/demo.rs

@@ -8,7 +8,7 @@ fn main() {
     dioxus_desktop::launch(App, |c| c);
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     cx.render(rsx!(
         div {
             "hello world!"

+ 1 - 1
examples/file_explorer.rs

@@ -18,7 +18,7 @@ fn main() {
     });
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let file_manager = use_ref(cx, || Files::new());
     let files = file_manager.read();
 

+ 1 - 1
examples/hydration.rs

@@ -19,7 +19,7 @@ fn main() {
     dioxus::desktop::launch(App, |c| c.with_prerendered(content));
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let mut val = use_state(cx, || 0);
 
     cx.render(rsx! {

+ 1 - 1
examples/pattern_model.rs

@@ -31,7 +31,7 @@ fn main() {
     });
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let state = use_ref(cx, || Calculator::new());
 
     let clear_display = state.read().display_value.eq("0");

+ 1 - 1
examples/readme.rs

@@ -7,7 +7,7 @@ fn main() {
     dioxus::desktop::launch(App, |c| c);
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let mut count = use_state(cx, || 0);
 
     cx.render(rsx! {

+ 1 - 1
examples/router.rs

@@ -23,7 +23,7 @@ pub enum Route {
     NotFound,
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let route = use_router(cx, Route::parse)?;
 
     cx.render(rsx! {

+ 1 - 1
examples/rsx_usage.rs

@@ -49,7 +49,7 @@ const NONE_ELEMENT: Option<()> = None;
 use baller::Baller;
 use dioxus::prelude::*;
 
-pub static Example: FC<()> = |(cx, props)| {
+pub static Example: FC<()> = |cx, props| {
     let formatting = "formatting!";
     let formatting_tuple = ("a", "b");
     let lazy_fmt = format_args!("lazily formatted text");

+ 1 - 1
examples/ssr.rs

@@ -9,7 +9,7 @@ fn main() {
     println!("{}", ssr::render_vdom(&vdom, |c| c));
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     cx.render(rsx!(
         div {
             h1 { "Title" }

+ 1 - 1
examples/ssr/basic.rs

@@ -12,7 +12,7 @@ fn main() {
     )
 }
 
-pub static App: FC<()> = |(cx, props)| {
+pub static App: FC<()> = |cx, props| {
     cx.render(rsx!(
         div {
             class: "overflow-hidden"

+ 1 - 1
examples/ssr/tide.rs

@@ -42,7 +42,7 @@ fn main() {}
 //     initial_name: String,
 // }
 
-// static Example: FC<ExampleProps> = |(cx, props)| {
+// static Example: FC<ExampleProps> = |cx, props| {
 //     let dispaly_name = use_state(cx, move || props.initial_name.clone());
 
 //     cx.render(rsx! {

+ 6 - 6
examples/ssr/tofile.rs

@@ -24,7 +24,7 @@ fn main() {
     .unwrap();
 }
 
-pub static App: FC<()> = |(cx, props)| {
+pub static App: FC<()> = |cx, props| {
     cx.render(rsx!(
         div { class: "overflow-hidden"
             link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| {
     ))
 };
 
-pub static Header: FC<()> = |(cx, props)| {
+pub static Header: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| {
     })
 };
 
-pub static Hero: FC<()> = |(cx, props)| {
+pub static Hero: FC<()> = |cx, props| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| {
         }
     })
 };
-pub static Entry: FC<()> = |(cx, props)| {
+pub static Entry: FC<()> = |cx, props| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| {
     })
 };
 
-pub static StacksIcon: FC<()> = |(cx, props)| {
+pub static StacksIcon: FC<()> = |cx, props| {
     cx.render(rsx!(
         svg {
             xmlns: "http://www.w3.org/2000/svg"
@@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
         }
     ))
 };
-pub static RightArrowIcon: FC<()> = |(cx, props)| {
+pub static RightArrowIcon: FC<()> = |cx, props| {
     cx.render(rsx!(
         svg {
             fill: "none"

+ 6 - 6
examples/tailwind.rs

@@ -14,7 +14,7 @@ fn main() {
 
 const STYLE: &str = "body {overflow:hidden;}";
 
-pub static App: FC<()> = |(cx, props)| {
+pub static App: FC<()> = |cx, props| {
     cx.render(rsx!(
         div { class: "overflow-hidden"
         style { "{STYLE}" }
@@ -30,7 +30,7 @@ pub static App: FC<()> = |(cx, props)| {
     ))
 };
 
-pub static Header: FC<()> = |(cx, props)| {
+pub static Header: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -56,7 +56,7 @@ pub static Header: FC<()> = |(cx, props)| {
     })
 };
 
-pub static Hero: FC<()> = |(cx, props)| {
+pub static Hero: FC<()> = |cx, props| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -94,7 +94,7 @@ pub static Hero: FC<()> = |(cx, props)| {
         }
     })
 };
-pub static Entry: FC<()> = |(cx, props)| {
+pub static Entry: FC<()> = |cx, props| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -107,7 +107,7 @@ pub static Entry: FC<()> = |(cx, props)| {
     })
 };
 
-pub static StacksIcon: FC<()> = |(cx, props)| {
+pub static StacksIcon: FC<()> = |cx, props| {
     cx.render(rsx!(
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
@@ -122,7 +122,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
         }
     ))
 };
-pub static RightArrowIcon: FC<()> = |(cx, props)| {
+pub static RightArrowIcon: FC<()> = |cx, props| {
     cx.render(rsx!(
         svg {
             fill: "none"

+ 23 - 0
examples/tasks.rs

@@ -0,0 +1,23 @@
+//! Example: README.md showcase
+//!
+//! The example from the README.md.
+
+use dioxus::prelude::*;
+fn main() {
+    dioxus::desktop::launch(App, |c| c);
+}
+
+static App: FC<()> = |cx, props| {
+    let mut count = use_state(cx, || 0);
+
+    cx.push_task(async {
+        panic!("polled future");
+        //
+    });
+
+    cx.render(rsx! {
+        div {
+            h1 { "High-Five counter: {count}" }
+        }
+    })
+};

+ 2 - 2
examples/todomvc.rs

@@ -22,7 +22,7 @@ pub struct TodoItem {
 }
 
 const STYLE: &str = include_str!("./assets/todomvc.css");
-const App: FC<()> = |(cx, props)| {
+const App: FC<()> = |cx, props| {
     let mut draft = use_state(cx, || "".to_string());
     let mut todos = use_state(cx, || HashMap::<u32, Rc<TodoItem>>::new());
     let mut filter = use_state(cx, || FilterState::All);
@@ -85,7 +85,7 @@ pub struct TodoEntryProps {
     todo: Rc<TodoItem>,
 }
 
-pub fn TodoEntry((cx, props): Scope<TodoEntryProps>) -> Element {
+pub fn TodoEntry(cx: Context, props: &TodoEntryProps) -> Element {
     let mut is_editing = use_state(cx, || false);
     let mut contents = use_state(cx, || String::from(""));
     let todo = &props.todo;

+ 2 - 2
examples/weather_app.rs

@@ -12,7 +12,7 @@ fn main() {
 
 const ENDPOINT: &str = "https://api.openweathermap.org/data/2.5/weather";
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     //
     let body = use_suspense(
         cx,
@@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| {
 #[derive(PartialEq, Props)]
 struct WeatherProps {}
 
-static WeatherDisplay: FC<WeatherProps> = |(cx, props)| {
+static WeatherDisplay: FC<WeatherProps> = |cx, props| {
     //
     cx.render(rsx!(
         div { class: "flex items-center justify-center flex-col"

+ 1 - 1
examples/web/blah.rs

@@ -24,7 +24,7 @@ fn main() {
     dioxus_web::launch(App, |c| c)
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let mut state = use_state(cx, || 0);
     cx.render(rsx! {
         div {

+ 2 - 2
examples/web/btns.rs

@@ -23,7 +23,7 @@ fn main() {
     // dioxus::web::launch(App, |c| c);
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     dbg!("rednering parent");
     cx.render(rsx! {
         div {
@@ -40,7 +40,7 @@ static App: FC<()> = |(cx, props)| {
     })
 };
 
-static But: FC<()> = |(cx, props)| {
+static But: FC<()> = |cx, props| {
     let mut count = use_state(cx, || 0);
 
     // let d = Dropper { name: "asd" };

+ 6 - 6
examples/web/demo.rs

@@ -24,7 +24,7 @@ fn main() {
     dioxus_web::launch(App, |c| c)
 }
 
-pub static App: FC<()> = |(cx, props)| {
+pub static App: FC<()> = |cx, props| {
     cx.render(rsx!(
         div { class: "overflow-hidden"
             link { href:"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel:"stylesheet" }
@@ -39,7 +39,7 @@ pub static App: FC<()> = |(cx, props)| {
     ))
 };
 
-pub static Header: FC<()> = |(cx, props)| {
+pub static Header: FC<()> = |cx, props| {
     cx.render(rsx! {
         div {
             header { class: "text-gray-400 bg-gray-900 body-font"
@@ -65,7 +65,7 @@ pub static Header: FC<()> = |(cx, props)| {
     })
 };
 
-pub static Hero: FC<()> = |(cx, props)| {
+pub static Hero: FC<()> = |cx, props| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -103,7 +103,7 @@ pub static Hero: FC<()> = |(cx, props)| {
         }
     })
 };
-pub static Entry: FC<()> = |(cx, props)| {
+pub static Entry: FC<()> = |cx, props| {
     //
     cx.render(rsx! {
         section{ class: "text-gray-400 bg-gray-900 body-font"
@@ -116,7 +116,7 @@ pub static Entry: FC<()> = |(cx, props)| {
     })
 };
 
-pub static StacksIcon: FC<()> = |(cx, props)| {
+pub static StacksIcon: FC<()> = |cx, props| {
     cx.render(rsx!(
         svg {
             // xmlns: "http://www.w3.org/2000/svg"
@@ -131,7 +131,7 @@ pub static StacksIcon: FC<()> = |(cx, props)| {
         }
     ))
 };
-pub static RightArrowIcon: FC<()> = |(cx, props)| {
+pub static RightArrowIcon: FC<()> = |cx, props| {
     cx.render(rsx!(
         svg {
             fill: "none"

+ 1 - 1
examples/web_tick.rs

@@ -19,7 +19,7 @@ fn main() {
     dioxus::web::launch(App, |c| c);
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let mut rng = SmallRng::from_entropy();
     let rows = (0..1_000).map(|f| {
         let label = Label::new(&mut rng);

+ 1 - 1
examples/webview_web.rs

@@ -16,7 +16,7 @@ fn main() {
     dioxus::web::launch(App, |c| c);
 }
 
-static App: FC<()> = |(cx, props)| {
+static App: FC<()> = |cx, props| {
     let mut count = use_state(cx, || 0);
 
     cx.render(rsx! {

+ 1 - 1
packages/core-macro/src/rsx/component.rs

@@ -173,7 +173,7 @@ impl ToTokens for Component {
                 if !self.children.is_empty() {
                     let childs = &self.children;
                     toks.append_all(quote! {
-                        .children(ScopeChildren::new(__cx.fragment_from_iter([ #( #childs ),* ])))
+                        .children(__cx.create_children([ #( #childs ),* ]))
                     });
                 }
 

+ 22 - 24
packages/core/src/diff.rs

@@ -321,14 +321,15 @@ impl<'bump> DiffState<'bump> {
             }
 
             MountType::Replace { old } => {
+                // todo: a bug here where we remove a node that is alread being replaced
                 if let Some(old_id) = old.try_mounted_id() {
                     self.mutations.replace_with(old_id, nodes_created as u32);
-                    self.remove_nodes(Some(old), true);
+                    self.remove_nodes(Some(old), false);
                 } else {
                     if let Some(id) = self.find_first_element_id(old) {
                         self.mutations.replace_with(id, nodes_created as u32);
                     }
-                    self.remove_nodes(Some(old), true);
+                    self.remove_nodes(Some(old), false);
                 }
             }
 
@@ -487,7 +488,11 @@ impl<'bump> DiffState<'bump> {
     }
 
     fn create_linked_node(&mut self, link: &'bump NodeLink) {
-        todo!()
+        if let Some(cur_scope) = self.stack.current_scope() {
+            link.scope_id.set(Some(cur_scope));
+            let node: &'bump VNode<'static> = unsafe { &*link.node };
+            self.create_node(unsafe { std::mem::transmute(node) });
+        }
     }
 
     // =================================
@@ -532,7 +537,6 @@ impl<'bump> DiffState<'bump> {
 
     fn diff_element_nodes(
         &mut self,
-
         old: &'bump VElement<'bump>,
         new: &'bump VElement<'bump>,
         old_node: &'bump VNode<'bump>,
@@ -629,10 +633,8 @@ impl<'bump> DiffState<'bump> {
 
     fn diff_component_nodes(
         &mut self,
-
         old_node: &'bump VNode<'bump>,
         new_node: &'bump VNode<'bump>,
-
         old: &'bump VComponent<'bump>,
         new: &'bump VComponent<'bump>,
     ) {
@@ -648,21 +650,20 @@ impl<'bump> DiffState<'bump> {
             new.associated_scope.set(Some(scope_addr));
 
             // make sure the component's caller function is up to date
-            let scope = self.scopes.get_scope(&scope_addr).unwrap();
-            let mut items = scope.items.borrow_mut();
+            let scope = unsafe { self.scopes.get_scope_mut(&scope_addr).unwrap() };
+            scope.caller = unsafe { std::mem::transmute(new.caller) };
 
             // React doesn't automatically memoize, but we do.
-            let props_are_the_same = todo!("reworking component memoization");
-            // let props_are_the_same = todo!("reworking component memoization");
-            // let props_are_the_same = old.comparator.unwrap();
-
-            // if self.cfg.force_diff || !props_are_the_same(new) {
-            //     let succeeded = scope.run_scope(self);
+            let props_are_the_same = old.comparator.unwrap();
 
-            //     if succeeded {
-            //         self.diff_node(scope.frames.wip_head(), scope.frames.fin_head());
-            //     }
-            // }
+            if self.force_diff || !props_are_the_same(new) {
+                if self.scopes.run_scope(&scope_addr) {
+                    self.diff_node(
+                        self.scopes.wip_head(&scope_addr),
+                        self.scopes.fin_head(&scope_addr),
+                    );
+                }
+            }
 
             self.stack.scope_stack.pop();
         } else {
@@ -1198,7 +1199,6 @@ impl<'bump> DiffState<'bump> {
     /// remove can happen whenever
     fn remove_nodes(
         &mut self,
-
         nodes: impl IntoIterator<Item = &'bump VNode<'bump>>,
         gen_muts: bool,
     ) {
@@ -1244,18 +1244,16 @@ impl<'bump> DiffState<'bump> {
                 }
 
                 VNode::Linked(l) => {
-                    todo!()
+                    let node = unsafe { std::mem::transmute(&*l.node) };
+                    self.remove_nodes(Some(node), gen_muts);
                 }
 
                 VNode::Component(c) => {
                     let scope_id = c.associated_scope.get().unwrap();
-                    // let scope = self.scopes.get_scope(&scope_id).unwrap();
                     let root = self.scopes.root_node(&scope_id);
                     self.remove_nodes(Some(root), gen_muts);
-
                     log::debug!("Destroying scope {:?}", scope_id);
-                    let mut s = self.scopes.try_remove(&scope_id).unwrap();
-                    s.hooks.clear_hooks();
+                    self.scopes.try_remove(&scope_id).unwrap();
                 }
             }
         }

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

@@ -245,7 +245,7 @@ fn it_drops() {
 
     simple_logger::init().unwrap();
 
-    let factory = NodeFactory { bump: &bump };
+    // let factory = NodeFactory { scope: &bump };
 
     struct DropInner {
         id: i32,

+ 16 - 5
packages/core/src/mutations.rs

@@ -3,21 +3,32 @@
 //! This module contains an internal API to generate these instructions.
 
 use crate::innerlude::*;
-use std::any::Any;
+use std::{any::Any, fmt::Debug};
 
-#[derive(Debug)]
 pub struct Mutations<'a> {
     pub edits: Vec<DomEdit<'a>>,
     pub noderefs: Vec<NodeRefMutation<'a>>,
+    pub effects: Vec<&'a dyn FnMut()>,
+}
+impl Debug for Mutations<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("Mutations")
+            .field("edits", &self.edits)
+            .field("noderefs", &self.noderefs)
+            // .field("effects", &self.effects)
+            .finish()
+    }
 }
 
 use DomEdit::*;
 
 impl<'a> Mutations<'a> {
     pub(crate) fn new() -> Self {
-        let edits = Vec::new();
-        let noderefs = Vec::new();
-        Self { edits, noderefs }
+        Self {
+            edits: Vec::new(),
+            noderefs: Vec::new(),
+            effects: Vec::new(),
+        }
     }
 
     // Navigation

+ 114 - 32
packages/core/src/nodes.rs

@@ -181,7 +181,7 @@ impl<'src> VNode<'src> {
     pub(crate) fn children(&self) -> &[VNode<'src>] {
         match &self {
             VNode::Fragment(f) => f.children,
-            VNode::Component(c) => todo!("children are not accessible through this"),
+            VNode::Component(_c) => todo!("children are not accessible through this"),
             _ => &[],
         }
     }
@@ -199,9 +199,9 @@ impl<'src> VNode<'src> {
                 key: f.key,
             }),
             VNode::Linked(c) => VNode::Linked(NodeLink {
-                gen_id: c.gen_id,
-                scope_id: c.scope_id,
-                link_idx: c.link_idx,
+                scope_id: c.scope_id.clone(),
+                link_idx: c.link_idx.clone(),
+                node: c.node.clone(),
             }),
         }
     }
@@ -224,11 +224,7 @@ impl Debug for VNode<'_> {
             }
             VNode::Suspended { .. } => write!(s, "VNode::VSuspended"),
             VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
-            VNode::Linked(c) => write!(
-                s,
-                "VNode::VCached {{ gen_id: {}, scope_id: {:?} }}",
-                c.gen_id, c.scope_id
-            ),
+            VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
         }
     }
 }
@@ -419,9 +415,25 @@ pub struct VSuspended<'a> {
 /// an `rsx!` call.
 #[derive(Debug)]
 pub struct NodeLink {
-    pub(crate) link_idx: usize,
-    pub(crate) gen_id: u32,
-    pub(crate) scope_id: ScopeId,
+    pub(crate) link_idx: Cell<usize>,
+    pub(crate) scope_id: Cell<Option<ScopeId>>,
+    pub(crate) node: *const VNode<'static>,
+}
+
+impl PartialEq for NodeLink {
+    fn eq(&self, other: &Self) -> bool {
+        self.node == other.node
+    }
+}
+impl NodeLink {
+    // we don't want to let users clone NodeLinks
+    pub(crate) fn clone_inner(&self) -> Self {
+        Self {
+            link_idx: self.link_idx.clone(),
+            scope_id: self.scope_id.clone(),
+            node: self.node.clone(),
+        }
+    }
 }
 
 /// This struct provides an ergonomic API to quickly build VNodes.
@@ -440,7 +452,7 @@ impl<'a> NodeFactory<'a> {
 
     #[inline]
     pub fn bump(&self) -> &'a bumpalo::Bump {
-        self.bump
+        &self.bump
     }
 
     /// Directly pass in text blocks without the need to use the format_args macro.
@@ -460,7 +472,7 @@ impl<'a> NodeFactory<'a> {
             Some(static_str) => (static_str, true),
             None => {
                 use bumpalo::core_alloc::fmt::Write;
-                let mut str_buf = bumpalo::collections::String::new_in(self.bump());
+                let mut str_buf = bumpalo::collections::String::new_in(self.bump);
                 str_buf.write_fmt(args).unwrap();
                 (str_buf.into_bump_str(), false)
             }
@@ -516,18 +528,18 @@ impl<'a> NodeFactory<'a> {
         A: 'a + AsRef<[Attribute<'a>]>,
         V: 'a + AsRef<[VNode<'a>]>,
     {
-        let listeners: &'a L = self.bump().alloc(listeners);
+        let listeners: &'a L = self.bump.alloc(listeners);
         let listeners = listeners.as_ref();
 
-        let attributes: &'a A = self.bump().alloc(attributes);
+        let attributes: &'a A = self.bump.alloc(attributes);
         let attributes = attributes.as_ref();
 
-        let children: &'a V = self.bump().alloc(children);
+        let children: &'a V = self.bump.alloc(children);
         let children = children.as_ref();
 
         let key = key.map(|f| self.raw_text(f).0);
 
-        VNode::Element(self.bump().alloc(VElement {
+        VNode::Element(self.bump.alloc(VElement {
             tag_name,
             key,
             namespace,
@@ -565,7 +577,7 @@ impl<'a> NodeFactory<'a> {
     where
         P: Properties + 'a,
     {
-        let bump = self.bump();
+        let bump = self.bump;
         let props = bump.alloc(props);
         let bump_props = props as *mut P as *mut ();
         let user_fc = component as *const ();
@@ -598,7 +610,6 @@ impl<'a> NodeFactory<'a> {
 
             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);
@@ -620,7 +631,6 @@ impl<'a> NodeFactory<'a> {
 
         let caller: &'a mut dyn Fn(&'a 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);
                 res
@@ -657,7 +667,7 @@ impl<'a> NodeFactory<'a> {
         self,
         node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
     ) -> VNode<'a> {
-        let bump = self.bump();
+        let bump = self.bump;
         let mut nodes = bumpalo::collections::Vec::new_in(bump);
 
         for node in node_iter {
@@ -697,6 +707,58 @@ impl<'a> NodeFactory<'a> {
         })
     }
 
+    // this isn't quite feasible yet
+    // I think we need some form of interior mutability or state on nodefactory that stores which subtree was created
+    pub fn create_children(
+        self,
+        node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
+    ) -> Element {
+        let bump = self.bump;
+        let mut nodes = bumpalo::collections::Vec::new_in(bump);
+
+        for node in node_iter {
+            nodes.push(node.into_vnode(self));
+        }
+
+        if nodes.is_empty() {
+            nodes.push(VNode::Anchor(bump.alloc(VAnchor {
+                dom_id: empty_cell(),
+            })));
+        }
+
+        let children = nodes.into_bump_slice();
+
+        // TODO
+        // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
+        //
+        // if cfg!(debug_assertions) {
+        //     if children.len() > 1 {
+        //         if children.last().unwrap().key().is_none() {
+        //             log::error!(
+        //                 r#"
+        // Warning: Each child in an array or iterator should have a unique "key" prop.
+        // Not providing a key will lead to poor performance with lists.
+        // See docs.rs/dioxus for more information.
+        // ---
+        // To help you identify where this error is coming from, we've generated a backtrace.
+        //                         "#,
+        //             );
+        //         }
+        //     }
+        // }
+
+        let frag = VNode::Fragment(VFragment {
+            children,
+            key: None,
+        });
+        let ptr = self.bump.alloc(frag) as *const _;
+        Some(NodeLink {
+            link_idx: Default::default(),
+            scope_id: Default::default(),
+            node: unsafe { std::mem::transmute(ptr) },
+        })
+    }
+
     pub fn annotate_lazy<'z, 'b>(
         f: impl FnOnce(NodeFactory<'z>) -> VNode<'z> + 'b,
     ) -> Option<LazyNodes<'z, 'b>> {
@@ -796,28 +858,48 @@ impl IntoVNode<'_> for Arguments<'_> {
     }
 }
 
+// called cx.render from a helper function
 impl IntoVNode<'_> for Option<NodeLink> {
-    fn into_vnode(self, cx: NodeFactory) -> VNode {
-        todo!()
+    fn into_vnode(self, _cx: NodeFactory) -> VNode {
+        match self {
+            Some(node) => VNode::Linked(node),
+            None => {
+                todo!()
+            }
+        }
     }
 }
 
+// essentially passing elements through props
+// just build a new element in place
 impl IntoVNode<'_> for &Option<NodeLink> {
-    fn into_vnode(self, cx: NodeFactory) -> VNode {
-        todo!()
+    fn into_vnode(self, _cx: NodeFactory) -> VNode {
+        match self {
+            Some(node) => VNode::Linked(NodeLink {
+                link_idx: node.link_idx.clone(),
+                scope_id: node.scope_id.clone(),
+                node: node.node,
+            }),
+            None => {
+                //
+                todo!()
+            }
+        }
     }
 }
 
-// Conveniently, we also support "None"
 impl IntoVNode<'_> for NodeLink {
-    fn into_vnode(self, cx: NodeFactory) -> VNode {
-        todo!()
+    fn into_vnode(self, _cx: NodeFactory) -> VNode {
+        VNode::Linked(self)
     }
 }
 
-// Conveniently, we also support "None"
 impl IntoVNode<'_> for &NodeLink {
-    fn into_vnode(self, cx: NodeFactory) -> VNode {
-        todo!()
+    fn into_vnode(self, _cx: NodeFactory) -> VNode {
+        VNode::Linked(NodeLink {
+            link_idx: self.link_idx.clone(),
+            scope_id: self.scope_id.clone(),
+            node: self.node,
+        })
     }
 }

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

@@ -2,6 +2,7 @@ use crate::innerlude::*;
 
 use futures_channel::mpsc::UnboundedSender;
 use fxhash::FxHashMap;
+use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
     cell::{Cell, RefCell},
@@ -62,9 +63,6 @@ pub struct Scope {
     // 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: *const dyn Fn(&Scope) -> Element,
 
     /*
@@ -241,19 +239,22 @@ impl Scope {
     /// }
     ///```
     pub fn render<'src>(&'src self, lazy_nodes: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
-        let bump = &self.wip_frame().bump;
+        let frame = self.wip_frame();
+        let bump = &frame.bump;
         let factory = NodeFactory { bump };
         let node = lazy_nodes.map(|f| f.call(factory))?;
+        let node = bump.alloc(node);
 
-        let idx = self
-            .wip_frame()
-            .add_node(unsafe { std::mem::transmute(node) });
+        let node_ptr = node as *mut _;
+        let node_ptr = unsafe { std::mem::transmute(node_ptr) };
 
-        Some(NodeLink {
-            gen_id: self.generation.get(),
-            scope_id: self.our_arena_idx,
-            link_idx: idx,
-        })
+        let link = NodeLink {
+            scope_id: Cell::new(Some(self.our_arena_idx)),
+            link_idx: Cell::new(0),
+            node: node_ptr,
+        };
+
+        Some(link)
     }
 
     /// Push an effect to be ran after the component has been successfully mounted to the dom
@@ -435,6 +436,7 @@ impl Scope {
             false => &self.frames[1],
         }
     }
+
     pub(crate) fn fin_frame(&self) -> &BumpFrame {
         match self.generation.get() & 1 == 1 {
             true => &self.frames[0],
@@ -496,28 +498,42 @@ impl Scope {
             effect();
         }
     }
+
+    pub fn root_node(&self) -> &VNode {
+        let node = *self.wip_frame().nodes.borrow().get(0).unwrap();
+        unsafe { std::mem::transmute(&*node) }
+    }
 }
 
-pub struct BumpFrame {
+pub(crate) struct BumpFrame {
     pub bump: Bump,
-    pub nodes: RefCell<Vec<VNode<'static>>>,
+    pub nodes: RefCell<Vec<*const VNode<'static>>>,
 }
 impl BumpFrame {
-    pub fn new() -> Self {
-        let bump = Bump::new();
+    pub fn new(capacity: usize) -> Self {
+        let bump = Bump::with_capacity(capacity);
 
         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) })]);
+        let node = bump.alloc(VNode::Text(unsafe { std::mem::transmute(node) }));
+        let nodes = RefCell::new(vec![node as *const _]);
         Self { bump, nodes }
     }
-    fn add_node(&self, node: VNode<'static>) -> usize {
+
+    pub fn allocated_bytes(&self) -> usize {
+        self.bump.allocated_bytes()
+    }
+
+    pub fn assign_nodelink(&self, node: &NodeLink) {
         let mut nodes = self.nodes.borrow_mut();
-        nodes.push(node);
-        nodes.len() - 1
+
+        let len = nodes.len();
+        nodes.push(node.node);
+
+        node.link_idx.set(len);
     }
 }
 
@@ -531,11 +547,18 @@ impl BumpFrame {
 #[derive(Default)]
 pub(crate) struct HookList {
     arena: Bump,
-    vals: RefCell<Vec<*mut dyn Any>>,
+    vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
     idx: Cell<usize>,
 }
 
 impl HookList {
+    pub fn new(capacity: usize) -> Self {
+        Self {
+            arena: Bump::with_capacity(capacity),
+            ..Default::default()
+        }
+    }
+
     pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
         self.vals.borrow().get(self.idx.get()).and_then(|inn| {
             self.idx.set(self.idx.get() + 1);
@@ -571,7 +594,7 @@ impl HookList {
         self.cur_idx() >= self.len()
     }
 
-    pub fn clear_hooks(&mut self) {
+    pub fn clear(&mut self) {
         self.vals.borrow_mut().drain(..).for_each(|state| {
             let as_mut = unsafe { &mut *state };
             let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };

+ 104 - 73
packages/core/src/scopearena.rs

@@ -1,3 +1,4 @@
+use fxhash::FxHashMap;
 use slab::Slab;
 use std::cell::{Cell, RefCell};
 
@@ -7,7 +8,6 @@ use futures_channel::mpsc::UnboundedSender;
 use crate::innerlude::*;
 
 pub type FcSlot = *const ();
-// pub heuristics: FxHashMap<FcSlot, Heuristic>,
 
 pub struct Heuristic {
     hook_arena_size: usize,
@@ -21,6 +21,7 @@ pub struct Heuristic {
 pub(crate) struct ScopeArena {
     bump: Bump,
     scopes: RefCell<Vec<*mut Scope>>,
+    pub heuristics: RefCell<FxHashMap<FcSlot, Heuristic>>,
     free_scopes: RefCell<Vec<ScopeId>>,
     nodes: RefCell<Slab<*const VNode<'static>>>,
     pub(crate) sender: UnboundedSender<SchedulerMsg>,
@@ -31,6 +32,7 @@ impl ScopeArena {
         Self {
             bump: Bump::new(),
             scopes: RefCell::new(Vec::new()),
+            heuristics: RefCell::new(FxHashMap::default()),
             free_scopes: RefCell::new(Vec::new()),
             nodes: RefCell::new(Slab::new()),
             sender,
@@ -68,33 +70,53 @@ impl ScopeArena {
         } else {
             let scope_id = ScopeId(self.scopes.borrow().len());
 
-            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 (node_capacity, hook_capacity) = {
+                let heuristics = self.heuristics.borrow();
+                if let Some(heuristic) = heuristics.get(&fc_ptr) {
+                    (heuristic.node_arena_size, heuristic.hook_arena_size)
+                } else {
+                    (0, 0)
+                }
             };
 
-            let new_scope = Scope {
+            let mut frames = [BumpFrame::new(node_capacity), BumpFrame::new(node_capacity)];
+
+            frames[0].nodes.get_mut().push({
+                let vnode = frames[0]
+                    .bump
+                    .alloc(VNode::Text(frames[0].bump.alloc(VText {
+                        dom_id: Default::default(),
+                        is_static: false,
+                        text: "",
+                    })));
+                unsafe { std::mem::transmute(vnode as *mut VNode) }
+            });
+
+            frames[1].nodes.get_mut().push({
+                let vnode = frames[1]
+                    .bump
+                    .alloc(VNode::Text(frames[1].bump.alloc(VText {
+                        dom_id: Default::default(),
+                        is_static: false,
+                        text: "",
+                    })));
+                unsafe { std::mem::transmute(vnode as *mut VNode) }
+            });
+
+            let scope = self.bump.alloc(Scope {
                 sender: self.sender.clone(),
                 parent_scope,
                 our_arena_idx: scope_id,
                 height,
                 subtree: Cell::new(subtree),
                 is_subtree_root: Cell::new(false),
-                frames: [BumpFrame::new(), BumpFrame::new()],
+                frames,
 
-                hooks: Default::default(),
+                hooks: HookList::new(hook_capacity),
                 shared_contexts: Default::default(),
                 caller,
                 generation: 0.into(),
 
-                old_root: RefCell::new(Some(old_root)),
-                new_root: RefCell::new(Some(new_root)),
                 items: RefCell::new(SelfReferentialItems {
                     listeners: Default::default(),
                     borrowed_props: Default::default(),
@@ -102,16 +124,42 @@ impl ScopeArena {
                     tasks: Default::default(),
                     pending_effects: Default::default(),
                 }),
-            };
+            });
 
-            let stable = self.bump.alloc(new_scope);
-            self.scopes.borrow_mut().push(stable);
+            self.scopes.borrow_mut().push(scope);
             scope_id
         }
     }
 
-    pub fn try_remove(&self, id: &ScopeId) -> Option<Scope> {
-        todo!()
+    pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
+        self.ensure_drop_safety(id);
+
+        let mut scope = unsafe { &mut *self.get_scope_raw(id)? };
+
+        // we're just reusing scopes so we need to clear it out
+        scope.hooks.clear();
+        scope.shared_contexts.get_mut().clear();
+        scope.parent_scope = None;
+        scope.generation.set(0);
+        scope.is_subtree_root.set(false);
+        scope.subtree.set(0);
+
+        let SelfReferentialItems {
+            borrowed_props,
+            listeners,
+            pending_effects,
+            suspended_nodes,
+            tasks,
+        } = scope.items.get_mut();
+
+        borrowed_props.clear();
+        listeners.clear();
+        pending_effects.clear();
+        suspended_nodes.clear();
+        tasks.clear();
+
+        self.free_scopes.borrow_mut().push(*id);
+        Some(())
     }
 
     pub fn reserve_node(&self, node: &VNode) -> ElementId {
@@ -123,16 +171,10 @@ impl ScopeArena {
         let node = unsafe { std::mem::transmute::<*const VNode, *const VNode>(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) {
-        todo!()
+        self.nodes.borrow_mut().remove(id.0);
     }
 
     // These methods would normally exist on `scope` but they need access to *all* of the scopes
@@ -152,35 +194,25 @@ impl ScopeArena {
     pub(crate) fn ensure_drop_safety(&self, scope_id: &ScopeId) {
         let scope = self.get_scope(scope_id).unwrap();
 
+        let mut items = scope.items.borrow_mut();
+
         // make sure we drop all borrowed props manually to guarantee that their drop implementation is called before we
         // run the hooks (which hold an &mut Reference)
-        // right now, we don't drop
-        scope
-            .items
-            .borrow_mut()
-            .borrowed_props
-            .drain(..)
-            .for_each(|comp| {
-                // First drop the component's undropped references
-                let scope_id = comp
-                    .associated_scope
-                    .get()
-                    .expect("VComponents should be associated with a valid Scope");
+        // recursively call ensure_drop_safety on all children
+        items.borrowed_props.drain(..).for_each(|comp| {
+            let scope_id = comp
+                .associated_scope
+                .get()
+                .expect("VComponents should be associated with a valid Scope");
 
-                todo!("move this onto virtualdom");
-                // let scope = unsafe { &mut *scope_id };
+            self.ensure_drop_safety(&scope_id);
 
-                // scope.ensure_drop_safety();
-
-                todo!("drop the component's props");
-                // let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
-                // drop_props();
-            });
+            let mut drop_props = comp.drop_props.borrow_mut().take().unwrap();
+            drop_props();
+        });
 
         // Now that all the references are gone, we can safely drop our own references in our listeners.
-        scope
-            .items
-            .borrow_mut()
+        items
             .listeners
             .drain(..)
             .for_each(|listener| drop(listener.callback.borrow_mut().take()));
@@ -212,59 +244,58 @@ impl ScopeArena {
 
             // just forget about our suspended nodes while we're at it
             items.suspended_nodes.clear();
+            items.tasks.clear();
+            items.pending_effects.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());
+            debug_assert!(items.suspended_nodes.is_empty());
+            debug_assert!(items.tasks.is_empty());
+            debug_assert!(items.pending_effects.is_empty());
 
-            log::debug!("Borrowed stuff is successfully cleared");
-
-            // temporarily cast the vcomponent to the right lifetime
-            // let vcomp = scope.load_vcomp();
+            // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders.
+            scope.wip_frame().nodes.borrow_mut().clear();
         }
 
         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);
+        if let Some(link) = render(scope) {
+            // right now, it's a panic to render a nodelink from another scope
+            // todo: enable this. it should (reasonably) work even if it doesnt make much sense
+            assert_eq!(link.scope_id.get(), Some(*id));
 
-            dbg!(&scope.wip_frame().nodes.borrow_mut());
-            // let mut old = scope.old_root.borrow_mut();
-            // let mut new = scope.new_root.borrow_mut();
+            // nodelinks are not assigned when called and must be done so through the create/diff phase
+            // however, we need to link this one up since it will never be used in diffing
+            scope.wip_frame().assign_nodelink(&link);
+            debug_assert_eq!(scope.wip_frame().nodes.borrow().len(), 1);
 
-            // let new_old = new.clone();
-            // *old = new_old;
-            // *new = Some(key);
+            if !scope.items.borrow().tasks.is_empty() {
+                // self.
+            }
 
-            // 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
         }
     }
 
+    // The head of the bumpframe is the first linked NodeLink
     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();
+        let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
         unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
     }
 
+    // The head of the bumpframe is the first linked NodeLink
     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: &VNode = nodes.get(0).unwrap();
+        let node: &VNode = unsafe { &**nodes.get(0).unwrap() };
         unsafe { std::mem::transmute::<&VNode, &VNode>(node) }
     }
 

+ 48 - 77
packages/core/src/virtual_dom.rs

@@ -13,6 +13,9 @@ use std::{any::Any, collections::VecDeque};
 
 /// A virtual node system that progresses user events and diffs UI trees.
 ///
+///
+/// ## Guide
+///
 /// Components are defined as simple functions that take [`Context`] and a [`Properties`] type and return an [`Element`].  
 ///
 /// ```rust
@@ -105,14 +108,16 @@ pub struct VirtualDom {
 
     _root_caller: *mut dyn Fn(&Scope) -> Element,
 
-    pub(crate) scopes: Box<ScopeArena>,
+    scopes: Box<ScopeArena>,
 
     receiver: UnboundedReceiver<SchedulerMsg>,
-    pub(crate) sender: UnboundedSender<SchedulerMsg>,
 
-    // Every component that has futures that need to be polled
+    sender: UnboundedSender<SchedulerMsg>,
+
     pending_futures: FxHashSet<ScopeId>,
+
     pending_messages: VecDeque<SchedulerMsg>,
+
     dirty_scopes: IndexSet<ScopeId>,
 }
 
@@ -130,7 +135,7 @@ impl VirtualDom {
     ///
     /// # Example
     /// ```
-    /// fn Example(cx: Context<()>) -> DomTree  {
+    /// fn Example(cx: Context, props: &()) -> Element  {
     ///     cx.render(rsx!( div { "hello world" } ))
     /// }
     ///
@@ -159,7 +164,7 @@ impl VirtualDom {
     ///     name: &'static str
     /// }
     ///
-    /// fn Example(cx: Context<SomeProps>) -> DomTree  {
+    /// fn Example(cx: Context, props: &SomeProps) -> Element  {
     ///     cx.render(rsx!{ div{ "hello {cx.name}" } })
     /// }
     ///
@@ -172,7 +177,7 @@ impl VirtualDom {
     /// let mut dom = VirtualDom::new_with_props(Example, SomeProps { name: "jane" });
     /// let mutations = dom.rebuild();
     /// ```
-    pub fn new_with_props<P: 'static + Send>(root: FC<P>, root_props: P) -> Self {
+    pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
         let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
         Self::new_with_props_and_scheduler(root, root_props, sender, receiver)
     }
@@ -230,8 +235,10 @@ impl VirtualDom {
     ///
     /// # Example
     ///
+    /// ```rust
+    ///
     ///
-    ///    
+    /// ```
     pub fn get_scheduler_channel(&self) -> futures_channel::mpsc::UnboundedSender<SchedulerMsg> {
         self.sender.clone()
     }
@@ -240,8 +247,10 @@ impl VirtualDom {
     ///
     /// # Example
     ///
+    /// ```rust
     ///
     ///
+    /// ```
     pub fn has_any_work(&self) -> bool {
         !(self.dirty_scopes.is_empty() && self.pending_messages.is_empty())
     }
@@ -347,7 +356,9 @@ impl VirtualDom {
     /// # Example
     ///
     /// ```no_run
-    /// static App: FC<()> = |cx, props|rsx!(cx, div {"hello"} );
+    /// fn App(cx: Context, props: &()) -> Element {
+    ///     cx.render(rsx!( div {"hello"} ))
+    /// }
     ///
     /// let mut dom = VirtualDom::new(App);
     ///
@@ -368,6 +379,7 @@ impl VirtualDom {
     /// applied the edits.
     ///
     /// Mutations are the only link between the RealDOM and the VirtualDOM.
+    ///
     pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
         let mut committed_mutations = vec![];
 
@@ -436,10 +448,7 @@ impl VirtualDom {
                 }
             }
 
-            // let scopes = &mut self.scopes;
             let work_completed = diff_state.work(&mut deadline);
-            // let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
-            // let work_completed = crate::diff::work(&mut diff_state, &mut deadline);
 
             if work_completed {
                 let DiffState {
@@ -454,7 +463,7 @@ impl VirtualDom {
                 }
 
                 // I think the stack should be empty at the end of diffing?
-                debug_assert_eq!(stack.scope_stack.len(), 0);
+                debug_assert_eq!(stack.scope_stack.len(), 1);
 
                 committed_mutations.push(mutations);
             } else {
@@ -536,74 +545,45 @@ impl VirtualDom {
     /// let edits = dom.diff();
     /// ```
     pub fn hard_diff<'a>(&'a mut self, scope_id: &ScopeId) -> Option<Mutations<'a>> {
-        log::debug!("hard diff {:?}", scope_id);
-
+        let mut diff_machine = DiffState::new(&self.scopes);
         if self.scopes.run_scope(scope_id) {
-            let mut diff_machine = DiffState::new(&self.scopes);
-
             diff_machine.force_diff = true;
-
-            diff_machine.work(|| false);
-            // let scopes = &mut self.scopes;
-            // crate::diff::diff_scope(&mut diff_machine, scope_id);
-            // self.scopes.diff_scope(&mut diff_machine, scope_id);
-
-            // dbg!(&diff_machine.mutations);
-            // let DiffState {
-            //     mutations,
-            //     stack,
-            //     seen_scopes,
-            //     force_diff,
-            //     ..
-            // } = mutations;
-
-            // let mutations = diff_machine.mutations;
-
-            // Some(unsafe { std::mem::transmute(mutations) })
-            todo!()
-        } else {
-            None
+            diff_machine.diff_scope(scope_id);
         }
+        Some(diff_machine.mutations)
     }
 
     /// Renders an `rsx` call into the Base Scope's allocator.
     ///
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     pub fn render_vnodes<'a>(&'a self, lazy_nodes: Option<LazyNodes<'a, '_>>) -> &'a VNode<'a> {
-        todo!()
+        let scope = self.scopes.get_scope(&self.base_scope).unwrap();
+        let frame = scope.wip_frame();
+        let factory = NodeFactory { bump: &frame.bump };
+        let node = lazy_nodes.unwrap().call(factory);
+        frame.bump.alloc(node)
     }
 
     /// Renders an `rsx` call into the Base Scope's allocator.
     ///
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.    
     pub fn diff_vnodes<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> {
-        todo!()
-        // let mutations = Mutations::new();
-        // let mut machine: DiffState = todo!();
-        // // let mut machine = DiffState::new(mutations);
-        // // let mut machine = DiffState::new(mutations);
-        // machine.stack.push(DiffInstruction::Diff { new, old });
-        // machine.mutations
+        let mut machine = DiffState::new(&self.scopes);
+        machine.stack.push(DiffInstruction::Diff { new, old });
+        machine.stack.scope_stack.push(self.base_scope);
+        machine.work(|| false);
+        machine.mutations
     }
 
     /// Renders an `rsx` call into the Base Scope's allocator.
     ///
     /// Useful when needing to render nodes from outside the VirtualDom, such as in a test.
     pub fn create_vnodes<'a>(&'a self, left: Option<LazyNodes<'a, '_>>) -> Mutations<'a> {
-        todo!()
-        // let old = self.bump.alloc(self.render_direct(left));
-
-        // let mut machine: DiffState = todo!();
-        // // let mut machine = DiffState::new(Mutations::new());
-        // // let mut machine = DiffState::new(Mutations::new());
-
-        // machine.stack.create_node(old, MountType::Append);
-
-        // todo!()
-
-        // // machine.work(&mut || false);
-
-        // // machine.mutations
+        let nodes = self.render_vnodes(left);
+        let mut machine = DiffState::new(&self.scopes);
+        machine.stack.create_node(nodes, MountType::Append);
+        machine.work(|| false);
+        machine.mutations
     }
 
     /// Renders an `rsx` call into the Base Scope's allocator.
@@ -616,26 +596,17 @@ impl VirtualDom {
     ) -> (Mutations<'a>, Mutations<'a>) {
         let (old, new) = (self.render_vnodes(left), self.render_vnodes(right));
 
-        // let mut machine: DiffState = todo!();
-        // let mut machine = DiffState::new(Mutations::new());
-
-        // machine.stack.create_node(old, MountType::Append);
-
-        todo!()
-
-        // machine.work(|| false);
-        // let create_edits = machine.mutations;
-
-        // let mut machine: DiffState = todo!();
-        // // let mut machine = DiffState::new(Mutations::new());
-
-        // machine.stack.push(DiffInstruction::Diff { old, new });
-
-        // machine.work(&mut || false);
+        let mut create = DiffState::new(&self.scopes);
+        create.stack.scope_stack.push(self.base_scope);
+        create.stack.create_node(old, MountType::Append);
+        create.work(|| false);
 
-        // let edits = machine.mutations;
+        let mut edit = DiffState::new(&self.scopes);
+        create.stack.scope_stack.push(self.base_scope);
+        edit.stack.push(DiffInstruction::Diff { old, new });
+        edit.work(&mut || false);
 
-        // (create_edits, edits)
+        (create.mutations, edit.mutations)
     }
 }
 

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

@@ -215,12 +215,12 @@ fn create_components() {
         })
     };
 
-    #[derive(Props)]
-    struct ChildProps<'a> {
-        children: ScopeChildren<'a>,
+    #[derive(Props, PartialEq)]
+    struct ChildProps {
+        children: Element,
     }
 
-    fn Child<'a>(cx: Context<'a>, props: &ChildProps<'a>) -> Element {
+    fn Child(cx: Context, props: &ChildProps) -> Element {
         cx.render(rsx! {
             h1 {}
             div { {&props.children} }
@@ -299,21 +299,3 @@ fn anchors() {
         ]
     );
 }
-
-#[test]
-fn suspended() {
-    todo!()
-    // static App: FC<()> = |cx, props| {
-    //     let val = use_suspense(cx, || async {}, |p| todo!());
-
-    //     cx.render(rsx! { {val} })
-    // };
-
-    // let mut dom = new_dom(App, ());
-    // let mutations = dom.rebuild();
-
-    // assert_eq!(
-    //     mutations.edits,
-    //     [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 },]
-    // );
-}

+ 3 - 22
packages/core/tests/diffing.rs

@@ -257,12 +257,12 @@ fn many_items_become_fragment() {
         ]
     );
 
-    // hmmmmmmmmm worried about reusing IDs that we shouldnt be
+    // note: the ID gets reused
     assert_eq!(
         change.edits,
         [
             Remove { root: 2 },
-            CreatePlaceholder { root: 4 },
+            CreatePlaceholder { root: 3 },
             ReplaceWith { root: 0, m: 1 },
         ]
     );
@@ -433,7 +433,7 @@ fn keyed_diffing_order() {
     let dom = new_dom();
 
     let left = rsx!(
-        // {(0..5).map(|f| {rsx! { div { key: "{f}"  }}})}
+        {(0..5).map(|f| {rsx! { div { key: "{f}"  }}})}
         p {"e"}
     );
     let right = rsx!(
@@ -802,22 +802,3 @@ fn controlled_keyed_diffing_out_of_order_max_test() {
         ]
     );
 }
-
-#[test]
-fn suspense() {
-    let dom = new_dom();
-
-    todo!()
-    // let edits = dom.create_vnodes(Some(LazyNodes::new(|f| {
-    //     use std::cell::{Cell, RefCell};
-    //     VNode::Suspended(f.bump().alloc(VSuspended {
-    //         task_id: 0,
-    //         callback: RefCell::new(None),
-    //         dom_id: Cell::new(None),
-    //     }))
-    // })));
-    // assert_eq!(
-    //     edits.edits,
-    //     [CreatePlaceholder { root: 0 }, AppendChildren { many: 1 }]
-    // );
-}

+ 3 - 4
packages/desktop/src/desktop_context.rs

@@ -1,7 +1,6 @@
 use std::cell::RefCell;
 
 use dioxus::prelude::Scope;
-use dioxus::ScopeChildren;
 use dioxus_core as dioxus;
 use dioxus_core::{Context, Element, LazyNodes, NodeFactory, Properties};
 use dioxus_core_macro::Props;
@@ -44,7 +43,7 @@ pub struct WebviewWindowProps<'a> {
     /// focuse me
     onfocused: &'a dyn FnMut(()),
 
-    children: ScopeChildren<'a>,
+    children: Element,
 }
 
 /// A handle to a
@@ -57,7 +56,7 @@ pub struct WebviewWindowProps<'a> {
 ///
 ///
 ///
-pub fn WebviewWindow((cx, props): Scope<WebviewWindowProps>) -> Element {
+pub fn WebviewWindow(cx: Context, props: &WebviewWindowProps) -> Element {
     let dtcx = cx.consume_state::<RefCell<DesktopContext>>()?;
 
     cx.use_hook(
@@ -91,7 +90,7 @@ fn syntax_works() {
     use dioxus_hooks::*;
     use dioxus_html as dioxus_elements;
 
-    static App: FC<()> = |(cx, props)| {
+    static App: FC<()> = |cx, props| {
         cx.render(rsx! {
             // left window
             WebviewWindow {

+ 3 - 4
packages/desktop/src/lib.rs

@@ -166,7 +166,7 @@ pub fn run<T: 'static + Send + Sync>(
                                 sender.unbounded_send(SchedulerMsg::UiEvent(event)).unwrap();
 
                                 if let Some(BridgeEvent::Update(edits)) = rx.blocking_recv() {
-                                    log::info!("bridge received message {:?}", edits);
+                                    log::info!("bridge received message");
                                     Some(RpcResponse::new_result(req.id.take(), Some(edits)))
                                 } else {
                                     log::info!("none received message");
@@ -276,7 +276,7 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
 
         runtime.block_on(async move {
             let mut vir = VirtualDom::new_with_props_and_scheduler(root, props, sender, receiver);
-            let _ = vir.get_event_sender();
+            let _ = vir.get_scheduler_channel();
 
             let edits = vir.rebuild();
 
@@ -298,14 +298,13 @@ pub(crate) fn launch_vdom_with_tokio<P: Send + 'static>(
                 // todo: maybe we want to schedule ourselves in
                 // on average though, the virtualdom running natively is stupid fast
 
-                let mut muts = vir.run_with_deadline(|| false);
+                let mut muts = vir.work_with_deadline(|| false);
 
                 log::debug!("finished running with deadline");
 
                 let mut edits = vec![];
 
                 while let Some(edit) = muts.pop() {
-                    log::debug!("sending message on channel with edit {:?}", edit);
                     let edit_string = serde_json::to_value(Evt { edits: edit.edits })
                         .expect("serializing edits should never fail");
                     edits.push(edit_string);

+ 1 - 0
packages/ssr/index.html

@@ -0,0 +1 @@
+asd

+ 11 - 9
packages/ssr/src/lib.rs

@@ -14,7 +14,7 @@ use std::fmt::{Display, Formatter};
 
 use dioxus_core::exports::bumpalo;
 use dioxus_core::exports::bumpalo::Bump;
-use dioxus_core::nodes::IntoVNode;
+use dioxus_core::IntoVNode;
 use dioxus_core::*;
 
 /// A memory pool for rendering
@@ -94,7 +94,7 @@ pub fn render_vdom_scope(vdom: &VirtualDom, scope: ScopeId) -> Option<String> {
         "{:}",
         TextRenderer {
             cfg: SsrConfig::default(),
-            root: vdom.get_scope(scope).unwrap().root_node(),
+            root: vdom.get_scope(&scope).unwrap().root_node(),
             vdom: Some(vdom)
         }
     ))
@@ -158,6 +158,9 @@ impl<'a> TextRenderer<'a, '_> {
                 }
                 write!(f, "<!-- -->")?;
             }
+            VNode::Linked(link) => {
+                todo!();
+            }
             VNode::Element(el) => {
                 if self.cfg.indent {
                     for _ in 0..il {
@@ -244,7 +247,7 @@ impl<'a> TextRenderer<'a, '_> {
                 let idx = vcomp.associated_scope.get().unwrap();
 
                 if let (Some(vdom), false) = (self.vdom, self.cfg.skip_components) {
-                    let new_node = vdom.get_scope(idx).unwrap().root_node();
+                    let new_node = vdom.get_scope(&idx).unwrap().root_node();
                     self.html_render(new_node, f, il + 1)?;
                 } else {
                 }
@@ -297,18 +300,17 @@ impl SsrConfig {
 mod tests {
     use super::*;
 
-    use dioxus_core as dioxus;
     use dioxus_core::prelude::*;
     use dioxus_core_macro::*;
     use dioxus_html as dioxus_elements;
 
-    static SIMPLE_APP: FC<()> = |(cx, _)| {
+    static SIMPLE_APP: FC<()> = |cx, _| {
         cx.render(rsx!(div {
             "hello world!"
         }))
     };
 
-    static SLIGHTLY_MORE_COMPLEX: FC<()> = |(cx, _)| {
+    static SLIGHTLY_MORE_COMPLEX: FC<()> = |cx, _| {
         cx.render(rsx! {
             div {
                 title: "About W3Schools"
@@ -327,14 +329,14 @@ mod tests {
         })
     };
 
-    static NESTED_APP: FC<()> = |(cx, _)| {
+    static NESTED_APP: FC<()> = |cx, _| {
         cx.render(rsx!(
             div {
                 SIMPLE_APP {}
             }
         ))
     };
-    static FRAGMENT_APP: FC<()> = |(cx, _)| {
+    static FRAGMENT_APP: FC<()> = |cx, _| {
         cx.render(rsx!(
             div { "f1" }
             div { "f2" }
@@ -390,7 +392,7 @@ mod tests {
 
     #[test]
     fn styles() {
-        static STLYE_APP: FC<()> = |(cx, _)| {
+        static STLYE_APP: FC<()> = |cx, _| {
             cx.render(rsx! {
                 div { color: "blue", font_size: "46px"  }
             })

+ 22 - 34
packages/web/src/dom.rs

@@ -7,17 +7,12 @@
 //! - tests to ensure dyn_into works for various event types.
 //! - Partial delegation?>
 
-use dioxus_core::{
-    events::{KeyCode, UserEvent},
-    mutations::NodeRefMutation,
-    scheduler::SchedulerMsg,
-    DomEdit, ElementId, ScopeId,
-};
+use dioxus_core::{DomEdit, ElementId, SchedulerMsg, ScopeId, UserEvent};
 use fxhash::FxHashMap;
-use std::{any::Any, fmt::Debug, rc::Rc, sync::Arc};
+use std::{any::Any, fmt::Debug, rc::Rc};
 use wasm_bindgen::{closure::Closure, JsCast};
 use web_sys::{
-    Attr, CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
+    CssStyleDeclaration, Document, Element, Event, HtmlElement, HtmlInputElement,
     HtmlOptionElement, HtmlTextAreaElement, Node, NodeList,
 };
 
@@ -55,7 +50,7 @@ impl WebsysDom {
         let document = load_document();
 
         let mut nodes = NodeSlab::new(2000);
-        let mut listeners = FxHashMap::default();
+        let listeners = FxHashMap::default();
 
         // re-hydrate the page - only supports one virtualdom per page
         if cfg.hydrate {
@@ -90,17 +85,17 @@ impl WebsysDom {
         }
     }
 
-    pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
-        for item in refs {
-            if let Some(bla) = &item.element {
-                let node = self.nodes[item.element_id.as_u64() as usize]
-                    .as_ref()
-                    .unwrap()
-                    .clone();
-                bla.set(Box::new(node)).unwrap();
-            }
-        }
-    }
+    // pub fn apply_refs(&mut self, refs: &[NodeRefMutation]) {
+    //     for item in refs {
+    //         if let Some(bla) = &item.element {
+    //             let node = self.nodes[item.element_id.as_u64() as usize]
+    //                 .as_ref()
+    //                 .unwrap()
+    //                 .clone();
+    //             bla.set(Box::new(node)).unwrap();
+    //         }
+    //     }
+    // }
 
     pub fn process_edits(&mut self, edits: &mut Vec<DomEdit>) {
         for edit in edits.drain(..) {
@@ -309,8 +304,8 @@ impl WebsysDom {
         }
     }
 
-    fn remove_event_listener(&mut self, event: &str, root: u64) {
-        // todo!()
+    fn remove_event_listener(&mut self, _event: &str, _root: u64) {
+        todo!()
     }
 
     fn set_text(&mut self, text: &str, root: u64) {
@@ -489,8 +484,9 @@ unsafe impl Sync for DioxusWebsysEvent {}
 // todo: some of these events are being casted to the wrong event type.
 // We need tests that simulate clicks/etc and make sure every event type works.
 fn virtual_event_from_websys_event(event: web_sys::Event) -> Box<dyn Any + Send> {
-    use crate::events::*;
-    use dioxus_core::events::on::*;
+    use dioxus_html::on::*;
+    use dioxus_html::KeyCode;
+    // use dioxus_core::events::on::*;
     match event.type_().as_str() {
         "copy" | "cut" | "paste" => Box::new(ClipboardEvent {}),
         "compositionend" | "compositionstart" | "compositionupdate" => {
@@ -682,15 +678,6 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
 
     let typ = event.type_();
 
-    // TODO: clean this up
-    if cfg!(debug_assertions) {
-        let attrs = target.attributes();
-        for x in 0..attrs.length() {
-            let attr: Attr = attrs.item(x).unwrap();
-            // log::debug!("attrs include: {:#?}, {:#?}", attr.name(), attr.value());
-        }
-    }
-
     use anyhow::Context;
 
     // The error handling here is not very descriptive and needs to be replaced with a zero-cost error system
@@ -716,7 +703,8 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result<UserEvent> {
         name: event_name_from_typ(&typ),
         event: virtual_event_from_websys_event(event.clone()),
         mounted_dom_id: Some(ElementId(real_id as usize)),
-        scope: ScopeId(triggered_scope as usize),
+        scope_id: ScopeId(triggered_scope as usize),
+        priority: dioxus_core::EventPriority::Medium,
     })
 }
 

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

@@ -1,5 +0,0 @@
-//! Ported events into Dioxus Synthetic Event system
-
-use dioxus_core::events::on::*;
-use wasm_bindgen::JsCast;
-use web_sys::{Event, UiEvent};

+ 6 - 7
packages/web/src/lib.rs

@@ -57,8 +57,8 @@ use std::rc::Rc;
 pub use crate::cfg::WebConfig;
 use crate::dom::load_document;
 use cache::intern_cached_strings;
-use dioxus::prelude::Properties;
-use dioxus::virtual_dom::VirtualDom;
+use dioxus::SchedulerMsg;
+use dioxus::VirtualDom;
 pub use dioxus_core as dioxus;
 use dioxus_core::prelude::FC;
 use futures_util::FutureExt;
@@ -66,7 +66,6 @@ use futures_util::FutureExt;
 mod cache;
 mod cfg;
 mod dom;
-mod events;
 mod nodeslab;
 mod ric_raf;
 
@@ -144,9 +143,10 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
 
     let root_el = load_document().get_element_by_id(&cfg.rootname).unwrap();
 
-    let tasks = dom.get_event_sender();
+    let tasks = dom.get_scheduler_channel();
 
-    let sender_callback = Rc::new(move |event| tasks.unbounded_send(event).unwrap());
+    let sender_callback: Rc<dyn Fn(SchedulerMsg)> =
+        Rc::new(move |event| tasks.unbounded_send(event).unwrap());
 
     let mut websys_dom = dom::WebsysDom::new(root_el, cfg, sender_callback);
 
@@ -170,13 +170,12 @@ pub async fn run_with_props<T: 'static + Send>(root: FC<T>, root_props: T, cfg:
         let mut deadline = work_loop.wait_for_idle_time().await;
 
         // run the virtualdom work phase until the frame deadline is reached
-        let mutations = dom.run_with_deadline(|| (&mut deadline).now_or_never().is_some());
+        let mutations = dom.work_with_deadline(|| (&mut deadline).now_or_never().is_some());
 
         // wait for the animation frame to fire so we can apply our changes
         work_loop.wait_for_raf().await;
 
         for mut edit in mutations {
-            // log::debug!("edits are {:#?}", edit);
             // actually apply our changes during the animation frame
             websys_dom.process_edits(&mut edit.edits);
         }