mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 22:20:19 +00:00
wip: stack-based "real child iterator"
This commit is contained in:
parent
0826fdfee1
commit
895cc0142b
6 changed files with 356 additions and 121 deletions
|
@ -39,6 +39,7 @@ log = "0.4"
|
|||
|
||||
smallvec = "1.6.1"
|
||||
|
||||
# Backs scopes and unique keys
|
||||
slotmap = "1.0.3"
|
||||
|
||||
futures = "0.3.15"
|
||||
|
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//!
|
||||
//!
|
||||
|
||||
pub mod util;
|
||||
pub mod arena;
|
||||
pub mod component; // Logic for extending FC
|
||||
|
||||
|
|
|
@ -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
57
packages/core/src/util.rs
Normal 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!()
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue