瀏覽代碼

WIP: moving to CbIdx as serializable event system

Jonathan Kelley 4 年之前
父節點
當前提交
e840f47

+ 1 - 3
README.md

@@ -16,11 +16,9 @@ static Example: FC<()> = |ctx| {
 
     ctx.view(html! {
         <div>
+            <h1> "Hello, {value}" </h1>
             <button onclick={move |_| set_value("world!")}> "?" </button>
             <button onclick={move |_| set_value("Dioxus 🎉")}> "?" </button>
-            <div>
-                <h1> "Hello, {value}" </h1>
-            </div>
         </div>
     })
 };

+ 10 - 5
notes/CHANGELOG.md

@@ -1,6 +1,4 @@
-# Project: Web-View 🤲 🍨
-> Proof of concept: stream render edits from server to client
-- [x] Prove that the diffing and patching framework can support patch streaming
+
 
 
 # Project: Live-View 🤲 🍨
@@ -8,6 +6,7 @@
 
 
 # Project: Sanitization (TBD)
+> Improve code health
 - [ ] (Macro) Clippy sanity for html macro
 - [ ] (Macro) Error sanitization
 
@@ -22,13 +21,19 @@
 # Project: Concurrency (TBD)
 > Ensure the concurrency model works well, play with lifetimes to check if it can be multithreaded + halted
 
+
+# Project: Web-View 🤲 🍨
+> Proof of concept: stream render edits from server to client
+- [x] Prove that the diffing and patching framework can support patch streaming
+
 # Project: Web_sys renderer (TBD)
-- [ ] (Web) Web-sys renderer and web tests
+- [x] WebSys edit interpreter
+- [ ] Event system using async channels
 
 # Project: String Render (TBD)
 > Implement a light-weight string renderer with basic caching 
 - [ ] (SSR) Implement stateful 3rd party string renderer
-- [ ] (Macro) Make VText nodes automatically capture and format IE allow "Text is {blah}" in place of {format!("Text is {}",blah)}
+- [x] (Macro) Make VText nodes automatically capture and format IE allow "Text is {blah}"
 
 # Project: Hooks + Context + Subscriptions (TBD)
 > Implement the foundations for state management

+ 47 - 24
packages/core/src/changelist.rs

@@ -22,7 +22,7 @@
 use bumpalo::Bump;
 
 use crate::innerlude::Listener;
-
+use serde::{Deserialize, Serialize};
 /// The `Edit` represents a single modifcation of the renderer tree.
 ///
 ///
