Prechádzať zdrojové kódy

Feat: props now autoderives its own trait

Jonathan Kelley 4 rokov pred
rodič
commit
b3c96a5

+ 19 - 0
packages/core-macro/examples/prop_test.rs

@@ -0,0 +1,19 @@
+fn main() {}
+
+pub mod dioxus {
+    pub mod prelude {
+        pub trait Properties {
+            type Builder;
+            fn builder() -> Self::Builder;
+        }
+    }
+}
+#[derive(dioxus_core_macro::Props)]
+struct SomeProps {
+    a: String,
+}
+
+#[derive(dioxus_core_macro::Props)]
+struct SomePropsTwo<'a> {
+    a: &'a str,
+}

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

@@ -678,6 +678,14 @@ Finally, call `.build()` to create the instance of `{name}`.
                         }
                     }
                 }
+
+                impl #impl_generics dioxus::prelude::Properties for #name #ty_generics{
+                    type Builder = #builder_name #generics_with_empty;
+                    fn builder() -> Self::Builder {
+                        #name::builder()
+                    }
+                }
+
             })
         }
 
@@ -1071,6 +1079,13 @@ Finally, call `.build()` to create the instance of `{name}`.
                 }
             )
         }
+
+        pub fn build_props_impl(&self) -> TokenStream {
+            // SomeProps: #name
+            // #builder_name
+            // #generics_with_empty
+            quote! {}
+        }
     }
 
     #[derive(Debug, Default)]

+ 0 - 7
packages/core/examples/borrowed.rs

@@ -69,13 +69,6 @@ impl PartialEq for ChildProps<'_> {
     }
 }
 
-impl<'a> Properties for ChildProps<'a> {
-    type Builder = ChildPropsBuilder<'a, ((), ())>;
-    fn builder() -> <Self as Properties>::Builder {
-        ChildProps::builder()
-    }
-}
-
 fn ChildItem(_ctx: Context, _props: &ChildProps) -> DomTree {
     todo!()
     //     ctx.render(rsx! {

+ 0 - 7
packages/core/examples/fc.rs

@@ -24,10 +24,3 @@ static SomeComponent: FC<ExampleProps> = |ctx, _props| {
 };
 
 fn main() {}
-
-impl Properties for ExampleProps {
-    type Builder = ExamplePropsBuilder<((),)>;
-    fn builder() -> Self::Builder {
-        ExampleProps::builder()
-    }
-}

+ 2 - 6
packages/core/examples/props.rs

@@ -1,6 +1,6 @@
-use dioxus_core_macro::Props;
+use dioxus_core::prelude::*;
 
-#[derive(Debug, Props)]
+#[derive(Debug, PartialEq, Props)]
 struct SomeProps {
     a: i32,
 
@@ -9,12 +9,8 @@ struct SomeProps {
     b: Option<i32>,
 }
 
-// have we committed to the trait style yet?
-
 fn main() {
     let g: SomeProps = SomeProps::builder().a(10).b(10).build();
 
     let _r = g.b.unwrap_or_else(|| 10);
 }
-
-fn auto_into_some() {}

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

@@ -23,11 +23,3 @@ static Example: FC<SomeProps> = |ctx, _props| {
         </div>
     })
 };
-
-// toodo: derive this
-impl Properties for SomeProps {
-    type Builder = SomePropsBuilder<((),)>;
-    fn builder() -> Self::Builder {
-        SomeProps::builder()
-    }
-}

+ 8 - 6
packages/core/src/diff.rs

@@ -42,7 +42,7 @@ use std::{
     cell::{RefCell, RefMut},
     cmp::Ordering,
     collections::VecDeque,
-    rc::Rc,
+    rc::{Rc, Weak},
 };
 
 /// The DiffState is a cursor internal to the VirtualDOM's diffing algorithm that allows persistence of state while
@@ -62,11 +62,9 @@ pub struct DiffMachine<'a> {
     pub diffed: FxHashSet<ScopeIdx>,
     pub lifecycle_events: VecDeque<LifeCycleEvent<'a>>,
 }
-
-// #[derive(Debug)]
 pub enum LifeCycleEvent<'a> {
     Mount {
-        caller: Rc<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
+        caller: Weak<dyn for<'r> Fn(Context<'r>) -> DomTree + 'a>,
         id: Uuid,
     },
     PropsChanged,
