diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 3b448e21b..451992e33 100644 --- a/packages/core/Cargo.toml +++ b/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" diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 9cbd2fe65..630e982ff 100644 --- a/packages/core/src/diff.rs +++ b/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"), + } + } + } +} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 987934316..0ae0e6301 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -8,6 +8,7 @@ //! //! +pub mod util; pub mod arena; pub mod component; // Logic for extending FC diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index c9ebda3b8..f619eb8b2 100644 --- a/packages/core/src/nodes.rs +++ b/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) -} +// /// 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>], +// 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)>, -} +// // 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; - } - } - } +// 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 } - } +// 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(); +// // // 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; - // } - // } - // } - // } +// // 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(); +// 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 - } +// 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!(), - } - } +// 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!(), - }) - } -} +// 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>; +// impl<'a> Iterator for RealNodeIterator<'a> { +// type Item = &'a VNode<'a>; - fn next(&mut self) -> Option { - todo!() - // let top_idx = self.nesting_idxs.get_mut(0).unwrap(); - // let node = &self.nodes.get_mut(*top_idx as usize); +// fn next(&mut self) -> Option { +// 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(); +// // 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!(), +// // 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!(), - // } - // } - // } - } -} +// // 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; diff --git a/packages/core/src/util.rs b/packages/core/src/util.rs new file mode 100644 index 000000000..dc798d20c --- /dev/null +++ b/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!() + } +} diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 60e97b9da..472c017d1 100644 --- a/packages/core/src/virtual_dom.rs +++ b/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.