wip: stack-based "real child iterator"

This commit is contained in:
Jonathan Kelley 2021-06-26 03:06:29 -04:00
parent 0826fdfee1
commit 895cc0142b
6 changed files with 356 additions and 121 deletions

View file

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

View file

@ -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"),
}
}
}
}

View file

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

View file

@ -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<Self::Item> {
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<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();
// // 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;

57
packages/core/src/util.rs Normal file
View file

@ -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!()
}
}

View file

@ -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.