فهرست منبع

wip: stack-based "real child iterator"

Jonathan Kelley 4 سال پیش
والد
کامیت
895cc01
6فایلهای تغییر یافته به همراه371 افزوده شده و 136 حذف شده
  1. 1 0
      packages/core/Cargo.toml
  2. 172 1
      packages/core/src/diff.rs
  3. 1 0
      packages/core/src/lib.rs
  4. 134 134
      packages/core/src/nodes.rs
  5. 57 0
      packages/core/src/util.rs
  6. 6 1
      packages/core/src/virtual_dom.rs

+ 1 - 0
packages/core/Cargo.toml

@@ -39,6 +39,7 @@ log = "0.4"
 
 smallvec = "1.6.1"
 
+# Backs scopes and unique keys
 slotmap = "1.0.3"
 
 futures = "0.3.15"

+ 172 - 1
packages/core/src/diff.rs

@@ -188,7 +188,18 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
                 }
 
                 // TODO on handling these types
-                VNode::Fragment(_) => todo!(),
+                VNode::Fragment(frag) => {
+                    if frag.children.len() == 0 {
+                        // do nothing
+                    } else {
+                        self.create(&frag.children[0]);
+                        self.dom.replace_with();
+                        for child in frag.children.iter().skip(1) {
+                            self.create(child);
+                            self.dom.append_child();
+                        }
+                    }
+                }
                 VNode::Suspended => todo!(),
             },
 
@@ -437,6 +448,10 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
             }
         }
     }
+
+    fn iter_children(&self, node: &VNode<'a>) -> ChildIterator<'a> {
+        todo!()
+    }
 }
 
 impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
@@ -1206,3 +1221,159 @@ enum KeyedPrefixResult {
     // the beginning of `new` and `old` we already processed.
     MoreWorkToDo(usize),
 }
+
+struct ChildIterator<'a> {
+    scopes: &'a ScopeArena,
+
+    // Heuristcally we should never bleed into 5 completely nested fragments/components
+    // Smallvec lets us stack allocate our little stack machine so the vast majority of cases are sane
+    stack: smallvec::SmallVec<[(u16, &'a VNode<'a>); 5]>,
+}
+
+impl<'a> ChildIterator<'a> {
+    fn new(starter: &'a VNode<'a>, scopes: &'a ScopeArena) -> Self {
+        Self {
+            scopes,
+            stack: smallvec::smallvec![(0, starter)],
+        }
+    }
+}
+
+impl<'a> Iterator for ChildIterator<'a> {
+    type Item = &'a VNode<'a>;
+
+    fn next(&mut self) -> Option<&'a VNode<'a>> {
+        let mut should_pop = false;
+        let mut returned_node = None;
+        let mut should_push = None;
+
+        while returned_node.is_none() {
+            if let Some((count, node)) = self.stack.last_mut() {
+                match node {
+                    // We can only exit our looping when we get "real" nodes
+                    VNode::Element(_) | VNode::Text(_) => {
+                        // We've recursed INTO an element/text
+                        // We need to recurse *out* of it and move forward to the next
+                        // println!("Found element! Returning it!");
+                        should_pop = true;
+                        returned_node = Some(&**node);
+                    }
+
+                    // If we get a fragment we push the next child
+                    VNode::Fragment(frag) => {
+                        let _count = *count as usize;
+                        if _count >= frag.children.len() {
+                            should_pop = true;
+                        } else {
+                            should_push = Some(&frag.children[_count]);
+                        }
+                    }
+
+                    // Immediately abort suspended nodes - can't do anything with them yet
+                    // VNode::Suspended => should_pop = true,
+                    VNode::Suspended => todo!(),
+
+                    // For components, we load their root and push them onto the stack
+                    VNode::Component(sc) => {
+                        let scope = self.scopes.try_get(sc.ass_scope.borrow().unwrap()).unwrap();
+
+                        // Simply swap the current node on the stack with the root of the component
+                        *node = scope.root();
+                    }
+                }
+            } else {
+                // If there's no more items on the stack, we're done!
+                return None;
+            }
+
+            if should_pop {
+                self.stack.pop();
+                if let Some((id, _)) = self.stack.last_mut() {
+                    *id += 1;
+                }
+                should_pop = false;
+            }
+
+            if let Some(push) = should_push {
+                self.stack.push((0, push));
+                should_push = None;
+            }
+        }
+
+        returned_node
+    }
+}
+
+mod tests {
+    use super::*;
+    use crate as dioxus;
+    use crate::innerlude::*;
+    use crate::util::DebugDom;
+    use dioxus_core_macro::*;
+
+    #[test]
+    fn test_child_iterator() {
+        static App: FC<()> = |cx| {
+            cx.render(rsx! {
+                Fragment {
+                    div {}
+                    h1 {}
+                    h2 {}
+                    h3 {}
+                    Fragment {
+                        "internal node"
+                        div {
+                            "baller text shouldn't show up"
+                        }
+                        p {
+
+                        }
+                        Fragment {
+                            Fragment {
+                                "wow you really like framgents"
+                                Fragment {
+                                    "why are you like this"
+                                    Fragment {
+                                        "just stop now please"
+                                        Fragment {
+                                            "this hurts"
+                                            Fragment {
+                                                "who needs this many fragments?????"
+                                                Fragment {
+                                                    "just... fine..."
+                                                    Fragment {
+                                                        "no"
+                                                    }
+                                                }
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    "my text node 1"
+                    "my text node 2"
+                    "my text node 3"
+                    "my text node 4"
+                }
+            })
+        };
+        let mut dom = VirtualDom::new(App);
+        let mut renderer = DebugDom::new();
+        dom.rebuild(&mut renderer).unwrap();
+        let starter = dom.base_scope().root();
+        let ite = ChildIterator::new(starter, &dom.components);
+        for child in ite {
+            match child {
+                VNode::Element(el) => println!("Found: Element {}", el.tag_name),
+                VNode::Text(t) => println!("Found: Text {:?}", t.text),
+
+                // These would represent failing cases.
+                VNode::Fragment(_) => panic!("Found: Fragment"),
+                VNode::Suspended => panic!("Found: Suspended"),
+                VNode::Component(_) => panic!("Found: Component"),
+            }
+        }
+    }
+}

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

@@ -8,6 +8,7 @@
 //!
 //!
 
+pub mod util;
 pub mod arena;
 pub mod component; // Logic for extending FC
 

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

@@ -437,140 +437,140 @@ impl<'a> VFragment<'a> {
     }
 }
 
-/// This method converts a list of nested real/virtual nodes into a stream of nodes that are definitely associated
-/// with the real dom. The only types of nodes that may be returned are text, elemets, and components.
-///
-/// Components *are* considered virtual, but this iterator can't necessarily handle them without the scope arena.
-///
-/// Why?
-/// ---
-/// Fragments are seen as virtual nodes but are actually a list of possibly-real nodes.
-/// JS implementations normalize their node lists when fragments are present. Here, we just create a new iterator
-/// that iterates through the recursive nesting of fragments.
-///
-/// Fragments are stupid and I wish we didn't need to support them.
-///
-/// This iterator only supports 3 levels of nested fragments
-///
-pub fn iterate_real_nodes<'a>(nodes: &'a [VNode<'a>]) -> RealNodeIterator<'a> {
-    RealNodeIterator::new(nodes)
-}
-
-pub struct RealNodeIterator<'a> {
-    nodes: &'a [VNode<'a>],
-
-    // this node is always a "real" node
-    // the index is "what sibling # is it"
-    // IE in a list of children on a fragment, the node will be a text node that's the 5th sibling
-    node_stack: Vec<(&'a VNode<'a>, u32)>,
-}
-
-impl<'a> RealNodeIterator<'a> {
-    // We immediately descend to the first real node we can find
-    fn new(nodes: &'a [VNode<'a>]) -> Self {
-        let mut node_stack = Vec::new();
-        if nodes.len() > 0 {
-            let mut cur_node = nodes.get(0).unwrap();
-            loop {
-                node_stack.push((cur_node, 0_u32));
-                if !cur_node.is_real() {
-                    cur_node = cur_node.get_child(0).unwrap();
-                } else {
-                    break;
-                }
-            }
-        }
-
-        Self { nodes, node_stack }
-    }
-
-    // // advances the cursor to the next element, panicing if we're on the 3rd level and still finding fragments
-    // fn advance_cursor(&mut self) {
-    //     let (mut cur_node, mut cur_id) = self.node_stack.last().unwrap();
-
-    //     while !cur_node.is_real() {
-    //         match cur_node {
-    //             VNode::Element(_) | VNode::Text(_) => todo!(),
-    //             VNode::Suspended => todo!(),
-    //             VNode::Component(_) => todo!(),
-    //             VNode::Fragment(frag) => {
-    //                 let p = frag.children;
-    //             }
-    //         }
-    //     }
-    // }
-
-    fn next_node(&mut self) -> bool {
-        let (mut cur_node, cur_id) = self.node_stack.last_mut().unwrap();
-
-        match cur_node {
-            VNode::Fragment(frag) => {
-                //
-                if *cur_id + 1 > frag.children.len() as u32 {
-                    self.node_stack.pop();
-                    let next = self.node_stack.last_mut();
-                    return false;
-                }
-                *cur_id += 1;
-                true
-            }
-
-            VNode::Element(_) => todo!(),
-            VNode::Text(_) => todo!(),
-            VNode::Suspended => todo!(),
-            VNode::Component(_) => todo!(),
-        }
-    }
-
-    fn get_current_node(&self) -> Option<&VNode<'a>> {
-        self.node_stack.last().map(|(node, id)| match node {
-            VNode::Element(_) => todo!(),
-            VNode::Text(_) => todo!(),
-            VNode::Fragment(_) => todo!(),
-            VNode::Suspended => todo!(),
-            VNode::Component(_) => todo!(),
-        })
-    }
-}
-
-impl<'a> Iterator for RealNodeIterator<'a> {
-    type Item = &'a VNode<'a>;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        todo!()
-        // let top_idx = self.nesting_idxs.get_mut(0).unwrap();
-        // let node = &self.nodes.get_mut(*top_idx as usize);
-
-        // if node.is_none() {
-        //     return None;
-        // }
-        // let node = node.unwrap();
-
-        // match node {
-        //     VNode::Element(_) | VNode::Text(_) => {
-        //         *top_idx += 1;
-        //         return Some(node);
-        //     }
-        //     VNode::Suspended => todo!(),
-        //     // we need access over the scope map
-        //     VNode::Component(_) => todo!(),
-
-        //     VNode::Fragment(frag) => {
-        //         let nest_idx = self.nesting_idxs.get_mut(1).unwrap();
-        //         let node = &frag.children.get_mut(*nest_idx as usize);
-        //         match node {
-        //             VNode::Element(_) | VNode::Text(_) => {
-        //                 *nest_idx += 1;
-        //                 return Some(node);
-        //             }
-        //             VNode::Fragment(_) => todo!(),
-        //             VNode::Suspended => todo!(),
-        //             VNode::Component(_) => todo!(),
-        //         }
-        //     }
-        // }
-    }
-}
+// /// This method converts a list of nested real/virtual nodes into a stream of nodes that are definitely associated
+// /// with the real dom. The only types of nodes that may be returned are text, elemets, and components.
+// ///
+// /// Components *are* considered virtual, but this iterator can't necessarily handle them without the scope arena.
+// ///
+// /// Why?
+// /// ---
+// /// Fragments are seen as virtual nodes but are actually a list of possibly-real nodes.
+// /// JS implementations normalize their node lists when fragments are present. Here, we just create a new iterator
+// /// that iterates through the recursive nesting of fragments.
+// ///
+// /// Fragments are stupid and I wish we didn't need to support them.
+// ///
+// /// This iterator only supports 3 levels of nested fragments
+// ///
+// pub fn iterate_real_nodes<'a>(nodes: &'a [VNode<'a>]) -> RealNodeIterator<'a> {
+//     RealNodeIterator::new(nodes)
+// }
+
+// pub struct RealNodeIterator<'a> {
+//     nodes: &'a [VNode<'a>],
+
+//     // this node is always a "real" node
+//     // the index is "what sibling # is it"
+//     // IE in a list of children on a fragment, the node will be a text node that's the 5th sibling
+//     node_stack: Vec<(&'a VNode<'a>, u32)>,
+// }
+
+// impl<'a> RealNodeIterator<'a> {
+//     // We immediately descend to the first real node we can find
+//     fn new(nodes: &'a [VNode<'a>]) -> Self {
+//         let mut node_stack = Vec::new();
+//         if nodes.len() > 0 {
+//             let mut cur_node = nodes.get(0).unwrap();
+//             loop {
+//                 node_stack.push((cur_node, 0_u32));
+//                 if !cur_node.is_real() {
+//                     cur_node = cur_node.get_child(0).unwrap();
+//                 } else {
+//                     break;
+//                 }
+//             }
+//         }
+
+//         Self { nodes, node_stack }
+//     }
+
+//     // // advances the cursor to the next element, panicing if we're on the 3rd level and still finding fragments
+//     // fn advance_cursor(&mut self) {
+//     //     let (mut cur_node, mut cur_id) = self.node_stack.last().unwrap();
+
+//     //     while !cur_node.is_real() {
+//     //         match cur_node {
+//     //             VNode::Element(_) | VNode::Text(_) => todo!(),
+//     //             VNode::Suspended => todo!(),
+//     //             VNode::Component(_) => todo!(),
+//     //             VNode::Fragment(frag) => {
+//     //                 let p = frag.children;
+//     //             }
+//     //         }
+//     //     }
+//     // }
+
+//     fn next_node(&mut self) -> bool {
+//         let (mut cur_node, cur_id) = self.node_stack.last_mut().unwrap();
+
+//         match cur_node {
+//             VNode::Fragment(frag) => {
+//                 //
+//                 if *cur_id + 1 > frag.children.len() as u32 {
+//                     self.node_stack.pop();
+//                     let next = self.node_stack.last_mut();
+//                     return false;
+//                 }
+//                 *cur_id += 1;
+//                 true
+//             }
+
+//             VNode::Element(_) => todo!(),
+//             VNode::Text(_) => todo!(),
+//             VNode::Suspended => todo!(),
+//             VNode::Component(_) => todo!(),
+//         }
+//     }
+
+//     fn get_current_node(&self) -> Option<&VNode<'a>> {
+//         self.node_stack.last().map(|(node, id)| match node {
+//             VNode::Element(_) => todo!(),
+//             VNode::Text(_) => todo!(),
+//             VNode::Fragment(_) => todo!(),
+//             VNode::Suspended => todo!(),
+//             VNode::Component(_) => todo!(),
+//         })
+//     }
+// }
+
+// impl<'a> Iterator for RealNodeIterator<'a> {
+//     type Item = &'a VNode<'a>;
+
+//     fn next(&mut self) -> Option<Self::Item> {
+//         todo!()
+//         // let top_idx = self.nesting_idxs.get_mut(0).unwrap();
+//         // let node = &self.nodes.get_mut(*top_idx as usize);
+
+//         // if node.is_none() {
+//         //     return None;
+//         // }
+//         // let node = node.unwrap();
+
+//         // match node {
+//         //     VNode::Element(_) | VNode::Text(_) => {
+//         //         *top_idx += 1;
+//         //         return Some(node);
+//         //     }
+//         //     VNode::Suspended => todo!(),
+//         //     // we need access over the scope map
+//         //     VNode::Component(_) => todo!(),
+
+//         //     VNode::Fragment(frag) => {
+//         //         let nest_idx = self.nesting_idxs.get_mut(1).unwrap();
+//         //         let node = &frag.children.get_mut(*nest_idx as usize);
+//         //         match node {
+//         //             VNode::Element(_) | VNode::Text(_) => {
+//         //                 *nest_idx += 1;
+//         //                 return Some(node);
+//         //             }
+//         //             VNode::Fragment(_) => todo!(),
+//         //             VNode::Suspended => todo!(),
+//         //             VNode::Component(_) => todo!(),
+//         //         }
+//         //     }
+//         // }
+//     }
+// }
 
 mod tests {
     use crate::debug_renderer::DebugRenderer;

+ 57 - 0
packages/core/src/util.rs

@@ -0,0 +1,57 @@
+use crate::innerlude::*;
+
+pub struct DebugDom {
+    counter: u32,
+}
+impl DebugDom {
+    pub fn new() -> Self {
+        Self { counter: 0 }
+    }
+}
+impl RealDom for DebugDom {
+    fn push_root(&mut self, root: RealDomNode) {}
+
+    fn append_child(&mut self) {}
+
+    fn replace_with(&mut self) {}
+
+    fn remove(&mut self) {}
+
+    fn remove_all_children(&mut self) {}
+
+    fn create_text_node(&mut self, text: &str) -> RealDomNode {
+        self.counter += 1;
+        RealDomNode::new(self.counter)
+    }
+
+    fn create_element(&mut self, tag: &str) -> RealDomNode {
+        self.counter += 1;
+        RealDomNode::new(self.counter)
+    }
+
+    fn create_element_ns(&mut self, tag: &str, namespace: &str) -> RealDomNode {
+        self.counter += 1;
+        RealDomNode::new(self.counter)
+    }
+
+    fn new_event_listener(
+        &mut self,
+        event: &str,
+        scope: ScopeIdx,
+        element_id: usize,
+        realnode: RealDomNode,
+    ) {
+    }
+
+    fn remove_event_listener(&mut self, event: &str) {}
+
+    fn set_text(&mut self, text: &str) {}
+
+    fn set_attribute(&mut self, name: &str, value: &str, is_namespaced: bool) {}
+
+    fn remove_attribute(&mut self, name: &str) {}
+
+    fn raw_node_as_any_mut(&self) -> &mut dyn std::any::Any {
+        todo!()
+    }
+}

+ 6 - 1
packages/core/src/virtual_dom.rs

@@ -336,7 +336,8 @@ impl VirtualDom {
     }
 
     pub fn base_scope(&self) -> &Scope {
-        todo!()
+        let idx = self.base_scope;
+        self.components.try_get(idx).unwrap()
     }
 }
 
@@ -578,6 +579,10 @@ impl Scope {
     pub fn cur_frame(&self) -> &BumpFrame {
         self.frames.cur_frame()
     }
+
+    pub fn root<'a>(&'a self) -> &'a VNode<'a> {
+        &self.frames.cur_frame().head_node
+    }
 }
 
 /// Components in Dioxus use the "Context" object to interact with their lifecycle.