Explorar el Código

wip: tests added for new iterative

Jonathan Kelley hace 3 años
padre
commit
24572b6

+ 2 - 0
packages/core/Cargo.toml

@@ -50,6 +50,8 @@ indexmap = "1.7.0"
 anyhow = "1.0.42"
 async-std = { version = "1.9.0", features = ["attributes"] }
 dioxus-html = { path = "../html" }
+fern = { version = "0.6.0", features = ["colored"] }
+simple_logger = "1.13.0"
 
 
 [features]

+ 55 - 41
packages/core/src/diff.rs

@@ -100,7 +100,7 @@ pub struct DiffMachine<'bump> {
 
     pub nodes_created_stack: SmallVec<[usize; 10]>,
 
-    pub node_stack: SmallVec<[DiffInstruction<'bump>; 10]>,
+    pub instructions: SmallVec<[DiffInstruction<'bump>; 10]>,
 
     pub scope_stack: SmallVec<[ScopeId; 5]>,
 
@@ -113,6 +113,7 @@ pub struct DiffMachine<'bump> {
 ///
 /// Right now, we insert an instruction for every child node we want to create and diff. This can be less efficient than
 /// a custom iterator type - but this is current easier to implement. In the future, let's try interact with the stack less.
+#[derive(Debug)]
 pub enum DiffInstruction<'a> {
     DiffNode {
         old: &'a VNode<'a>,
@@ -121,7 +122,8 @@ pub enum DiffInstruction<'a> {
 
     DiffChildren {
         progress: usize,
-        children: &'a [VNode<'a>],
+        old: &'a [VNode<'a>],
+        new: &'a [VNode<'a>],
     },
 
     Create {
@@ -148,7 +150,7 @@ impl<'bump> DiffMachine<'bump> {
         shared: &'bump SharedResources,
     ) -> Self {
         Self {
-            node_stack: smallvec![],
+            instructions: smallvec![],
             nodes_created_stack: smallvec![],
             mutations: edits,
             scope_stack: smallvec![cur_scope],
@@ -158,6 +160,12 @@ impl<'bump> DiffMachine<'bump> {
         }
     }
 
+    pub fn new_headless(shared: &'bump SharedResources) -> Self {
+        let edits = Mutations::new();
+        let cur_scope = ScopeId(0);
+        Self::new(edits, cur_scope, shared)
+    }
+
     //
     pub async fn diff_scope(&mut self, id: ScopeId) -> Result<()> {
         let component = self.get_scope_mut(&id).ok_or_else(|| Error::NotMounted)?;
@@ -176,50 +184,60 @@ impl<'bump> DiffMachine<'bump> {
         // todo: don't move the reused instructions around
         // defer to individual functions so the compiler produces better code
         // large functions tend to be difficult for the compiler to work with
-        let mut should_pop = false;
-        while let Some(make) = self.node_stack.last_mut() {
-            should_pop = false;
-            match make {
+        while let Some(instruction) = self.instructions.last_mut() {
+            log::debug!("Handling diff instruction: {:?}", instruction);
+            match instruction {
                 DiffInstruction::DiffNode { old, new, .. } => {
                     let (old, new) = (*old, *new);
+                    self.instructions.pop();
                     self.diff_node(old, new);
                 }
 
-                DiffInstruction::DiffChildren { progress, children } => {
-                    //
+                // this is slightly more complicated, we need to find a way to pause our LIS code
+                DiffInstruction::DiffChildren { progress, old, new } => {
+                    todo!()
                 }
 
                 DiffInstruction::Create { node, .. } => {
                     let node = *node;
+                    self.instructions.pop();
                     self.create_node(node);
                 }
+
                 DiffInstruction::CreateChildren { progress, children } => {
-                    if let Some(child) = (children).get(*progress) {
+                    if let Some(child) = children.get(*progress) {
                         *progress += 1;
                         self.create_node(child);
                     } else {
-                        should_pop = true;
+                        self.instructions.pop();
                     }
                 }
 
-                DiffInstruction::Append {} => {
+                DiffInstruction::Append => {
                     let many = self.nodes_created_stack.pop().unwrap();
                     self.edit_append_children(many as u32);
+                    self.instructions.pop();
                 }
 
                 DiffInstruction::Replace { with } => {
                     let with = *with;
                     let many = self.nodes_created_stack.pop().unwrap();
-
                     self.edit_replace_with(with as u32, many as u32);
+                    self.instructions.pop();
                 }
 
-                DiffInstruction::InsertAfter => todo!(),
-                DiffInstruction::InsertBefore => todo!(),
-            }
-            if should_pop {
-                self.node_stack.pop();
-            }
+                DiffInstruction::InsertAfter => {
+                    let n = self.nodes_created_stack.pop().unwrap();
+                    self.edit_insert_after(n as u32);
+                    self.instructions.pop();
+                }
+
+                DiffInstruction::InsertBefore => {
+                    let n = self.nodes_created_stack.pop().unwrap();
+                    self.edit_insert_before(n as u32);
+                    self.instructions.pop();
+                }
+            };
         }
 
         Ok(())
@@ -268,15 +286,13 @@ impl<'bump> DiffMachine<'bump> {
             attributes,
             children,
             namespace,
-            static_attrs: _,
-            static_children: _,
-            static_listeners: _,
             dom_id,
-            key,
+            ..
         } = element;
 
         let real_id = self.vdom.reserve_node();
         self.edit_create_element(tag_name, *namespace, real_id);
+        *self.nodes_created_stack.last_mut().unwrap() += 1;
         dom_id.set(Some(real_id));
 
         let cur_scope = self.current_scope().unwrap();
@@ -285,30 +301,26 @@ impl<'bump> DiffMachine<'bump> {
             self.fix_listener(listener);
             listener.mounted_node.set(Some(real_id));
             self.edit_new_event_listener(listener, cur_scope.clone());
-
-            // if the node has an event listener, then it must be visited ?
         });
 
         for attr in *attributes {
             self.edit_set_attribute(attr);
         }
 
-        // TODO: the append child edit
+        self.instructions.push(DiffInstruction::Append);
 
-        *self.nodes_created_stack.last_mut().unwrap() += 1;
-
-        // push every child onto the stack
         self.nodes_created_stack.push(0);
-        for child in *children {
-            self.node_stack
-                .push(DiffInstruction::Create { node: child })
-        }
+        self.instructions.push(DiffInstruction::CreateChildren {
+            children,
+            progress: 0,
+        });
     }
 
     fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
-        for node in frag.children {
-            self.node_stack.push(DiffInstruction::Create { node })
-        }
+        self.instructions.push(DiffInstruction::CreateChildren {
+            children: frag.children,
+            progress: 0,
+        });
     }
 
     fn create_component_node(&mut self, vcomponent: &'bump VComponent<'bump>) {
@@ -348,7 +360,7 @@ impl<'bump> DiffMachine<'bump> {
 
         // Run the scope for one iteration to initialize it
         match new_component.run_scope() {
-            Ok(_) => {
+            Ok(_g) => {
                 // all good, new nodes exist
             }
             Err(err) => {
@@ -361,11 +373,13 @@ impl<'bump> DiffMachine<'bump> {
         // Take the node that was just generated from running the component
         let nextnode = new_component.frames.fin_head();
 
-        // Push the new scope onto the stack
-        self.scope_stack.push(new_idx);
+        // // Push the new scope onto the stack
+        // self.scope_stack.push(new_idx);
 
-        todo!();
         // // Run the creation algorithm with this scope on the stack
+        self.instructions
+            .push(DiffInstruction::Create { node: nextnode });
+
         // let meta = self.create_vnode(nextnode);
 
         // // pop the scope off the stack
@@ -376,7 +390,7 @@ impl<'bump> DiffMachine<'bump> {
         // }
 
         // // Finally, insert this scope as a seen node.
-        // self.seen_scopes.insert(new_idx);
+        self.seen_scopes.insert(new_idx);
     }
 
     // =================================

+ 6 - 6
packages/core/src/nodes.rs

@@ -38,12 +38,12 @@ pub enum VNode<'src> {
 impl<'src> VNode<'src> {
     pub fn key(&self) -> Option<&'src str> {
         match &self {
-            VNode::Text(_) => todo!(),
-            VNode::Element(_) => todo!(),
-            VNode::Fragment(_) => todo!(),
-            VNode::Component(_) => todo!(),
-            VNode::Suspended(_) => todo!(),
-            VNode::Anchor(_) => todo!(),
+            VNode::Element(el) => el.key,
+            VNode::Component(c) => c.key,
+            VNode::Fragment(f) => f.key,
+            VNode::Text(_t) => None,
+            VNode::Suspended(s) => None,
+            VNode::Anchor(f) => None,
         }
     }
     pub fn direct_id(&self) -> ElementId {

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

@@ -159,23 +159,53 @@ impl VirtualDom {
             .get_scope_mut(&self.base_scope)
             .expect("The base scope should never be moved");
 
-        todo!();
+        // todo!();
 
         // // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
-        // if cur_component.run_scope().is_ok() {
-        //     let meta = diff_machine.create_vnode(cur_component.frames.fin_head());
-        //     diff_machine.edit_append_children(meta.added_to_stack);
-        // } else {
-        //     // todo: should this be a hard error?
-        //     log::warn!(
-        //         "Component failed to run succesfully during rebuild.
-        //         This does not result in a failed rebuild, but indicates a logic failure within your app."
-        //     );
-        // }
+        if cur_component.run_scope().is_ok() {
+            diff_machine.nodes_created_stack.push(0);
+            diff_machine.instructions.push(DiffInstruction::Create {
+                node: cur_component.frames.fin_head(),
+            });
+        } else {
+            // todo: should this be a hard error?
+            log::warn!(
+                "Component failed to run succesfully during rebuild.
+                This does not result in a failed rebuild, but indicates a logic failure within your app."
+            );
+        }
 
         Ok(diff_machine.mutations)
     }
 
+    pub async fn rebuild_async<'s>(&'s mut self) -> Result<Mutations<'s>> {
+        let mut diff_machine = DiffMachine::new(Mutations::new(), self.base_scope, &self.shared);
+
+        let cur_component = diff_machine
+            .get_scope_mut(&self.base_scope)
+            .expect("The base scope should never be moved");
+
+        // todo!();
+
+        // // We run the component. If it succeeds, then we can diff it and add the changes to the dom.
+        if cur_component.run_scope().is_ok() {
+            diff_machine.nodes_created_stack.push(0);
+            diff_machine.instructions.push(DiffInstruction::Create {
+                node: cur_component.frames.fin_head(),
+            });
+            diff_machine.work().await.unwrap();
+            let many = diff_machine.nodes_created_stack.pop().unwrap() as u32;
+            diff_machine.edit_append_children(many);
+        } else {
+            // todo: should this be a hard error?
+            log::warn!(
+                "Component failed to run succesfully during rebuild.
+                This does not result in a failed rebuild, but indicates a logic failure within your app."
+            );
+        }
+
+        Ok(diff_machine.mutations)
+    }
     /// Runs the virtualdom immediately, not waiting for any suspended nodes to complete.
     ///
     /// This method will not wait for any suspended nodes to complete.

+ 38 - 11
packages/core/tests/iterative.rs

@@ -8,6 +8,7 @@ use dioxus::{
     scheduler::Mutations,
     DomEdit,
 };
+mod test_logging;
 use dioxus_core as dioxus;
 use dioxus_html as dioxus_elements;
 
@@ -29,29 +30,55 @@ fn test_original_diff() {
 }
 
 #[async_std::test]
-async fn test_iterative_diff() {
+async fn test_iterative_create() {
     static App: FC<()> = |cx| {
         cx.render(rsx! {
             div {
                 div {
                     "Hello, world!"
+                    div {
+                        div {
+                            Fragment {
+                                "hello"
+                                "world"
+                            }
+                        }
+                    }
                 }
             }
         })
     };
 
-    let shared = SharedResources::new();
+    test_logging::set_up_logging();
 
-    let mut machine = DiffMachine::new_headless(&shared);
-    let a = machine.work().await.unwrap();
+    let mut dom = VirtualDom::new(App);
+    let mutations = dom.rebuild_async().await.unwrap();
+    dbg!(mutations);
 }
 
 #[async_std::test]
-async fn websys_loop() {
-    ///loop {
-    ///    let deadline = request_idle_callback().await;
-    ///    let edits = dom.work(deadline);
-    ///    request_animation_frame().await;
-    ///    apply(edits);
-    ///}
+async fn test_iterative_create_list() {
+    static App: FC<()> = |cx| {
+        cx.render(rsx! {
+            {(0..10).map(|f| rsx!{ div {
+                "hello"
+            }})}
+        })
+    };
+
+    test_logging::set_up_logging();
+
+    let mut dom = VirtualDom::new(App);
+    let mutations = dom.rebuild_async().await.unwrap();
+    dbg!(mutations);
 }
+
+// #[async_std::test]
+// async fn websys_loop() {
+//     ///loop {
+//     ///    let deadline = request_idle_callback().await;
+//     ///    let edits = dom.work(deadline);
+//     ///    request_animation_frame().await;
+//     ///    apply(edits);
+//     ///}
+// }

+ 49 - 0
packages/core/tests/test_logging.rs

@@ -0,0 +1,49 @@
+pub fn set_up_logging() {
+    use fern::colors::{Color, ColoredLevelConfig};
+    use log::debug;
+
+    // configure colors for the whole line
+    let colors_line = ColoredLevelConfig::new()
+        .error(Color::Red)
+        .warn(Color::Yellow)
+        // we actually don't need to specify the color for debug and info, they are white by default
+        .info(Color::White)
+        .debug(Color::White)
+        // depending on the terminals color scheme, this is the same as the background color
+        .trace(Color::BrightBlack);
+
+    // configure colors for the name of the level.
+    // since almost all of them are the same as the color for the whole line, we
+    // just clone `colors_line` and overwrite our changes
+    let colors_level = colors_line.clone().info(Color::Green);
+    // here we set up our fern Dispatch
+    fern::Dispatch::new()
+        .format(move |out, message, record| {
+            out.finish(format_args!(
+                "{color_line}[{level}{color_line}] {message}\x1B[0m",
+                color_line = format_args!(
+                    "\x1B[{}m",
+                    colors_line.get_color(&record.level()).to_fg_str()
+                ),
+                level = colors_level.color(record.level()),
+                message = message,
+            ));
+        })
+        // set the default log level. to filter out verbose log messages from dependencies, set
+        // this to Warn and overwrite the log level for your crate.
+        .level(log::LevelFilter::Debug)
+        // .level(log::LevelFilter::Warn)
+        // change log levels for individual modules. Note: This looks for the record's target
+        // field which defaults to the module path but can be overwritten with the `target`
+        // parameter:
+        // `info!(target="special_target", "This log message is about special_target");`
+        // .level_for("dioxus", log::LevelFilter::Debug)
+        // .level_for("dioxus", log::LevelFilter::Info)
+        // .level_for("pretty_colored", log::LevelFilter::Trace)
+        // output to stdout
+        .chain(std::io::stdout())
+        .apply()
+        .unwrap();
+
+    debug!("finished setting up logging! yay!");
+}