wip: stack-based "real child iterator"
This commit is contained in:
1 parent
0826fdfee1
commit
895cc0142b
6 files changed
+356
-121
No files matched your search
@@ -39,6 +39,7 @@ log = "0.4"
|
|||||||
|
|
||||||
smallvec = "1.6.1"
|
smallvec = "1.6.1"
|
||||||
|
|
||||||
|
# Backs scopes and unique keys
|
||||||
slotmap = "1.0.3"
|
slotmap = "1.0.3"
|
||||||
|
|
||||||
futures = "0.3.15"
|
futures = "0.3.15"
|
||||||
|
|||||||
+172
-1
@@ -188,7 +188,18 @@ impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO on handling these types
|
// 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!(),
|
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> {
|
impl<'a, Dom: RealDom> DiffMachine<'a, Dom> {
|
||||||
@@ -1206,3 +1221,159 @@ enum KeyedPrefixResult {
|
|||||||
// the beginning of `new` and `old` we already processed.
|
// the beginning of `new` and `old` we already processed.
|
||||||
MoreWorkToDo(usize),
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
pub mod util;
|
||||||
pub mod arena;
|
pub mod arena;
|
||||||
pub mod component; // Logic for extending FC
|
pub mod component; // Logic for extending FC
|
||||||
|
|
||||||
|
|||||||
+119
-119
@@ -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
|
// /// 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.
|
// /// 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.
|
// /// Components *are* considered virtual, but this iterator can't necessarily handle them without the scope arena.
|
||||||
///
|
// ///
|
||||||
/// Why?
|
// /// Why?
|
||||||
/// ---
|
// /// ---
|
||||||
/// Fragments are seen as virtual nodes but are actually a list of possibly-real nodes.
|
// /// 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
|
// /// 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.
|
// /// that iterates through the recursive nesting of fragments.
|
||||||
///
|
// ///
|
||||||
/// Fragments are stupid and I wish we didn't need to support them.
|
// /// Fragments are stupid and I wish we didn't need to support them.
|
||||||
///
|
// ///
|
||||||
/// This iterator only supports 3 levels of nested fragments
|
// /// This iterator only supports 3 levels of nested fragments
|
||||||
///
|
// ///
|
||||||
pub fn iterate_real_nodes<'a>(nodes: &'a [VNode<'a>]) -> RealNodeIterator<'a> {
|
// pub fn iterate_real_nodes<'a>(nodes: &'a [VNode<'a>]) -> RealNodeIterator<'a> {
|
||||||
RealNodeIterator::new(nodes)
|
// RealNodeIterator::new(nodes)
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub struct RealNodeIterator<'a> {
|
// pub struct RealNodeIterator<'a> {
|
||||||
nodes: &'a [VNode<'a>],
|
// nodes: &'a [VNode<'a>],
|
||||||
|
|
||||||
// this node is always a "real" node
|
// // this node is always a "real" node
|
||||||
// the index is "what sibling # is it"
|
// // 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
|
// // 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)>,
|
// node_stack: Vec<(&'a VNode<'a>, u32)>,
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'a> RealNodeIterator<'a> {
|
// impl<'a> RealNodeIterator<'a> {
|
||||||
// We immediately descend to the first real node we can find
|
// // We immediately descend to the first real node we can find
|
||||||
fn new(nodes: &'a [VNode<'a>]) -> Self {
|
// fn new(nodes: &'a [VNode<'a>]) -> Self {
|
||||||
let mut node_stack = Vec::new();
|
// let mut node_stack = Vec::new();
|
||||||
if nodes.len() > 0 {
|
// if nodes.len() > 0 {
|
||||||
let mut cur_node = nodes.get(0).unwrap();
|
// let mut cur_node = nodes.get(0).unwrap();
|
||||||
loop {
|
// loop {
|
||||||
node_stack.push((cur_node, 0_u32));
|
// node_stack.push((cur_node, 0_u32));
|
||||||
if !cur_node.is_real() {
|
// if !cur_node.is_real() {
|
||||||
cur_node = cur_node.get_child(0).unwrap();
|
// cur_node = cur_node.get_child(0).unwrap();
|
||||||
} else {
|
// } else {
|
||||||
break;
|
// 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
|
// // // advances the cursor to the next element, panicing if we're on the 3rd level and still finding fragments
|
||||||
// fn advance_cursor(&mut self) {
|
// // fn advance_cursor(&mut self) {
|
||||||
// let (mut cur_node, mut cur_id) = self.node_stack.last().unwrap();
|
// // let (mut cur_node, mut cur_id) = self.node_stack.last().unwrap();
|
||||||
|
|
||||||
// while !cur_node.is_real() {
|
// // while !cur_node.is_real() {
|
||||||
// match cur_node {
|
// // match cur_node {
|
||||||
// VNode::Element(_) | VNode::Text(_) => todo!(),
|
// // VNode::Element(_) | VNode::Text(_) => todo!(),
|
||||||
// VNode::Suspended => todo!(),
|
// // VNode::Suspended => todo!(),
|
||||||
// VNode::Component(_) => todo!(),
|
// // VNode::Component(_) => todo!(),
|
||||||
// VNode::Fragment(frag) => {
|
// // VNode::Fragment(frag) => {
|
||||||
// let p = frag.children;
|
// // let p = frag.children;
|
||||||
// }
|
// // }
|
||||||
// }
|
// // }
|
||||||
// }
|
// // }
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
fn next_node(&mut self) -> bool {
|
// fn next_node(&mut self) -> bool {
|
||||||
let (mut cur_node, cur_id) = self.node_stack.last_mut().unwrap();
|
// let (mut cur_node, cur_id) = self.node_stack.last_mut().unwrap();
|
||||||
|
|
||||||
match cur_node {
|
// match cur_node {
|
||||||
VNode::Fragment(frag) => {
|
// VNode::Fragment(frag) => {
|
||||||
//
|
// //
|
||||||
if *cur_id + 1 > frag.children.len() as u32 {
|
// if *cur_id + 1 > frag.children.len() as u32 {
|
||||||
self.node_stack.pop();
|
// self.node_stack.pop();
|
||||||
let next = self.node_stack.last_mut();
|
// let next = self.node_stack.last_mut();
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
*cur_id += 1;
|
// *cur_id += 1;
|
||||||
true
|
// true
|
||||||
}
|
// }
|
||||||
|
|
||||||
VNode::Element(_) => todo!(),
|
// VNode::Element(_) => todo!(),
|
||||||
VNode::Text(_) => todo!(),
|
// VNode::Text(_) => todo!(),
|
||||||
VNode::Suspended => todo!(),
|
// VNode::Suspended => todo!(),
|
||||||
VNode::Component(_) => todo!(),
|
// VNode::Component(_) => todo!(),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn get_current_node(&self) -> Option<&VNode<'a>> {
|
// fn get_current_node(&self) -> Option<&VNode<'a>> {
|
||||||
self.node_stack.last().map(|(node, id)| match node {
|
// self.node_stack.last().map(|(node, id)| match node {
|
||||||
VNode::Element(_) => todo!(),
|
// VNode::Element(_) => todo!(),
|
||||||
VNode::Text(_) => todo!(),
|
// VNode::Text(_) => todo!(),
|
||||||
VNode::Fragment(_) => todo!(),
|
// VNode::Fragment(_) => todo!(),
|
||||||
VNode::Suspended => todo!(),
|
// VNode::Suspended => todo!(),
|
||||||
VNode::Component(_) => todo!(),
|
// VNode::Component(_) => todo!(),
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'a> Iterator for RealNodeIterator<'a> {
|
// impl<'a> Iterator for RealNodeIterator<'a> {
|
||||||
type Item = &'a VNode<'a>;
|
// type Item = &'a VNode<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
// fn next(&mut self) -> Option<Self::Item> {
|
||||||
todo!()
|
// todo!()
|
||||||
// let top_idx = self.nesting_idxs.get_mut(0).unwrap();
|
// // let top_idx = self.nesting_idxs.get_mut(0).unwrap();
|
||||||
// let node = &self.nodes.get_mut(*top_idx as usize);
|
// // let node = &self.nodes.get_mut(*top_idx as usize);
|
||||||
|
|
||||||
// if node.is_none() {
|
// // if node.is_none() {
|
||||||
// return None;
|
// // return None;
|
||||||
// }
|
// // }
|
||||||
// let node = node.unwrap();
|
// // let node = node.unwrap();
|
||||||
|
|
||||||
// match node {
|
// // match node {
|
||||||
// VNode::Element(_) | VNode::Text(_) => {
|
// // VNode::Element(_) | VNode::Text(_) => {
|
||||||
// *top_idx += 1;
|
// // *top_idx += 1;
|
||||||
// return Some(node);
|
// // return Some(node);
|
||||||
// }
|
// // }
|
||||||
// VNode::Suspended => todo!(),
|
// // VNode::Suspended => todo!(),
|
||||||
// // we need access over the scope map
|
// // // we need access over the scope map
|
||||||
// VNode::Component(_) => todo!(),
|
// // VNode::Component(_) => todo!(),
|
||||||
|
|
||||||
// VNode::Fragment(frag) => {
|
// // VNode::Fragment(frag) => {
|
||||||
// let nest_idx = self.nesting_idxs.get_mut(1).unwrap();
|
// // let nest_idx = self.nesting_idxs.get_mut(1).unwrap();
|
||||||
// let node = &frag.children.get_mut(*nest_idx as usize);
|
// // let node = &frag.children.get_mut(*nest_idx as usize);
|
||||||
// match node {
|
// // match node {
|
||||||
// VNode::Element(_) | VNode::Text(_) => {
|
// // VNode::Element(_) | VNode::Text(_) => {
|
||||||
// *nest_idx += 1;
|
// // *nest_idx += 1;
|
||||||
// return Some(node);
|
// // return Some(node);
|
||||||
// }
|
// // }
|
||||||
// VNode::Fragment(_) => todo!(),
|
// // VNode::Fragment(_) => todo!(),
|
||||||
// VNode::Suspended => todo!(),
|
// // VNode::Suspended => todo!(),
|
||||||
// VNode::Component(_) => todo!(),
|
// // VNode::Component(_) => todo!(),
|
||||||
// }
|
// // }
|
||||||
// }
|
// // }
|
||||||
// }
|
// // }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::debug_renderer::DebugRenderer;
|
use crate::debug_renderer::DebugRenderer;
|
||||||
|
|||||||
@@ -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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -336,7 +336,8 @@ impl VirtualDom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn base_scope(&self) -> &Scope {
|
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 {
|
pub fn cur_frame(&self) -> &BumpFrame {
|
||||||
self.frames.cur_frame()
|
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.
|
/// Components in Dioxus use the "Context" object to interact with their lifecycle.
|
||||||
|
|||||||
Reference in new issue
Block a user