@@ -76,7 +74,6 @@ pub enum LifeCycleEvent<'a> {
 
 impl<'a> DiffMachine<'a> {
     pub fn new() -> Self {
-        // pub fn new(bump: &'a Bump) -> Self {
         Self {
             lifecycle_events: VecDeque::new(),
             change_list: EditMachine::new(),
@@ -265,8 +262,13 @@ impl<'a> DiffMachine<'a> {
                 let id = uuid::Uuid::new_v4();
                 *component.stable_addr.as_ref().borrow_mut() = Some(id);
                 self.change_list.save_known_root(id);
+
+                let caller = Rc::downgrade(&component.caller);
+                // let broken_caller: Weak<dyn Fn(Context) -> DomTree + 'static> =
+                //     unsafe { std::mem::transmute(caller) };
                 self.lifecycle_events.push_back(LifeCycleEvent::Mount {
-                    caller: component.caller.clone(),
+                    caller,
+                    // caller: broken_caller,
                     id,
                 });
             }

+ 33 - 20
packages/core/src/patch.rs

@@ -23,38 +23,42 @@ pub type EditList<'src> = Vec<Edit<'src>>;
 
 /// The `Edit` represents a single modifcation of the renderer tree.
 /// todo@ jon: allow serde to be optional
+/// todo @jon, go through and make certain fields static. tag names should be known at compile time
 #[derive(Debug, serde::Serialize, serde::Deserialize)]
 #[serde(tag = "type")]
-pub enum Edit<'d> {
+pub enum Edit<'src_bump> {
     // ========================================================
     // Common Ops: The most common operation types
     // ========================================================
     SetText {
-        text: &'d str,
+        text: &'src_bump str,
     },
     SetClass {
-        class_name: &'d str,
+        class_name: &'src_bump str,
     },
     CreateTextNode {
-        text: &'d str,
+        text: &'src_bump str,
     },
     CreateElement {
-        tag_name: &'d str,
+        // todo - make static?
+        tag_name: &'src_bump str,
     },
     CreateElementNs {
-        tag_name: &'d str,
-        ns: &'d str,
+        // todo - make static?
+        tag_name: &'src_bump str,
+        // todo - make static?
+        ns: &'src_bump str,
     },
 
     // ========================================================
     // Attributes
     // ========================================================
     SetAttribute {
-        name: &'d str,
-        value: &'d str,
+        name: &'src_bump str,
+        value: &'src_bump str,
     },
     RemoveAttribute {
-        name: &'d str,
+        name: &'src_bump str,
     },
     RemoveChild {
         n: u32,
@@ -64,17 +68,20 @@ pub enum Edit<'d> {
     // Event Listeners: Event types and IDs used to update the VDOM
     // ============================================================
     NewListener {
-        event: &'d str,
+        // todo - make static?
+        event: &'src_bump str,
         scope: ScopeIdx,
         id: usize,
     },
     UpdateListener {
-        event: &'d str,
+        // todo - make static?
+        event: &'src_bump str,
         scope: ScopeIdx,
         id: usize,
     },
     RemoveListener {
-        event: &'d str,
+        // todo - make static?
+        event: &'src_bump str,
     },
 
     // ========================================================
@@ -124,22 +131,28 @@ pub enum Edit<'d> {
     },
 }
 
-pub struct EditMachine<'src> {
+/// The edit machine represents a stream of differences between two component trees.
+///
+/// This struct is interesting in that it keeps track of differences by borrowing
+/// from the source rather than writing to a new buffer. This means that the virtual dom
+/// *cannot* be updated while this machine is in existence without "unsafe".
+///
+/// This unsafety is handled by methods on the virtual dom and is not exposed via lib code.
+pub struct EditMachine<'lock> {
     pub traversal: Traversal,
     next_temporary: u32,
     forcing_new_listeners: bool,
-    pub emitter: EditList<'src>,
+
+    pub emitter: EditList<'lock>,
 }
 
-impl<'b> EditMachine<'b> {
+impl<'lock> EditMachine<'lock> {
     pub fn new() -> Self {
-        // pub fn new(_bump: &'b bumpalo::Bump) -> Self {
-        // todo: see if bumpalo is needed for edit list
         Self {
             traversal: Traversal::new(),
             next_temporary: 0,
             forcing_new_listeners: false,
-            emitter: EditList::<'b>::default(),
+            emitter: EditList::<'lock>::default(),
         }
     }
 }
@@ -147,7 +160,7 @@ impl<'b> EditMachine<'b> {
 // ===================================
 //  Traversal Methods
 // ===================================
-impl<'b> EditMachine<'b> {
+impl<'src> EditMachine<'src> {
     pub fn go_down(&mut self) {
         self.traversal.down();
     }

+ 3 - 0
packages/core/src/scope.rs

@@ -97,6 +97,8 @@ impl Scope {
     // 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.
     // This should never happen, but is a good check to keep around
     pub fn new<'creator_node>(
+        // pub fn new(
+        // caller: Weak<dyn Fn(Context) -> DomTree + 'static>,
         caller: Weak<dyn Fn(Context) -> DomTree + 'creator_node>,
         myidx: ScopeIdx,
         parent: Option<ScopeIdx>,
@@ -106,6 +108,7 @@ impl Scope {
         let broken_caller: Weak<dyn Fn(Context) -> DomTree + 'static> =
             unsafe { std::mem::transmute(caller) };
 
+        // let broken_caller = caller;
         Self {
             caller: broken_caller,
             hook_arena: typed_arena::Arena::new(),

+ 44 - 30
packages/core/src/virtual_dom.rs

@@ -101,6 +101,9 @@ impl VirtualDom {
             component.run();
         }
 
+        // get raw pointer to the arena
+        let very_unsafe_components = &mut self.components as *mut generational_arena::Arena<Scope>;
+
         {
             let component = self
                 .components
@@ -110,36 +113,47 @@ impl VirtualDom {
             diff_machine.diff_node(component.old_frame(), component.new_frame());
         }
 
-        // 'render: loop {
-        //     for event in &mut diff_machine.lifecycle_events.drain(..) {
-        //         log::debug!("event is {:#?}", event);
-        //         match event {
-        //             LifeCycleEvent::Mount { caller, id } => {
-        //                 diff_machine.change_list.load_known_root(id);
-        //                 let idx = self
-        //                     .components
-        //                     .insert_with(|f| create_scoped(caller, f, None));
-        //                 // .insert_with(|f| create_scoped(caller, props, myidx, parent));
-        //             }
-        //             LifeCycleEvent::PropsChanged => {
-        //                 //
-        //                 break 'render;
-        //             }
-        //             LifeCycleEvent::SameProps => {
-        //                 //
-        //                 break 'render;
-        //             }
-        //             LifeCycleEvent::Remove => {
-        //                 //
-        //                 break 'render;
-        //             }
-        //         }
-        //     }
-
-        //     if diff_machine.lifecycle_events.is_empty() {
-        //         break 'render;
-        //     }
-        // }
+        // chew down the the lifecycle events until all dirty nodes are computed
+        while let Some(event) = diff_machine.lifecycle_events.pop_front() {
+            match event {
+                // A new component has been computed from the diffing algorithm
+                // create a new component in the arena, run it, move the diffing machine to this new spot, and then diff it
+                // this will flood the lifecycle queue with new updates
+                LifeCycleEvent::Mount { caller, id } => {
+                    log::debug!("Mounting a new component");
+                    diff_machine.change_list.load_known_root(id);
+
+                    // We're modifying the component arena while holding onto references into the assoiated bump arenas of its children
+                    // those references are stable, even if the component arena moves around in memory, thanks to the bump arenas.
+                    // However, there is no way to convey this to rust, so we need to use unsafe to pierce through the lifetime.
+                    unsafe {
+                        let p = &mut *(very_unsafe_components);
+
+                        // todo, hook up the parent/child indexes properly
+                        let idx = p.insert_with(|f| Scope::new(caller, f, None));
+                        let c = p.get_mut(idx).unwrap();
+                        c.run();
+                        diff_machine.diff_node(c.old_frame(), c.new_frame());
+                    }
+                }
+                LifeCycleEvent::PropsChanged => {
+                    //
+                    // break 'render;
+                }
+                LifeCycleEvent::SameProps => {
+                    //
+                    // break 'render;
+                }
+                LifeCycleEvent::Remove => {
+                    //
+                    // break 'render;
+                }
+            }
+
+            // } else {
+            //     break 'render;
+            // }
+        }
 
         let edits: Vec<Edit<'s>> = diff_machine.consume();
         Ok(edits)