@@ -33,7 +33,7 @@ use crate::innerlude::Listener;
 ///
 ///
 /// todo@ jon: allow serde to be optional
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
+#[derive(Debug, Serialize, Deserialize)]
 #[serde(tag = "type")]
 pub enum Edit<'d> {
     SetText { text: &'d str },
@@ -47,8 +47,8 @@ pub enum Edit<'d> {
     AppendChild,
     CreateTextNode { text: &'d str },
     CreateElement { tag_name: &'d str },
-    NewEventListener { event_type: &'d str, a: u32, b: u32 },
-    UpdateEventListener { event_type: &'d str, a: u32, b: u32 },
+    NewEventListener { event_type: &'d str, idx: CbIdx },
+    UpdateEventListener { event_type: &'d str, idx: CbIdx },
     RemoveEventListener { event_type: &'d str },
     CreateElementNs { tag_name: &'d str, ns: &'d str },
     SaveChildrenToTemporaries { temp: u32, start: u32, end: u32 },
@@ -60,6 +60,26 @@ pub enum Edit<'d> {
     SetClass { class_name: &'d str },
 }
 
+/// Re-export a cover over generational ID for libraries that don't need it
+/// We can go back and forth between the two via methods on GI
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
+pub struct CbIdx {
+    gi_id: usize,
+    gi_gen: u64,
+    listener_idx: usize,
+}
+
+impl CbIdx {
+    pub fn from_gi_index(index: generational_arena::Index, listener_idx: usize) -> Self {
+        let (gi_id, gi_gen) = index.into_raw_parts();
+        Self {
+            gi_id,
+            gi_gen,
+            listener_idx,
+        }
+    }
+}
+
 pub type EditList<'src> = Vec<Edit<'src>>;
 
 pub struct EditMachine<'src> {
@@ -306,38 +326,41 @@ impl<'a> EditMachine<'a> {
         self.forcing_new_listeners = previous;
     }
 
-    pub fn new_event_listener(&mut self, listener: &Listener) {
+    pub fn new_event_listener(&mut self, event: &'a str, idx: CbIdx) {
         debug_assert!(self.traversal_is_committed());
-        // todo!("Event listener not wired up yet");
-        log::debug!("emit: new_event_listener({:?})", listener);
-        let (a, b) = listener.get_callback_parts();
-        debug_assert!(a != 0);
-        // let event_id = self.ensure_string(listener.event);
         self.emitter.push(Edit::NewEventListener {
-            event_type: listener.event.into(),
-            a,
-            b,
+            event_type: event,
+            idx,
         });
+        // todo!("Event listener not wired up yet");
+        // log::debug!("emit: new_event_listener({:?})", listener);
+        // let (a, b) = listener.get_callback_parts();
+        // debug_assert!(a != 0);
+        // // let event_id = self.ensure_string(listener.event);
         // self.emitter.new_event_listener(listener.event.into(), a, b);
     }
 
-    pub fn update_event_listener(&mut self, listener: &Listener) {
+    pub fn update_event_listener(&mut self, event: &'a str, idx: CbIdx) {
         debug_assert!(self.traversal_is_committed());
-
         if self.forcing_new_listeners {
-            self.new_event_listener(listener);
+            self.new_event_listener(event, idx);
             return;
         }
 
-        log::debug!("emit: update_event_listener({:?})", listener);
-        // todo!("Event listener not wired up yet");
-        let (a, b) = listener.get_callback_parts();
-        debug_assert!(a != 0);
-        self.emitter.push(Edit::UpdateEventListener {
-            event_type: listener.event.into(),
-            a,
-            b,
+        self.emitter.push(Edit::NewEventListener {
+            event_type: event,
+            idx,
         });
+
+        // log::debug!("emit: update_event_listener({:?})", listener);
+        // // todo!("Event listener not wired up yet");
+        // let (a, b) = listener.get_callback_parts();
+        // debug_assert!(a != 0);
+        // self.emitter.push(Edit::UpdateEventListener {
+        //     event_type: listener.event.into(),
+        //     a,
+        //     b,
+        // });
         // self.emitter.update_event_listener(event_id.into(), a, b);
     }
 

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

@@ -44,9 +44,9 @@ pub struct Context<'src> {
 }
 
 impl<'a> Context<'a> {
-    pub fn props<P>() -> &'a P {
-        todo!()
-    }
+    // pub fn props<P>() -> &'a P {
+    //     todo!()
+    // }
 
     // impl<'a, PropType> Context<'a, PropType> {
     /// Access the children elements passed into the component
@@ -56,7 +56,8 @@ impl<'a> Context<'a> {
 
     /// Create a subscription that schedules a future render for the reference component
     pub fn schedule_update(&self) -> impl Fn() -> () {
-        todo!("Subscription API is not ready yet");
+        // log::debug!()
+        // todo!("Subscription API is not ready yet");
         || {}
     }
 

+ 49 - 23
packages/core/src/dodriodiff.rs

@@ -37,7 +37,7 @@ use fxhash::{FxHashMap, FxHashSet};
 use generational_arena::Index;
 
 use crate::{
-    changelist::{Edit, EditList, EditMachine},
+    changelist::{CbIdx, Edit, EditList, EditMachine},
     innerlude::{Attribute, Listener, Scope, VElement, VNode, VText},
     virtual_dom::LifecycleEvent,
 };
@@ -61,16 +61,20 @@ pub struct DiffMachine<'a> {
     immediate_queue: Vec<Index>,
     diffed: FxHashSet<Index>,
     need_to_diff: FxHashSet<Index>,
+
+    // Current scopes we're comparing
+    current_idx: Option<generational_arena::Index>,
 }
 
 impl<'a> DiffMachine<'a> {
     pub fn new(bump: &'a Bump) -> Self {
-        log::debug!("starsting diff machine");
+        log::debug!("starting diff machine");
         Self {
             change_list: EditMachine::new(bump),
             immediate_queue: Vec::new(),
             diffed: FxHashSet::default(),
             need_to_diff: FxHashSet::default(),
+            current_idx: None,
         }
     }
 
@@ -78,8 +82,17 @@ impl<'a> DiffMachine<'a> {
         self.change_list.emitter
     }
 
-    pub fn diff_node(&mut self, old: &VNode<'a>, new: &VNode<'a>) {
+    pub fn diff_node(
+        &mut self,
+        old: &VNode<'a>,
+        new: &VNode<'a>,
+        scope: Option<generational_arena::Index>,
+    ) {
         log::debug!("Diffing nodes");
+
+        // Set it while diffing
+        // Reset it when finished diffing
+        self.current_idx = scope;
         /*
         For each valid case, we "commit traversal", meaning we save this current position in the tree.
         Then, we diff and queue an edit event (via chagelist). s single trees - when components show up, we save that traversal and then re-enter later.
@@ -222,6 +235,7 @@ impl<'a> DiffMachine<'a> {
                 todo!("Suspended components not currently available")
             }
         }
+        self.current_idx = None;
     }
 
     // Diff event listeners between `old` and `new`.
@@ -237,16 +251,20 @@ impl<'a> DiffMachine<'a> {
         }
 
         'outer1: for new_l in new {
-            unsafe {
-                // Safety relies on removing `new_l` from the registry manually at
-                // the end of its lifetime. This happens below in the `'outer2`
-                // loop, and elsewhere in diffing when removing old dom trees.
-                // registry.add(new_l);
-            }
+            // unsafe {
+            // Safety relies on removing `new_l` from the registry manually at
+            // the end of its lifetime. This happens below in the `'outer2`
+            // loop, and elsewhere in diffing when removing old dom trees.
+            // registry.add(new_l);
+            // }
 
-            for old_l in old {
+            for (l_idx, old_l) in old.iter().enumerate() {
                 if new_l.event == old_l.event {
-                    self.change_list.update_event_listener(new_l);
+                    let event_type = new_l.event;
+                    if let Some(scope) = self.current_idx {
+                        let cb = CbIdx::from_gi_index(scope, l_idx);
+                        self.change_list.update_event_listener(event_type, cb);
+                    }
                     continue 'outer1;
                 }
             }
@@ -507,7 +525,7 @@ impl<'a> DiffMachine<'a> {
 
             self.change_list.go_to_sibling(i);
 
-            self.diff_node(old, new);
+            self.diff_node(old, new, self.current_idx);
 
             shared_prefix_count += 1;
         }
@@ -705,7 +723,7 @@ impl<'a> DiffMachine<'a> {
                 // [... parent]
                 self.change_list.go_down_to_temp_child(temp);
                 // [... parent last]
-                self.diff_node(&old[old_index], last);
+                self.diff_node(&old[old_index], last, self.current_idx);
 
                 if new_index_is_in_lis.contains(&last_index) {
                     // Don't move it, since it is already where it needs to be.
@@ -758,7 +776,7 @@ impl<'a> DiffMachine<'a> {
                     // [... parent new_child]
                 }
 
-                self.diff_node(&old[old_index], new_child);
+                self.diff_node(&old[old_index], new_child, self.current_idx);
             }
         }
 
@@ -789,8 +807,7 @@ impl<'a> DiffMachine<'a> {
 
         for (i, (old_child, new_child)) in old.iter().zip(new.iter()).enumerate() {
             self.change_list.go_to_sibling(new_shared_suffix_start + i);
-
-            self.diff_node(old_child, new_child);
+            self.diff_node(old_child, new_child, self.current_idx);
         }
 
         // [... parent]
@@ -818,7 +835,7 @@ impl<'a> DiffMachine<'a> {
             // [... parent prev_child]
             self.change_list.go_to_sibling(i);
             // [... parent this_child]
-            self.diff_node(old_child, new_child);
+            self.diff_node(old_child, new_child, self.current_idx);
         }
 
         match old.len().cmp(&new.len()) {
@@ -880,12 +897,21 @@ impl<'a> DiffMachine<'a> {
                     self.change_list.create_element(tag_name);
                 }
 
-                for l in listeners {
-                    // unsafe {
-                    //     registry.add(l);
-                    // }
-                    self.change_list.new_event_listener(l);
-                }
+                listeners.iter().enumerate().for_each(|(id, listener)| {
+                    if let Some(index) = self.current_idx {
+                        self.change_list
+                            .new_event_listener(listener.event, CbIdx::from_gi_index(index, id));
+                    } else {
+                        // Don't panic
+                        // Used for testing
+                        log::trace!("Failed to set listener, create was not called in the context of the virtual dom");
+                    }
+                });
+                // for l in listeners {
+                // unsafe {
+                //     registry.add(l);
+                // }
+                // }
 
                 for attr in attributes {
                     self.change_list

+ 1 - 116
packages/core/src/lib.rs

@@ -96,6 +96,7 @@ pub(crate) mod innerlude {
     pub(crate) use crate::virtual_dom::VirtualDom;
     pub(crate) use nodes::*;
 
+    pub use crate::changelist::CbIdx;
     // pub use nodes::iterables::IterableNodes;
     /// This type alias is an internal way of abstracting over the static functions that represent components.
 
@@ -151,119 +152,3 @@ pub mod prelude {
 
     pub use crate::hooks::*;
 }
-
-// #[macro_use]
-// extern crate dioxus_core_macro;
-
-// #[macro_use]
-// extern crate fstrings;
-// pub use dioxus_core_macro::format_args_f;
-// macro_rules! mk_macros {( @with_dollar![$dol:tt]=>
-//     $(
-//         #[doc = $doc_string:literal]
-//         $printlnf:ident
-//             => $println:ident!($($stream:ident,)? ...)
-//         ,
-//     )*
-// ) => (
-//     $(
-//         #[doc = $doc_string]
-//         #[macro_export]
-//         macro_rules! $printlnf {(
-//             $($dol $stream : expr,)? $dol($dol args:tt)*
-//         ) => (
-//             $println!($($dol $stream,)? "{}", format_args_f!($dol($dol args)*))
-//         )}
-//     )*
-// )}
-
-// mk_macros! { @with_dollar![$]=>
-//     #[doc = "Like [`print!`](https://doc.rust-lang.org/std/macro.print.html), but with basic f-string interpolation."]
-//     print_f
-//         => print!(...)
-//     ,
-//     #[doc = "Like [`println!`](https://doc.rust-lang.org/std/macro.println.html), but with basic f-string interpolation."]
-//     println_f
-//         => println!(...)
-//     ,
-//     #[doc = "Like [`eprint!`](https://doc.rust-lang.org/std/macro.eprint.html), but with basic f-string interpolation."]
-//     eprint_f
-//         => eprint!(...)
-//     ,
-//     #[doc = "Like [`eprintln!`](https://doc.rust-lang.org/std/macro.eprintln.html), but with basic f-string interpolation."]
-//     eprintln_f
-//         => eprintln!(...)
-//     ,
-//     #[doc = "Like [`format!`](https://doc.rust-lang.org/std/macro.format.html), but with basic f-string interpolation."]
-//     format_f
-//         => format!(...)
-//     ,
-//     #[doc = "Shorthand for [`format_f`]."]
-//     f
-//         => format!(...)
-//     ,
-//     #[doc = "Like [`panic!`](https://doc.rust-lang.org/std/macro.panic.html), but with basic f-string interpolation."]
-//     panic_f
-//         => panic!(...)
-//     ,
-//     #[doc = "Like [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html), but with basic f-string interpolation."]
-//     unreachable_f
-//         => unreachable!(...)
-//     ,
-//     #[doc = "Like [`unimplemented!`](https://doc.rust-lang.org/std/macro.unimplemented.html), but with basic f-string interpolation."]
-//     unimplemented_f
-//         => unimplemented!(...)
-//     ,
-//     #[doc = "Like [`write!`](https://doc.rust-lang.org/std/macro.write.html), but with basic f-string interpolation."]
-//     write_f
-//         => write!(stream, ...)
-//     ,
-//     #[doc = "Like [`writeln!`](https://doc.rust-lang.org/std/macro.writeln.html), but with basic f-string interpolation."]
-//     writeln_f
-//         => writeln!(stream, ...)
-//     ,
-// }
-/// Like the `format!` macro for creating `std::string::String`s but for
-/// `bumpalo::collections::String`.
-///
-/// # Examples
-///
-/// ```
-/// use bumpalo::Bump;
-///
-/// let b = Bump::new();
-///
-/// let who = "World";
-/// let s = bumpalo::format!(in &b, "Hello, {}!", who);
-/// assert_eq!(s, "Hello, World!")
-/// ```
-#[macro_export]
-macro_rules! ifmt {
-    ( in $bump:expr; $fmt:literal;) => {{
-        use bumpalo::core_alloc::fmt::Write;
-        use $crate::prelude::bumpalo;
-        let bump = $bump;
-        let mut s = bumpalo::collections::String::new_in(bump);
-        let args = $crate::prelude::format_args_f!($fmt);
-        s.write_fmt(args);
-        s
-    }};
-}
-// ( in $bump:expr; $fmt:expr; ) => {
-// $println!("{}", format_args_f!($dol($dol args)*))
-
-// write!(&mut s, println!("{}", args));
-// let _ = $crate::write_f!(&mut s, $fmt);
-// s
-//     use fstrings::*;
-//     $crate::ifmt!(in $bump, $fmt)
-// };
-
-#[test]
-fn macro_test() {
-    let w = 123;
-    let world = &w;
-    // let g = format_args_f!("Hello {world}");
-
-    // dbg!(g);
-}

+ 1 - 0
packages/web/Cargo.toml

@@ -19,6 +19,7 @@ log = "0.4.14"
 fxhash = "0.2.1"
 pretty_env_logger = "0.4.0"
 console_error_panic_hook = "0.1.6"
+generational-arena = "0.2.8"
 # html-validation = { path = "../html-validation", version = "0.1.1" }
 
 [dependencies.web-sys]

+ 0 - 13
packages/web/examples/jackjill.rs

@@ -23,16 +23,3 @@ static Example: FC<()> = |ctx, props| {
         </div>
     })
 };
-
-struct ItemProps {
-    name: String,
-    birthdate: String,
-}
-static Item: FC<ItemProps> = |ctx, ItemProps { name, birthdate }| {
-    ctx.view(html! {
-        <div>
-            <p>"{name}"</p>
-            <p>"{birthdate}"</p>
-        </div>
-    })
-};

+ 45 - 11
packages/web/src/interpreter.rs

@@ -20,22 +20,50 @@ impl std::fmt::Debug for RootCallback {
 pub(crate) struct PatchMachine {
     pub(crate) stack: Stack,
 
-    root: Element,
+    pub(crate) root: Element,
+
+    pub(crate) temporaries: FxHashMap<u32, Node>,
+
+    pub(crate) document: Document,
 
-    temporaries: FxHashMap<u32, Node>,
+    pub(crate) events: EventDelegater,
+}
 
-    // callback: RootCallback,
-    // callback: Option<Closure<dyn Fn(EventTrigger)>>,
-    document: Document,
+#[derive(Debug)]
+pub(crate) struct EventDelegater {
+    root: Element,
 
     // every callback gets a monotomically increasing callback ID
     callback_id: usize,
 
+    // map of listener types to number of those listeners
+    listeners: FxHashMap<&'static str, (usize, Closure<dyn Fn()>)>,
+
     // Map of callback_id to component index and listener id
     callback_map: FxHashMap<usize, (usize, usize)>,
 }
 
-// templates: FxHashMap<CacheId, Node>,
+impl EventDelegater {
+    pub fn new(root: Element) -> Self {
+        Self {
+            root,
+            callback_id: 0,
+            listeners: FxHashMap::default(),
+            callback_map: FxHashMap::default(),
+        }
+    }
+
+    pub fn add_listener(
+        &mut self,
+        event: &'static str,
+        gi: generational_arena::Index,
+        listener_id: usize,
+    ) {
+    }
+}
+
+// callback: RootCallback,
+// callback: Option<Closure<dyn Fn(EventTrigger)>>,
 
 #[derive(Debug, Default)]
 pub struct Stack {
@@ -85,16 +113,14 @@ impl PatchMachine {
             .expect("must have access to the Document");
 
         // attach all listeners to the container element
+        let events = EventDelegater::new(root.clone());
 
-        // templates: Default::default(),
         Self {
             root,
+            events,
             stack: Stack::with_capacity(20),
             temporaries: Default::default(),
-            // callback: None,
             document,
-            callback_id: 0,
-            callback_map: FxHashMap::default(),
         }
     }
 
@@ -293,7 +319,11 @@ impl PatchMachine {
             }
 
             // 11
-            Edit::NewEventListener { event_type, a, b } => {
+            Edit::NewEventListener {
+                event_type,
+                idx: a,
+                b,
+            } => {
                 // attach the correct attributes to the element
                 // these will be used by accessing the event's target
                 // This ensures we only ever have one handler attached to the root, but decide
@@ -304,16 +334,20 @@ impl PatchMachine {
                 let el = el
                     .dyn_ref::<Element>()
                     .expect(&format!("not an element: {:?}", el));
+
                 // el.add_event_listener_with_callback(
                 //     event_type,
                 //     self.callback.as_ref().unwrap().as_ref().unchecked_ref(),
                 // )
                 // .unwrap();
+
                 debug!("adding attributes: {}, {}", a, b);
                 el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string())
                     .unwrap();
                 el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string())
                     .unwrap();
+
+                self.events.add_listener(event_type, gi, listener_id)
             }
 
             // 12