From 078b8ba833213b27e8626ffe6b0f94cdbc16faa1 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 24 Nov 2022 10:56:44 -0600 Subject: [PATCH 01/43] work on seperating tree struture from realdom --- packages/native-core/Cargo.toml | 3 +- packages/native-core/src/lib.rs | 45 +- packages/native-core/src/node.rs | 101 +++ packages/native-core/src/node_ref.rs | 9 +- packages/native-core/src/real_dom.rs | 141 +---- packages/native-core/src/state.rs | 3 +- packages/native-core/src/traversable.rs | 102 ---- packages/native-core/src/tree.rs | 781 ++++++++++++++++++++++++ packages/native-core/src/utils.rs | 428 +++++++------ 9 files changed, 1119 insertions(+), 494 deletions(-) create mode 100644 packages/native-core/src/node.rs delete mode 100644 packages/native-core/src/traversable.rs create mode 100644 packages/native-core/src/tree.rs diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 76270671a..09c08bc61 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -20,6 +20,7 @@ smallvec = "1.6" rustc-hash = "1.1.0" anymap = "0.12.1" slab = "0.4" +parking_lot = "0.12.1" [dev-dependencies] -rand = "0.8.5" +rand = "0.8.5" \ No newline at end of file diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index bf24096a3..bdd7e24be 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -1,54 +1,17 @@ -use std::cmp::Ordering; - -use dioxus_core::ElementId; +use tree::NodeId; pub mod layout_attributes; +pub mod node; pub mod node_ref; pub mod real_dom; pub mod state; #[doc(hidden)] pub mod traversable; +pub mod tree; pub mod utils; /// A id for a node that lives in the real dom. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum RealNodeId { - ElementId(ElementId), - UnaccessableId(usize), -} - -impl RealNodeId { - pub fn as_element_id(&self) -> ElementId { - match self { - RealNodeId::ElementId(id) => *id, - RealNodeId::UnaccessableId(_) => panic!("Expected element id"), - } - } - - pub fn as_unaccessable_id(&self) -> usize { - match self { - RealNodeId::ElementId(_) => panic!("Expected unaccessable id"), - RealNodeId::UnaccessableId(id) => *id, - } - } -} - -impl Ord for RealNodeId { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other).unwrap() - } -} - -impl PartialOrd for RealNodeId { - fn partial_cmp(&self, other: &Self) -> Option { - match (self, other) { - (Self::ElementId(a), Self::ElementId(b)) => a.0.partial_cmp(&b.0), - (Self::UnaccessableId(a), Self::UnaccessableId(b)) => a.partial_cmp(b), - (Self::ElementId(_), Self::UnaccessableId(_)) => Some(Ordering::Greater), - (Self::UnaccessableId(_), Self::ElementId(_)) => Some(Ordering::Less), - } - } -} +type RealNodeId = NodeId; /// Used in derived state macros #[derive(Eq, PartialEq)] diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs new file mode 100644 index 000000000..5d6285071 --- /dev/null +++ b/packages/native-core/src/node.rs @@ -0,0 +1,101 @@ +use crate::{state::State, tree::NodeId, RealNodeId}; +use dioxus_core::ElementId; +use rustc_hash::{FxHashMap, FxHashSet}; + +/// The node is stored client side and stores only basic data about the node. +#[derive(Debug, Clone)] +pub struct Node { + /// The transformed state of the node. + pub state: S, + /// The raw data for the node + pub node_data: NodeData, +} + +#[derive(Debug, Clone)] +pub struct NodeData { + /// The id of the node in the vdom. + pub element_id: Option, + /// Additional inforation specific to the node type + pub node_type: NodeType, + /// The number of parents before the root node. The root node has height 1. + pub height: u16, +} + +/// A type of node with data specific to the node type. The types are a subset of the [VNode] types. +#[derive(Debug, Clone)] +pub enum NodeType { + Text { + text: String, + }, + Element { + tag: String, + namespace: Option<&'static str>, + attributes: FxHashMap, + listeners: FxHashSet, + }, + Placeholder, +} + +impl Node { + pub(crate) fn new(node_type: NodeType) -> Self { + Node { + state: S::default(), + node_data: NodeData { + element_id: None, + node_type, + height: 0, + }, + } + } + + /// link a child node + fn add_child(&mut self, child: RealNodeId) { + if let NodeType::Element { children, .. } = &mut self.node_data.node_type { + children.push(child); + } + } + + /// remove a child node + fn remove_child(&mut self, child: RealNodeId) { + if let NodeType::Element { children, .. } = &mut self.node_data.node_type { + children.retain(|c| c != &child); + } + } + + /// link the parent node + fn set_parent(&mut self, parent: RealNodeId) { + self.node_data.parent = Some(parent); + } + + /// get the mounted id of the node + pub fn mounted_id(&self) -> Option { + self.node_data.element_id + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct OwnedAttributeDiscription { + pub name: String, + pub namespace: Option, + pub volatile: bool, +} + +/// An attribute on a DOM node, such as `id="my-thing"` or +/// `href="https://example.com"`. +#[derive(Clone, Copy, Debug)] +pub struct OwnedAttributeView<'a> { + /// The discription of the attribute. + pub attribute: &'a OwnedAttributeDiscription, + + /// The value of the attribute. + pub value: &'a OwnedAttributeValue, +} + +#[derive(Clone, Debug)] +pub enum OwnedAttributeValue { + Text(String), + Float(f32), + Int(i32), + Bool(bool), + None, +} diff --git a/packages/native-core/src/node_ref.rs b/packages/native-core/src/node_ref.rs index edbcfa696..671d85663 100644 --- a/packages/native-core/src/node_ref.rs +++ b/packages/native-core/src/node_ref.rs @@ -1,7 +1,8 @@ +use dioxus_core::ElementId; + use crate::{ - real_dom::{NodeData, NodeType, OwnedAttributeView}, + node::{NodeData, NodeType, OwnedAttributeView}, state::union_ordered_iter, - RealNodeId, }; /// A view into a [VNode] with limited access. @@ -21,8 +22,8 @@ impl<'a> NodeView<'a> { } /// Get the id of the node - pub fn id(&self) -> RealNodeId { - self.inner.id.unwrap() + pub fn id(&self) -> Option { + self.inner.element_id } /// Get the tag of the node if the tag is enabled in the mask diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 20a055778..53547de68 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,12 +1,14 @@ use anymap::AnyMap; -use dioxus_core::{AttributeDiscription, ElementId, Mutations, OwnedAttributeValue, VNode}; +use dioxus_core::{ElementId, Mutations, VNode}; use rustc_hash::{FxHashMap, FxHashSet}; use slab::Slab; use std::ops::{Index, IndexMut}; +use crate::node::{Node, NodeType}; use crate::node_ref::{AttributeMask, NodeMask}; use crate::state::State; use crate::traversable::Traversable; +use crate::tree::{NodeId, Tree, TreeLike, TreeView}; use crate::RealNodeId; /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers. @@ -14,14 +16,10 @@ use crate::RealNodeId; /// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes. #[derive(Debug)] pub struct RealDom { - root: usize, - nodes: Vec>>>, - // some nodes do not have an ElementId immediately, those node are stored here - internal_nodes: Slab>>, + tree: Tree>, + /// a map from element id to real node id + node_id_mapping: Vec, nodes_listening: FxHashMap<&'static str, FxHashSet>, - last: Option, - // any nodes that have children queued to be added in the form (parent, children remaining) - parents_queued: Vec<(RealNodeId, u32)>, } impl Default for RealDom { @@ -39,23 +37,15 @@ impl RealDom { listeners: FxHashSet::default(), children: Vec::new(), }); - root.node_data.id = Some(RealNodeId::ElementId(ElementId(0))); + root.node_data.element_id = Some(ElementId(0)); + let tree = Tree::new(root); + let root_id = tree.root(); + tree.get_mut(root_id).unwrap().node_data.id = root_id; RealDom { - root: 0, - nodes: vec![Some(Box::new(root))], - internal_nodes: Slab::new(), + tree, + node_id_mapping: Vec::new(), nodes_listening: FxHashMap::default(), - last: None, - parents_queued: Vec::new(), - } - } - - pub fn resolve_maybe_id(&self, id: Option) -> RealNodeId { - if let Some(id) = id { - RealNodeId::ElementId(ElementId(id as usize)) - } else { - self.last.unwrap() } } @@ -68,7 +58,7 @@ impl RealDom { nodes_updated.push((RealNodeId::ElementId(ElementId(0)), NodeMask::ALL)); for mutations in mutations_vec { for e in mutations.edits { - use dioxus_core::DomEdit::*; + use dioxus_core::Mutation::*; match e { AppendChildren { root, children } => { let target = self.resolve_maybe_id(root); @@ -751,81 +741,6 @@ impl IndexMut for RealDom { } } -/// The node is stored client side and stores only basic data about the node. -#[derive(Debug, Clone)] -pub struct Node { - /// The transformed state of the node. - pub state: S, - /// The raw data for the node - pub node_data: NodeData, -} - -#[derive(Debug, Clone)] -pub struct NodeData { - /// The id of the node this node was created from. - pub id: Option, - /// The parent id of the node. - pub parent: Option, - /// Additional inforation specific to the node type - pub node_type: NodeType, - /// The number of parents before the root node. The root node has height 1. - pub height: u16, -} - -/// A type of node with data specific to the node type. The types are a subset of the [VNode] types. -#[derive(Debug, Clone)] -pub enum NodeType { - Text { - text: String, - }, - Element { - tag: String, - namespace: Option<&'static str>, - attributes: FxHashMap, - listeners: FxHashSet, - children: Vec, - }, - Placeholder, -} - -impl Node { - fn new(node_type: NodeType) -> Self { - Node { - state: S::default(), - node_data: NodeData { - id: None, - parent: None, - node_type, - height: 0, - }, - } - } - - /// link a child node - fn add_child(&mut self, child: RealNodeId) { - if let NodeType::Element { children, .. } = &mut self.node_data.node_type { - children.push(child); - } - } - - /// remove a child node - fn remove_child(&mut self, child: RealNodeId) { - if let NodeType::Element { children, .. } = &mut self.node_data.node_type { - children.retain(|c| c != &child); - } - } - - /// link the parent node - fn set_parent(&mut self, parent: RealNodeId) { - self.node_data.parent = Some(parent); - } - - /// get the mounted id of the node - pub fn mounted_id(&self) -> RealNodeId { - self.node_data.id.unwrap() - } -} - impl Traversable for RealDom { type Id = RealNodeId; type Node = Node; @@ -869,33 +784,3 @@ impl Traversable for RealDom { self.get(id).and_then(|n| n.node_data.parent) } } - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct OwnedAttributeDiscription { - pub name: String, - pub namespace: Option, - pub volatile: bool, -} - -impl PartialEq for OwnedAttributeDiscription { - fn eq(&self, other: &AttributeDiscription) -> bool { - self.name == other.name - && match (&self.namespace, other.namespace) { - (Some(a), Some(b)) => a == b, - (None, None) => true, - _ => false, - } - && self.volatile == other.volatile - } -} - -/// An attribute on a DOM node, such as `id="my-thing"` or -/// `href="https://example.com"`. -#[derive(Clone, Debug)] -pub struct OwnedAttributeView<'a> { - /// The discription of the attribute. - pub attribute: &'a OwnedAttributeDiscription, - - /// The value of the attribute. - pub value: &'a OwnedAttributeValue, -} diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index 60f8187e3..d858a2a78 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -1,8 +1,7 @@ use std::{cmp::Ordering, fmt::Debug}; +use crate::node::NodeData; use crate::node_ref::{NodeMask, NodeView}; -use crate::real_dom::NodeData; -use crate::traversable::Traversable; use crate::RealNodeId; use anymap::AnyMap; use rustc_hash::FxHashSet; diff --git a/packages/native-core/src/traversable.rs b/packages/native-core/src/traversable.rs deleted file mode 100644 index e51295124..000000000 --- a/packages/native-core/src/traversable.rs +++ /dev/null @@ -1,102 +0,0 @@ -/// This is a interface for a tree with the ability to jump to a specific node -pub trait Traversable { - type Id: Copy; - type Node; - - fn height(&self, id: Self::Id) -> Option; - - fn get(&self, id: Self::Id) -> Option<&Self::Node>; - fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node>; - - fn children(&self, node: Self::Id) -> &[Self::Id]; - fn parent(&self, node: Self::Id) -> Option; - - fn map &N, FMut: Fn(&mut Self::Node) -> &mut N>( - &mut self, - f: F, - f_mut: FMut, - ) -> Map - where - Self: Sized, - { - Map { - tree: self, - f, - f_mut, - } - } - - // this is safe because no node will have itself as it's parent - fn get_node_parent_mut( - &mut self, - id: Self::Id, - ) -> (Option<&mut Self::Node>, Option<&mut Self::Node>) { - let node = self.get_mut(id).map(|n| n as *mut _); - let parent = self - .parent(id) - .and_then(|n| self.get_mut(n)) - .map(|n| n as *mut _); - unsafe { (node.map(|n| &mut *n), parent.map(|n| &mut *n)) } - } - - // this is safe because no node will have itself as a child - fn get_node_children_mut( - &mut self, - id: Self::Id, - ) -> (Option<&mut Self::Node>, Vec<&mut Self::Node>) { - let node = self.get_mut(id).map(|n| n as *mut _); - let mut children = Vec::new(); - let children_indexes = self.children(id).to_vec(); - for id in children_indexes { - if let Some(n) = self.get_mut(id) { - children.push(unsafe { &mut *(n as *mut _) }); - } - } - unsafe { (node.map(|n| &mut *n), children) } - } -} - -/// Maps one type of tree to another. Similar to [std::iter::Map]. -pub struct Map< - 'a, - T: Traversable, - N, - F: Fn(&::Node) -> &N, - FMut: Fn(&mut ::Node) -> &mut N, -> { - f: F, - f_mut: FMut, - tree: &'a mut T, -} - -impl< - 'a, - T: Traversable, - N, - F: Fn(&::Node) -> &N, - FMut: Fn(&mut ::Node) -> &mut N, - > Traversable for Map<'a, T, N, F, FMut> -{ - type Id = ::Id; - type Node = N; - - fn height(&self, id: Self::Id) -> Option { - self.tree.height(id) - } - - fn get(&self, id: Self::Id) -> Option<&Self::Node> { - self.tree.get(id).map(&self.f) - } - - fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> { - self.tree.get_mut(id).map(&self.f_mut) - } - - fn children(&self, id: Self::Id) -> &[Self::Id] { - self.tree.children(id) - } - - fn parent(&self, id: Self::Id) -> Option { - self.tree.parent(id) - } -} diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs new file mode 100644 index 000000000..76769ee54 --- /dev/null +++ b/packages/native-core/src/tree.rs @@ -0,0 +1,781 @@ +use core::panic; +use parking_lot::lock_api::RawMutex as _; +use parking_lot::{RawMutex, RwLock}; +use slab::Slab; +use std::cell::UnsafeCell; +use std::collections::VecDeque; +use std::marker::PhantomData; +use std::sync::Arc; + +#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)] +pub struct NodeId(pub usize); + +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct Node { + value: T, + parent: Option, + children: Vec, +} + +#[derive(Debug)] +pub struct Tree { + nodes: Slab>, + root: NodeId, +} + +pub trait TreeView: Sized { + type Iterator<'a>: Iterator + where + T: 'a, + Self: 'a; + type IteratorMut<'a>: Iterator + where + T: 'a, + Self: 'a; + + fn root(&self) -> NodeId; + + fn get(&self, id: NodeId) -> Option<&T>; + + fn get_unchecked(&self, id: NodeId) -> &T { + unsafe { self.get(id).unwrap_unchecked() } + } + + fn get_mut(&mut self, id: NodeId) -> Option<&mut T>; + + fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T { + unsafe { self.get_mut(id).unwrap_unchecked() } + } + + fn children<'a>(&'a self, id: NodeId) -> Option>; + + fn children_mut<'a>(&'a mut self, id: NodeId) -> Option>; + + fn parent_child_mut<'a>( + &'a mut self, + id: NodeId, + ) -> Option<(&'a mut T, Self::IteratorMut<'a>)> { + let mut_ptr: *mut Self = self; + unsafe { + // Safety: No node has itself as a child. + (*mut_ptr).get_mut(id).and_then(|parent| { + (*mut_ptr) + .children_mut(id) + .map(|children| (parent, children)) + }) + } + } + + fn children_ids<'a>(&'a self, id: NodeId) -> Option<&'a [NodeId]>; + + fn parent(&self, id: NodeId) -> Option<&T>; + + fn parent_mut(&mut self, id: NodeId) -> Option<&mut T>; + + fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, &mut T)> { + let mut_ptr: *mut Self = self; + unsafe { + // Safety: No node has itself as a parent. + (*mut_ptr) + .get_mut(id) + .and_then(|parent| (*mut_ptr).parent_mut(id).map(|children| (parent, children))) + } + } + + fn parent_id(&self, id: NodeId) -> Option; + + fn map &T2, FMut: Fn(&mut T) -> &mut T2>( + &mut self, + map: F, + map_mut: FMut, + ) -> TreeMap { + TreeMap::new(self, map, map_mut) + } + + fn size(&self) -> usize; + + fn traverse_depth_first(&self, mut f: impl FnMut(&T)) { + let mut stack = vec![self.root()]; + while let Some(id) = stack.pop() { + if let Some(node) = self.get(id) { + f(node); + if let Some(children) = self.children_ids(id) { + stack.extend(children.iter().copied().rev()); + } + } + } + } + + fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(&mut T)) { + let mut stack = vec![self.root()]; + while let Some(id) = stack.pop() { + if let Some(node) = self.get_mut(id) { + f(node); + if let Some(children) = self.children_ids(id) { + stack.extend(children.iter().copied().rev()); + } + } + } + } + + fn traverse_breadth_first(&self, mut f: impl FnMut(&T)) { + let mut queue = VecDeque::new(); + queue.push_back(self.root()); + while let Some(id) = queue.pop_front() { + if let Some(node) = self.get(id) { + f(node); + if let Some(children) = self.children_ids(id) { + for id in children { + queue.push_back(*id); + } + } + } + } + } + + fn traverse_breadth_first_mut(&mut self, mut f: impl FnMut(&mut T)) { + let mut queue = VecDeque::new(); + queue.push_back(self.root()); + while let Some(id) = queue.pop_front() { + if let Some(node) = self.get_mut(id) { + f(node); + if let Some(children) = self.children_ids(id) { + for id in children { + queue.push_back(*id); + } + } + } + } + } +} + +pub trait TreeLike: TreeView { + fn new(root: T) -> Self; + + fn add_child(&mut self, parent: NodeId, value: T) -> NodeId; + + fn remove(&mut self, id: NodeId) -> Option; + + fn replace(&mut self, id: NodeId, value: T); + + fn insert_before(&mut self, id: NodeId, value: T) -> NodeId; + + fn insert_after(&mut self, id: NodeId, value: T) -> NodeId; +} + +pub struct ChildNodeIterator<'a, T, Tr: TreeView> { + tree: &'a Tr, + children_ids: &'a [NodeId], + index: usize, + node_type: PhantomData, +} + +impl<'a, T: 'a, Tr: TreeView> Iterator for ChildNodeIterator<'a, T, Tr> { + type Item = &'a T; + + fn next(&mut self) -> Option { + self.children_ids.get(self.index).map(|id| { + self.index += 1; + self.tree.get_unchecked(*id) + }) + } +} + +pub struct ChildNodeIteratorMut<'a, T, Tr: TreeView + 'a> { + tree: *mut Tr, + children_ids: &'a [NodeId], + index: usize, + node_type: PhantomData, +} + +unsafe impl<'a, T, Tr: TreeView + 'a> Sync for ChildNodeIteratorMut<'a, T, Tr> {} + +impl<'a, T, Tr: TreeView> ChildNodeIteratorMut<'a, T, Tr> { + fn tree_mut(&mut self) -> &'a mut Tr { + unsafe { &mut *self.tree } + } +} + +impl<'a, T: 'a, Tr: TreeView> Iterator for ChildNodeIteratorMut<'a, T, Tr> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + let owned = self.children_ids.get(self.index).copied(); + match owned { + Some(id) => { + self.index += 1; + + Some(self.tree_mut().get_mut_unchecked(id)) + } + None => None, + } + } +} + +impl TreeView for Tree { + type Iterator<'a> = ChildNodeIterator<'a, T, Tree> where T: 'a; + type IteratorMut<'a> = ChildNodeIteratorMut<'a, T, Tree> where T: 'a; + + fn root(&self) -> NodeId { + self.root + } + + fn get(&self, id: NodeId) -> Option<&T> { + self.nodes.get(id.0).map(|node| &node.value) + } + + fn get_mut(&mut self, id: NodeId) -> Option<&mut T> { + self.nodes.get_mut(id.0).map(|node| &mut node.value) + } + + fn children<'a>(&'a self, id: NodeId) -> Option> { + self.children_ids(id).map(|children_ids| ChildNodeIterator { + tree: self, + children_ids, + index: 0, + node_type: PhantomData, + }) + } + + fn children_mut<'a>(&'a mut self, id: NodeId) -> Option> { + let raw_ptr = self as *mut Self; + unsafe { + // Safety: No node will appear as a child twice + self.children_ids(id) + .map(|children_ids| ChildNodeIteratorMut { + tree: &mut *raw_ptr, + children_ids, + index: 0, + node_type: PhantomData, + }) + } + } + + fn children_ids<'a>(&'a self, id: NodeId) -> Option<&'a [NodeId]> { + self.nodes.get(id.0).map(|node| node.children.as_slice()) + } + + fn parent(&self, id: NodeId) -> Option<&T> { + self.nodes + .get(id.0) + .and_then(|node| node.parent.map(|id| self.nodes.get(id.0).unwrap())) + .map(|node| &node.value) + } + + fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> { + let self_ptr = self as *mut Self; + unsafe { + // Safety: No node has itself as a parent. + self.nodes + .get_mut(id.0) + .and_then(move |node| { + node.parent + .map(move |id| (*self_ptr).nodes.get_mut(id.0).unwrap()) + }) + .map(|node| &mut node.value) + } + } + + fn parent_id(&self, id: NodeId) -> Option { + self.nodes.get(id.0).and_then(|node| node.parent) + } + + fn get_unchecked(&self, id: NodeId) -> &T { + unsafe { &self.nodes.get_unchecked(id.0).value } + } + + fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T { + unsafe { &mut self.nodes.get_unchecked_mut(id.0).value } + } + + fn size(&self) -> usize { + self.nodes.len() + } +} + +impl TreeLike for Tree { + fn new(root: T) -> Self { + let mut nodes = Slab::default(); + let root = NodeId(nodes.insert(Node { + value: root, + parent: None, + children: Vec::new(), + })); + Self { nodes, root } + } + + fn add_child(&mut self, parent: NodeId, value: T) -> NodeId { + let node = Node { + value, + parent: Some(parent), + children: Vec::new(), + }; + let id = self.nodes.insert(node); + self.nodes + .get_mut(parent.0) + .unwrap() + .children + .push(NodeId(id)); + NodeId(id) + } + + fn remove(&mut self, id: NodeId) -> Option { + self.nodes.try_remove(id.0).map(|node| { + if let Some(parent) = node.parent { + self.nodes + .get_mut(parent.0) + .unwrap() + .children + .retain(|child| child != &id); + } + node.value + }) + } + + fn replace(&mut self, id: NodeId, value: T) { + let old = self + .nodes + .get_mut(id.0) + .expect("tried to replace a node that doesn't exist"); + old.value = value; + } + + fn insert_before(&mut self, id: NodeId, value: T) -> NodeId { + let node = self.nodes.get(id.0).unwrap(); + let parent_id = node.parent.expect("tried to insert before root"); + let new = Node { + value, + parent: Some(parent_id), + children: Vec::new(), + }; + let new_id = NodeId(self.nodes.insert(new)); + let parent = self.nodes.get_mut(parent_id.0).unwrap(); + let index = parent + .children + .iter() + .position(|child| child == &id) + .unwrap(); + parent.children.insert(index, new_id); + new_id + } + + fn insert_after(&mut self, id: NodeId, value: T) -> NodeId { + let node = self.nodes.get(id.0).unwrap(); + let parent_id = node.parent.expect("tried to insert before root"); + let new = Node { + value, + parent: Some(parent_id), + children: Vec::new(), + }; + let new_id = NodeId(self.nodes.insert(new)); + let parent = self.nodes.get_mut(parent_id.0).unwrap(); + let index = parent + .children + .iter() + .position(|child| child == &id) + .unwrap(); + parent.children.insert(index + 1, new_id); + new_id + } +} + +pub struct TreeMap<'a, T1, T2, Tr, F, FMut> +where + Tr: TreeView, + F: Fn(&T1) -> &T2, + FMut: Fn(&mut T1) -> &mut T2, +{ + tree: &'a mut Tr, + map: F, + map_mut: FMut, + in_node_type: PhantomData, + out_node_type: PhantomData, +} + +impl<'a, T1, T2, Tr, F, FMut> TreeMap<'a, T1, T2, Tr, F, FMut> +where + Tr: TreeView, + F: Fn(&T1) -> &T2, + FMut: Fn(&mut T1) -> &mut T2, +{ + pub fn new(tree: &'a mut Tr, map: F, map_mut: FMut) -> Self { + TreeMap { + tree, + map, + map_mut, + in_node_type: PhantomData, + out_node_type: PhantomData, + } + } +} + +impl<'a, T1, T2, Tr, F, FMut> TreeView for TreeMap<'a, T1, T2, Tr, F, FMut> +where + Tr: TreeView, + F: Fn(&T1) -> &T2, + FMut: Fn(&mut T1) -> &mut T2, +{ + type Iterator<'b> = ChildNodeIterator<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>> + where + T2: 'b, + Self:'b; + type IteratorMut<'b> = ChildNodeIteratorMut<'b, T2, TreeMap<'a, T1, T2, Tr, F, FMut>> + where + T2: 'b, + Self:'b; + + fn root(&self) -> NodeId { + self.tree.root() + } + + fn get(&self, id: NodeId) -> Option<&T2> { + self.tree.get(id).map(|node| (self.map)(node)) + } + + fn get_mut(&mut self, id: NodeId) -> Option<&mut T2> { + self.tree.get_mut(id).map(|node| (self.map_mut)(node)) + } + + fn children<'b>(&'b self, id: NodeId) -> Option> { + self.children_ids(id).map(|children_ids| ChildNodeIterator { + tree: self, + children_ids, + index: 0, + node_type: PhantomData, + }) + } + + fn children_mut<'b>(&'b mut self, id: NodeId) -> Option> { + let raw_ptr = self as *mut Self; + unsafe { + // Safety: No node can be a child twice. + self.children_ids(id) + .map(|children_ids| ChildNodeIteratorMut { + tree: &mut *raw_ptr, + children_ids, + index: 0, + node_type: PhantomData, + }) + } + } + + fn children_ids<'b>(&'b self, id: NodeId) -> Option<&'b [NodeId]> { + self.tree.children_ids(id) + } + + fn parent(&self, id: NodeId) -> Option<&T2> { + self.tree.parent(id).map(|node| (self.map)(node)) + } + + fn parent_mut(&mut self, id: NodeId) -> Option<&mut T2> { + self.tree.parent_mut(id).map(|node| (self.map_mut)(node)) + } + + fn parent_id(&self, id: NodeId) -> Option { + self.tree.parent_id(id) + } + + fn get_unchecked(&self, id: NodeId) -> &T2 { + (self.map)(self.tree.get_unchecked(id)) + } + + fn get_mut_unchecked(&mut self, id: NodeId) -> &mut T2 { + (self.map_mut)(self.tree.get_mut_unchecked(id)) + } + + fn size(&self) -> usize { + self.tree.size() + } +} + +/// A view into a tree that can be shared between multiple threads. Nodes are locked invividually. +pub struct SharedView<'a, T, Tr: TreeView> { + tree: Arc>, + node_locks: Arc>>, + node_type: PhantomData, +} + +impl<'a, T, Tr: TreeView> SharedView<'a, T, Tr> { + /// Checks if a node is currently locked. Returns None if the node does not exist. + pub fn check_lock(&self, id: NodeId) -> Option { + let mut locks = self.node_locks.read(); + locks.get(id.0).map(|lock| lock.is_locked()) + } +} + +unsafe impl<'a, T, Tr: TreeView> Send for SharedView<'a, T, Tr> {} +unsafe impl<'a, T, Tr: TreeView> Sync for SharedView<'a, T, Tr> {} +impl<'a, T, Tr: TreeView> Clone for SharedView<'a, T, Tr> { + fn clone(&self) -> Self { + Self { + tree: self.tree.clone(), + node_locks: self.node_locks.clone(), + node_type: PhantomData, + } + } +} + +impl<'a, T, Tr: TreeView> SharedView<'a, T, Tr> { + pub fn new(tree: &'a mut Tr) -> Self { + let tree = Arc::new(UnsafeCell::new(tree)); + let mut node_locks = Vec::new(); + for _ in 0..unsafe { (*tree.get()).size() } { + node_locks.push(RawMutex::INIT); + } + Self { + tree, + node_locks: Arc::new(RwLock::new(node_locks)), + node_type: PhantomData, + } + } + + fn lock_node(&self, node: NodeId) { + let read = self.node_locks.read(); + let lock = read.get(node.0); + match lock { + Some(lock) => lock.lock(), + None => { + drop(read); + let mut write = self.node_locks.write(); + write.resize_with(node.0 + 1, || RawMutex::INIT); + unsafe { write.get_unchecked(node.0).lock() } + } + } + } + + fn unlock_node(&self, node: NodeId) { + let read = self.node_locks.read(); + let lock = read.get(node.0); + match lock { + Some(lock) => unsafe { lock.unlock() }, + None => { + panic!("unlocking node that was not locked") + } + } + } + + fn with_node(&self, node_id: NodeId, f: impl FnOnce(&'a mut Tr) -> R) -> R { + self.lock_node(node_id); + let tree = unsafe { &mut *self.tree.get() }; + let r = f(tree); + self.unlock_node(node_id); + r + } +} + +impl<'a, T, Tr: TreeView> TreeView for SharedView<'a, T, Tr> { + type Iterator<'b> = Tr::Iterator<'b> where T: 'b, Self: 'b; + + type IteratorMut<'b>=Tr::IteratorMut<'b> + where + T: 'b, + Self: 'b; + + fn root(&self) -> NodeId { + unsafe { (*self.tree.get()).root() } + } + + fn get(&self, id: NodeId) -> Option<&T> { + self.with_node(id, |t| t.get(id)) + } + + fn get_mut(&mut self, id: NodeId) -> Option<&mut T> { + self.with_node(id, |t| t.get_mut(id)) + } + + fn children<'b>(&'b self, id: NodeId) -> Option> { + self.with_node(id, |t| t.children(id)) + } + + fn children_mut<'b>(&'b mut self, id: NodeId) -> Option> { + self.with_node(id, |t| t.children_mut(id)) + } + + fn children_ids<'b>(&'b self, id: NodeId) -> Option<&'b [NodeId]> { + self.with_node(id, |t| t.children_ids(id)) + } + + fn parent(&self, id: NodeId) -> Option<&T> { + self.with_node(id, |t| t.get(id)) + } + + fn parent_mut(&mut self, id: NodeId) -> Option<&mut T> { + self.with_node(id, |t| t.parent_mut(id)) + } + + fn parent_id(&self, id: NodeId) -> Option { + self.with_node(id, |t| t.parent_id(id)) + } + + fn size(&self) -> usize { + unsafe { (*self.tree.get()).size() } + } +} + +#[test] +fn creation() { + let mut tree = Tree::new(1); + let parent = tree.root(); + let child = tree.add_child(parent, 0); + + println!("Tree: {:#?}", tree); + assert_eq!(tree.size(), 2); + assert_eq!(*tree.get(parent).unwrap(), 1); + assert_eq!(*tree.get(child).unwrap(), 0); + assert_eq!(tree.parent_id(parent), None); + assert_eq!(tree.parent_id(child).unwrap(), parent); + assert_eq!(tree.children_ids(parent).unwrap(), &[child]); +} + +#[test] +fn insertion() { + let mut tree = Tree::new(0); + let parent = tree.root(); + let child = tree.add_child(parent, 2); + let before = tree.insert_before(child, 1); + let after = tree.insert_after(child, 3); + + println!("Tree: {:#?}", tree); + assert_eq!(tree.size(), 4); + assert_eq!(*tree.get(parent).unwrap(), 0); + assert_eq!(*tree.get(before).unwrap(), 1); + assert_eq!(*tree.get(child).unwrap(), 2); + assert_eq!(*tree.get(after).unwrap(), 3); + assert_eq!(tree.parent_id(before).unwrap(), parent); + assert_eq!(tree.parent_id(child).unwrap(), parent); + assert_eq!(tree.parent_id(after).unwrap(), parent); + assert_eq!(tree.children_ids(parent).unwrap(), &[before, child, after]); +} + +#[test] +fn deletion() { + let mut tree = Tree::new(0); + let parent = tree.root(); + let child = tree.add_child(parent, 2); + let before = tree.insert_before(child, 1); + let after = tree.insert_after(child, 3); + + println!("Tree: {:#?}", tree); + assert_eq!(tree.size(), 4); + assert_eq!(*tree.get(parent).unwrap(), 0); + assert_eq!(*tree.get(before).unwrap(), 1); + assert_eq!(*tree.get(child).unwrap(), 2); + assert_eq!(*tree.get(after).unwrap(), 3); + assert_eq!(tree.parent_id(before).unwrap(), parent); + assert_eq!(tree.parent_id(child).unwrap(), parent); + assert_eq!(tree.parent_id(after).unwrap(), parent); + assert_eq!(tree.children_ids(parent).unwrap(), &[before, child, after]); + + tree.remove(child); + + println!("Tree: {:#?}", tree); + assert_eq!(tree.size(), 3); + assert_eq!(*tree.get(parent).unwrap(), 0); + assert_eq!(*tree.get(before).unwrap(), 1); + assert_eq!(tree.get(child), None); + assert_eq!(*tree.get(after).unwrap(), 3); + assert_eq!(tree.parent_id(before).unwrap(), parent); + assert_eq!(tree.parent_id(after).unwrap(), parent); + assert_eq!(tree.children_ids(parent).unwrap(), &[before, after]); + + tree.remove(before); + + println!("Tree: {:#?}", tree); + assert_eq!(tree.size(), 2); + assert_eq!(*tree.get(parent).unwrap(), 0); + assert_eq!(tree.get(before), None); + assert_eq!(*tree.get(after).unwrap(), 3); + assert_eq!(tree.parent_id(after).unwrap(), parent); + assert_eq!(tree.children_ids(parent).unwrap(), &[after]); + + tree.remove(after); + + println!("Tree: {:#?}", tree); + assert_eq!(tree.size(), 1); + assert_eq!(*tree.get(parent).unwrap(), 0); + assert_eq!(tree.get(after), None); + assert_eq!(tree.children_ids(parent).unwrap(), &[]); +} + +#[test] +fn shared_view() { + use std::thread; + let mut tree = Tree::new(1); + let parent = tree.root(); + let child = tree.add_child(parent, 0); + + let shared = SharedView::new(&mut tree); + + thread::scope(|s| { + let (mut shared1, mut shared2, mut shared3) = + (shared.clone(), shared.clone(), shared.clone()); + s.spawn(move || { + assert_eq!(*shared1.get_mut(parent).unwrap(), 1); + assert_eq!(*shared1.get_mut(child).unwrap(), 0); + }); + s.spawn(move || { + assert_eq!(*shared2.get_mut(child).unwrap(), 0); + assert_eq!(*shared2.get_mut(parent).unwrap(), 1); + }); + s.spawn(move || { + assert_eq!(*shared3.get_mut(parent).unwrap(), 1); + assert_eq!(*shared3.get_mut(child).unwrap(), 0); + }); + }); +} + +#[test] +fn map() { + #[derive(Debug, PartialEq)] + struct Value { + value: i32, + } + impl Value { + fn new(value: i32) -> Self { + Self { value } + } + } + let mut tree = Tree::new(Value::new(1)); + let parent = tree.root(); + let child = tree.add_child(parent, Value::new(0)); + + let mut mapped = tree.map(|x| &x.value, |x| &mut x.value); + + *mapped.get_mut(child).unwrap() = 1; + *mapped.get_mut(parent).unwrap() = 2; + + assert_eq!(*tree.get(parent).unwrap(), Value::new(2)); + assert_eq!(*tree.get(child).unwrap(), Value::new(1)); +} + +#[test] +fn traverse_depth_first() { + let mut tree = Tree::new(0); + let parent = tree.root(); + let child1 = tree.add_child(parent, 1); + tree.add_child(child1, 2); + let child2 = tree.add_child(parent, 3); + tree.add_child(child2, 4); + + let mut node_count = 0; + tree.traverse_depth_first(move |node| { + assert_eq!(*node, node_count); + node_count += 1; + }); +} + +#[test] +fn traverse_breadth_first() { + let mut tree = Tree::new(0); + let parent = tree.root(); + let child1 = tree.add_child(parent, 1); + tree.add_child(child1, 3); + let child2 = tree.add_child(parent, 2); + tree.add_child(child2, 4); + + let mut node_count = 0; + tree.traverse_breadth_first(move |node| { + assert_eq!(*node, node_count); + node_count += 1; + }); +} diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index aa61807a4..0a5e7ebf1 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -1,229 +1,225 @@ -use crate::{ - real_dom::{NodeType, RealDom}, - state::State, - RealNodeId, -}; -use dioxus_core::{DomEdit, ElementId, Mutations}; +// use crate::{real_dom::RealDom, state::State, RealNodeId}; +// use dioxus_core::{ElementId, Mutations}; -pub enum ElementProduced { - /// The iterator produced an element by progressing to the next node in a depth first order. - Progressed(RealNodeId), - /// The iterator reached the end of the tree and looped back to the root - Looped(RealNodeId), -} -impl ElementProduced { - pub fn id(&self) -> RealNodeId { - match self { - ElementProduced::Progressed(id) => *id, - ElementProduced::Looped(id) => *id, - } - } -} +// pub enum ElementProduced { +// /// The iterator produced an element by progressing to the next node in a depth first order. +// Progressed(RealNodeId), +// /// The iterator reached the end of the tree and looped back to the root +// Looped(RealNodeId), +// } +// impl ElementProduced { +// pub fn id(&self) -> RealNodeId { +// match self { +// ElementProduced::Progressed(id) => *id, +// ElementProduced::Looped(id) => *id, +// } +// } +// } -#[derive(Debug)] -enum NodePosition { - AtNode, - InChild(usize), -} +// #[derive(Debug)] +// enum NodePosition { +// AtNode, +// InChild(usize), +// } -impl NodePosition { - fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self { - match self { - Self::AtNode => Self::AtNode, - Self::InChild(i) => Self::InChild(f(*i)), - } - } +// impl NodePosition { +// fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self { +// match self { +// Self::AtNode => Self::AtNode, +// Self::InChild(i) => Self::InChild(f(*i)), +// } +// } - fn get_or_insert(&mut self, child_idx: usize) -> usize { - match self { - Self::AtNode => { - *self = Self::InChild(child_idx); - child_idx - } - Self::InChild(i) => *i, - } - } -} +// fn get_or_insert(&mut self, child_idx: usize) -> usize { +// match self { +// Self::AtNode => { +// *self = Self::InChild(child_idx); +// child_idx +// } +// Self::InChild(i) => *i, +// } +// } +// } -/// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom]. -/// This iterator traverses the tree depth first. -/// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]). -/// The iterator loops around when it reaches the end or the beginning. -pub struct PersistantElementIter { - // stack of elements and fragments - stack: smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, -} +// /// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom]. +// /// This iterator traverses the tree depth first. +// /// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]). +// /// The iterator loops around when it reaches the end or the beginning. +// pub struct PersistantElementIter { +// // stack of elements and fragments +// stack: smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, +// } -impl Default for PersistantElementIter { - fn default() -> Self { - PersistantElementIter { - stack: smallvec::smallvec![(RealNodeId::ElementId(ElementId(0)), NodePosition::AtNode)], - } - } -} +// impl Default for PersistantElementIter { +// fn default() -> Self { +// PersistantElementIter { +// stack: smallvec::smallvec![(RealNodeId::ElementId(ElementId(0)), NodePosition::AtNode)], +// } +// } +// } -impl PersistantElementIter { - pub fn new() -> Self { - Self::default() - } +// impl PersistantElementIter { +// pub fn new() -> Self { +// Self::default() +// } - /// remove stale element refreneces - /// returns true if the focused element is removed - pub fn prune(&mut self, mutations: &Mutations, rdom: &RealDom) -> bool { - let mut changed = false; - let ids_removed: Vec<_> = mutations - .edits - .iter() - .filter_map(|e| { - // nodes within templates will never be removed - if let DomEdit::Remove { root } = e { - let id = rdom.resolve_maybe_id(*root); - Some(id) - } else { - None - } - }) - .collect(); - // if any element is removed in the chain, remove it and its children from the stack - if let Some(r) = self - .stack - .iter() - .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id)) - { - self.stack.truncate(r); - changed = true; - } - // if a child is removed or inserted before or at the current element, update the child index - for (el_id, child_idx) in self.stack.iter_mut() { - if let NodePosition::InChild(child_idx) = child_idx { - if let NodeType::Element { children, .. } = &rdom[*el_id].node_data.node_type { - for m in &mutations.edits { - match m { - DomEdit::Remove { root } => { - let id = rdom.resolve_maybe_id(*root); - if children.iter().take(*child_idx + 1).any(|c| *c == id) { - *child_idx -= 1; - } - } - DomEdit::InsertBefore { root, nodes } => { - let id = rdom.resolve_maybe_id(*root); - let n = nodes.len(); - if children.iter().take(*child_idx + 1).any(|c| *c == id) { - *child_idx += n as usize; - } - } - DomEdit::InsertAfter { root, nodes } => { - let id = rdom.resolve_maybe_id(*root); - let n = nodes.len(); - if children.iter().take(*child_idx).any(|c| *c == id) { - *child_idx += n as usize; - } - } - _ => (), - } - } - } - } - } - changed - } +// /// remove stale element refreneces +// /// returns true if the focused element is removed +// pub fn prune(&mut self, mutations: &Mutations, rdom: &RealDom) -> bool { +// let mut changed = false; +// let ids_removed: Vec<_> = mutations +// .edits +// .iter() +// .filter_map(|e| { +// // nodes within templates will never be removed +// if let DomEdit::Remove { root } = e { +// let id = rdom.resolve_maybe_id(*root); +// Some(id) +// } else { +// None +// } +// }) +// .collect(); +// // if any element is removed in the chain, remove it and its children from the stack +// if let Some(r) = self +// .stack +// .iter() +// .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id)) +// { +// self.stack.truncate(r); +// changed = true; +// } +// // if a child is removed or inserted before or at the current element, update the child index +// for (el_id, child_idx) in self.stack.iter_mut() { +// if let NodePosition::InChild(child_idx) = child_idx { +// if let NodeType::Element { children, .. } = &rdom[*el_id].node_data.node_type { +// for m in &mutations.edits { +// match m { +// DomEdit::Remove { root } => { +// let id = rdom.resolve_maybe_id(*root); +// if children.iter().take(*child_idx + 1).any(|c| *c == id) { +// *child_idx -= 1; +// } +// } +// DomEdit::InsertBefore { root, nodes } => { +// let id = rdom.resolve_maybe_id(*root); +// let n = nodes.len(); +// if children.iter().take(*child_idx + 1).any(|c| *c == id) { +// *child_idx += n as usize; +// } +// } +// DomEdit::InsertAfter { root, nodes } => { +// let id = rdom.resolve_maybe_id(*root); +// let n = nodes.len(); +// if children.iter().take(*child_idx).any(|c| *c == id) { +// *child_idx += n as usize; +// } +// } +// _ => (), +// } +// } +// } +// } +// } +// changed +// } - /// get the next element - pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { - if self.stack.is_empty() { - let id = RealNodeId::ElementId(ElementId(0)); - let new = (id, NodePosition::AtNode); - self.stack.push(new); - ElementProduced::Looped(id) - } else { - let (last, o_child_idx) = self.stack.last_mut().unwrap(); - let node = &rdom[*last]; - match &node.node_data.node_type { - NodeType::Element { children, .. } => { - *o_child_idx = o_child_idx.map(|i| i + 1); - // if we have children, go to the next child - let child_idx = o_child_idx.get_or_insert(0); - if child_idx >= children.len() { - self.pop(); - self.next(rdom) - } else { - let id = children[child_idx]; - if let NodeType::Element { .. } = &rdom[id].node_data.node_type { - self.stack.push((id, NodePosition::AtNode)); - } - ElementProduced::Progressed(id) - } - } +// /// get the next element +// pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { +// if self.stack.is_empty() { +// let id = RealNodeId::ElementId(ElementId(0)); +// let new = (id, NodePosition::AtNode); +// self.stack.push(new); +// ElementProduced::Looped(id) +// } else { +// let (last, o_child_idx) = self.stack.last_mut().unwrap(); +// let node = &rdom[*last]; +// match &node.node_data.node_type { +// NodeType::Element { children, .. } => { +// *o_child_idx = o_child_idx.map(|i| i + 1); +// // if we have children, go to the next child +// let child_idx = o_child_idx.get_or_insert(0); +// if child_idx >= children.len() { +// self.pop(); +// self.next(rdom) +// } else { +// let id = children[child_idx]; +// if let NodeType::Element { .. } = &rdom[id].node_data.node_type { +// self.stack.push((id, NodePosition::AtNode)); +// } +// ElementProduced::Progressed(id) +// } +// } - NodeType::Text { .. } | NodeType::Placeholder { .. } => { - // we are at a leaf, so we are done - ElementProduced::Progressed(self.pop()) - } - } - } - } +// NodeType::Text { .. } | NodeType::Placeholder { .. } => { +// // we are at a leaf, so we are done +// ElementProduced::Progressed(self.pop()) +// } +// } +// } +// } - /// get the previous element - pub fn prev(&mut self, rdom: &RealDom) -> ElementProduced { - // recursively add the last child element to the stack - fn push_back( - stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, - new_node: RealNodeId, - rdom: &RealDom, - ) -> RealNodeId { - match &rdom[new_node].node_data.node_type { - NodeType::Element { children, .. } => { - if children.is_empty() { - new_node - } else { - stack.push((new_node, NodePosition::InChild(children.len() - 1))); - push_back(stack, *children.last().unwrap(), rdom) - } - } - _ => new_node, - } - } - if self.stack.is_empty() { - let new_node = RealNodeId::ElementId(ElementId(0)); - ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom)) - } else { - let (last, o_child_idx) = self.stack.last_mut().unwrap(); - let node = &rdom[*last]; - match &node.node_data.node_type { - NodeType::Element { children, .. } => { - // if we have children, go to the next child - if let NodePosition::InChild(0) = o_child_idx { - ElementProduced::Progressed(self.pop()) - } else { - *o_child_idx = o_child_idx.map(|i| i - 1); - if let NodePosition::InChild(child_idx) = o_child_idx { - if *child_idx >= children.len() || children.is_empty() { - self.pop(); - self.prev(rdom) - } else { - let new_node = children[*child_idx]; - ElementProduced::Progressed(push_back( - &mut self.stack, - new_node, - rdom, - )) - } - } else { - self.pop(); - self.prev(rdom) - } - } - } +// /// get the previous element +// pub fn prev(&mut self, rdom: &RealDom) -> ElementProduced { +// // recursively add the last child element to the stack +// fn push_back( +// stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, +// new_node: RealNodeId, +// rdom: &RealDom, +// ) -> RealNodeId { +// match &rdom[new_node].node_data.node_type { +// NodeType::Element { children, .. } => { +// if children.is_empty() { +// new_node +// } else { +// stack.push((new_node, NodePosition::InChild(children.len() - 1))); +// push_back(stack, *children.last().unwrap(), rdom) +// } +// } +// _ => new_node, +// } +// } +// if self.stack.is_empty() { +// let new_node = RealNodeId::ElementId(ElementId(0)); +// ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom)) +// } else { +// let (last, o_child_idx) = self.stack.last_mut().unwrap(); +// let node = &rdom[*last]; +// match &node.node_data.node_type { +// NodeType::Element { children, .. } => { +// // if we have children, go to the next child +// if let NodePosition::InChild(0) = o_child_idx { +// ElementProduced::Progressed(self.pop()) +// } else { +// *o_child_idx = o_child_idx.map(|i| i - 1); +// if let NodePosition::InChild(child_idx) = o_child_idx { +// if *child_idx >= children.len() || children.is_empty() { +// self.pop(); +// self.prev(rdom) +// } else { +// let new_node = children[*child_idx]; +// ElementProduced::Progressed(push_back( +// &mut self.stack, +// new_node, +// rdom, +// )) +// } +// } else { +// self.pop(); +// self.prev(rdom) +// } +// } +// } - NodeType::Text { .. } | NodeType::Placeholder { .. } => { - // we are at a leaf, so we are done - ElementProduced::Progressed(self.pop()) - } - } - } - } +// NodeType::Text { .. } | NodeType::Placeholder { .. } => { +// // we are at a leaf, so we are done +// ElementProduced::Progressed(self.pop()) +// } +// } +// } +// } - fn pop(&mut self) -> RealNodeId { - self.stack.pop().unwrap().0 - } -} +// fn pop(&mut self) -> RealNodeId { +// self.stack.pop().unwrap().0 +// } +// } From dcad2fe8a2c8d74f890e3ef5f0fa1fd9311ec5c0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 24 Nov 2022 15:05:15 -0600 Subject: [PATCH 02/43] intial update to new mutations --- packages/native-core/src/lib.rs | 2 - packages/native-core/src/node.rs | 28 +- packages/native-core/src/node_ref.rs | 52 +- packages/native-core/src/real_dom.rs | 917 +++++++++------------------ packages/native-core/src/state.rs | 23 +- packages/native-core/src/tree.rs | 173 +++-- 6 files changed, 440 insertions(+), 755 deletions(-) diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index bdd7e24be..895bb7dc9 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -5,8 +5,6 @@ pub mod node; pub mod node_ref; pub mod real_dom; pub mod state; -#[doc(hidden)] -pub mod traversable; pub mod tree; pub mod utils; diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index 5d6285071..09dd03e71 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -1,4 +1,4 @@ -use crate::{state::State, tree::NodeId, RealNodeId}; +use crate::{state::State, tree::NodeId}; use dioxus_core::ElementId; use rustc_hash::{FxHashMap, FxHashSet}; @@ -13,12 +13,15 @@ pub struct Node { #[derive(Debug, Clone)] pub struct NodeData { + /// The id of the node + pub node_id: NodeId, /// The id of the node in the vdom. pub element_id: Option, /// Additional inforation specific to the node type pub node_type: NodeType, /// The number of parents before the root node. The root node has height 1. pub height: u16, + height_dirty: bool, } /// A type of node with data specific to the node type. The types are a subset of the [VNode] types. @@ -29,7 +32,7 @@ pub enum NodeType { }, Element { tag: String, - namespace: Option<&'static str>, + namespace: Option, attributes: FxHashMap, listeners: FxHashSet, }, @@ -44,29 +47,12 @@ impl Node { element_id: None, node_type, height: 0, + node_id: NodeId(0), + height_dirty: true, }, } } - /// link a child node - fn add_child(&mut self, child: RealNodeId) { - if let NodeType::Element { children, .. } = &mut self.node_data.node_type { - children.push(child); - } - } - - /// remove a child node - fn remove_child(&mut self, child: RealNodeId) { - if let NodeType::Element { children, .. } = &mut self.node_data.node_type { - children.retain(|c| c != &child); - } - } - - /// link the parent node - fn set_parent(&mut self, parent: RealNodeId) { - self.node_data.parent = Some(parent); - } - /// get the mounted id of the node pub fn mounted_id(&self) -> Option { self.node_data.element_id diff --git a/packages/native-core/src/node_ref.rs b/packages/native-core/src/node_ref.rs index 671d85663..759cc5ffb 100644 --- a/packages/native-core/src/node_ref.rs +++ b/packages/native-core/src/node_ref.rs @@ -42,7 +42,7 @@ impl<'a> NodeView<'a> { self.mask .namespace .then_some(match &self.inner.node_type { - NodeType::Element { namespace, .. } => *namespace, + NodeType::Element { namespace, .. } => namespace.as_deref(), _ => None, }) .flatten() @@ -93,7 +93,7 @@ impl<'a> NodeView<'a> { pub enum AttributeMask { All, /// A list of attribute names that are visible, this list must be sorted - Dynamic(Vec<&'static str>), + Dynamic(Vec), /// A list of attribute names that are visible, this list must be sorted Static(&'static [&'static str]), } @@ -105,14 +105,14 @@ impl AttributeMask { fn contains_attribute(&self, attr: &str) -> bool { match self { AttributeMask::All => true, - AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(), + AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(), AttributeMask::Static(l) => l.binary_search(&attr).is_ok(), } } /// Create a new dynamic attribute mask with a single attribute - pub fn single(new: &'static str) -> Self { - Self::Dynamic(vec![new]) + pub fn single(new: &str) -> Self { + Self::Dynamic(vec![new.to_string()]) } /// Ensure the attribute list is sorted. @@ -133,15 +133,27 @@ impl AttributeMask { /// Combine two attribute masks pub fn union(&self, other: &Self) -> Self { let new = match (self, other) { - (AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic( - union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), - ), - (AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic( - union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), - ), - (AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic( - union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), - ), + (AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => { + AttributeMask::Dynamic(union_ordered_iter( + s.iter().map(|s| s.as_str()), + o.iter().map(|s| s.as_str()), + s.len() + o.len(), + )) + } + (AttributeMask::Static(s), AttributeMask::Dynamic(o)) => { + AttributeMask::Dynamic(union_ordered_iter( + s.iter().copied(), + o.iter().map(|s| s.as_str()), + s.len() + o.len(), + )) + } + (AttributeMask::Dynamic(s), AttributeMask::Static(o)) => { + AttributeMask::Dynamic(union_ordered_iter( + s.iter().map(|s| s.as_str()), + o.iter().copied(), + s.len() + o.len(), + )) + } (AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic( union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()), ), @@ -153,9 +165,9 @@ impl AttributeMask { /// Check if two attribute masks overlap fn overlaps(&self, other: &Self) -> bool { - fn overlaps_iter( - self_iter: impl Iterator, - mut other_iter: impl Iterator, + fn overlaps_iter<'a>( + self_iter: impl Iterator, + mut other_iter: impl Iterator, ) -> bool { if let Some(mut other_attr) = other_iter.next() { for self_attr in self_iter { @@ -180,13 +192,13 @@ impl AttributeMask { (AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(), (AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(), (AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => { - overlaps_iter(v1.iter().copied(), v2.iter().copied()) + overlaps_iter(v1.iter().map(|s| s.as_str()), v2.iter().map(|s| s.as_str())) } (AttributeMask::Dynamic(v), AttributeMask::Static(s)) => { - overlaps_iter(v.iter().copied(), s.iter().copied()) + overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied()) } (AttributeMask::Static(s), AttributeMask::Dynamic(v)) => { - overlaps_iter(v.iter().copied(), s.iter().copied()) + overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied()) } (AttributeMask::Static(s1), AttributeMask::Static(s2)) => { overlaps_iter(s1.iter().copied(), s2.iter().copied()) diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 53547de68..cbeb80f2b 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,13 +1,11 @@ use anymap::AnyMap; -use dioxus_core::{ElementId, Mutations, VNode}; +use dioxus_core::{ElementId, Mutations}; use rustc_hash::{FxHashMap, FxHashSet}; -use slab::Slab; -use std::ops::{Index, IndexMut}; +use std::ops::{Deref, DerefMut, Index, IndexMut}; -use crate::node::{Node, NodeType}; +use crate::node::{Node, NodeData, NodeType, OwnedAttributeDiscription}; use crate::node_ref::{AttributeMask, NodeMask}; use crate::state::State; -use crate::traversable::Traversable; use crate::tree::{NodeId, Tree, TreeLike, TreeView}; use crate::RealNodeId; @@ -18,8 +16,10 @@ use crate::RealNodeId; pub struct RealDom { tree: Tree>, /// a map from element id to real node id - node_id_mapping: Vec, - nodes_listening: FxHashMap<&'static str, FxHashSet>, + node_id_mapping: Vec>, + nodes_listening: FxHashMap>, + stack: Vec, + templates: FxHashMap>, } impl Default for RealDom { @@ -32,369 +32,307 @@ impl RealDom { pub fn new() -> RealDom { let mut root = Node::new(NodeType::Element { tag: "Root".to_string(), - namespace: Some("Root"), + namespace: Some("Root".to_string()), attributes: FxHashMap::default(), listeners: FxHashSet::default(), - children: Vec::new(), }); root.node_data.element_id = Some(ElementId(0)); - let tree = Tree::new(root); + let mut tree = Tree::new(root); let root_id = tree.root(); - tree.get_mut(root_id).unwrap().node_data.id = root_id; + tree.get_mut(root_id).unwrap().node_data.node_id = root_id; RealDom { tree, node_id_mapping: Vec::new(), nodes_listening: FxHashMap::default(), + stack: vec![root_id], + templates: FxHashMap::default(), } } + fn element_to_node_id(&self, element_id: ElementId) -> RealNodeId { + self.node_id_mapping.get(element_id.0).unwrap().unwrap() + } + + fn set_element_id(&mut self, node_id: NodeId, element_id: ElementId) { + let node = self.tree.get_mut(node_id).unwrap(); + let node_id = node.node_data.node_id; + node.node_data.element_id = Some(element_id); + if self.node_id_mapping.len() <= element_id.0 { + self.node_id_mapping.resize(element_id.0 + 1, None); + } + self.node_id_mapping[element_id.0] = Some(node_id); + } + + fn load_child(&self, path: &[u8]) -> RealNodeId { + let mut current = *self.stack.last().unwrap(); + for i in path { + current = self.tree.children_ids(current).unwrap()[*i as usize]; + } + current + } + + fn create_node(&mut self, node: Node) -> RealNodeId { + let node_id = self.tree.create_node(node); + let node = self.tree.get_mut(node_id).unwrap(); + node.node_data.node_id = node_id; + node_id + } + /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. - pub fn apply_mutations( + pub fn apply_mutations<'a>( &mut self, - mutations_vec: Vec, + mutations_vec: Vec>, ) -> Vec<(RealNodeId, NodeMask)> { - let mut nodes_updated = Vec::new(); - nodes_updated.push((RealNodeId::ElementId(ElementId(0)), NodeMask::ALL)); + let mut nodes_updated: Vec<(RealNodeId, NodeMask)> = Vec::new(); for mutations in mutations_vec { for e in mutations.edits { use dioxus_core::Mutation::*; match e { - AppendChildren { root, children } => { - let target = self.resolve_maybe_id(root); - for id in children { - let id = RealNodeId::ElementId(ElementId(id as usize)); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - self.link_child(id, target).unwrap(); + AppendChildren { m } => { + let data = self.stack.split_off(m); + let (parent, children) = data.split_first().unwrap(); + for child in children { + self.tree.add_child(*parent, *child); + nodes_updated.push((*parent, NodeMask::ALL)); } } - ReplaceWith { root, nodes } => { - let id_to_replace = self.resolve_maybe_id(root); - let target = self[id_to_replace].node_data.parent.unwrap(); - for id in nodes { - let id = RealNodeId::ElementId(ElementId(id as usize)); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - self.link_child_before(id, target, id_to_replace).unwrap(); - } - self.remove(id_to_replace).unwrap(); + AssignId { path, id } => { + self.set_element_id(self.load_child(&path), id); } - InsertAfter { root, nodes } => { - let root = self.resolve_maybe_id(root); - let target = self.parent(root).unwrap(); - for id in nodes { - let id = RealNodeId::ElementId(ElementId(id as usize)); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - self.link_child_after(id, target, root).unwrap(); - } - } - InsertBefore { root, nodes } => { - let root = self.resolve_maybe_id(root); - let target = self.parent(root).unwrap(); - for id in nodes { - let id = RealNodeId::ElementId(ElementId(id as usize)); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - self.link_child_before(id, target, root).unwrap(); - } - } - Remove { root } => { - let id = self.resolve_maybe_id(root); - if let Some(parent) = self.parent(id) { - self.mark_dirty(parent, NodeMask::NONE, &mut nodes_updated); - } - self.remove(id).unwrap(); - } - CreateTextNode { root, text } => { - let n = Node::new(NodeType::Text { - text: text.to_string(), - }); - let id = self.insert(n, root, &mut nodes_updated); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - if let Some((parent, remaining)) = self.parents_queued.last_mut() { - *remaining -= 1; - let parent = *parent; - if *remaining == 0 { - self.parents_queued.pop(); - } - self.link_child(id, parent).unwrap(); - } - self.last = Some(id); - } - CreateElement { - root, - tag, - children, - } => { - let n = Node::new(NodeType::Element { - tag: tag.to_string(), + CreateElement { name } => { + let node = Node::new(NodeType::Element { + tag: name.to_string(), namespace: None, attributes: FxHashMap::default(), listeners: FxHashSet::default(), - children: Vec::new(), }); - let id = self.insert(n, root, &mut nodes_updated); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - if let Some((parent, remaining)) = self.parents_queued.last_mut() { - *remaining -= 1; - let parent = *parent; - if *remaining == 0 { - self.parents_queued.pop(); - } - self.link_child(id, parent).unwrap(); - } - self.last = Some(id); - if children > 0 { - self.parents_queued.push((id, children)); - } + let id = self.create_node(node); + self.stack.push(id); + nodes_updated.push((id, NodeMask::ALL)); } - CreateElementNs { - root, - tag, - ns, - children, - } => { - let n = Node::new(NodeType::Element { - tag: tag.to_string(), - namespace: Some(ns), + CreateElementNamespace { name, namespace } => { + let node = Node::new(NodeType::Element { + tag: name.to_string(), + namespace: Some(namespace.to_string()), attributes: FxHashMap::default(), listeners: FxHashSet::default(), - children: Vec::new(), }); - let id = self.insert(n, root, &mut nodes_updated); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - if let Some((parent, remaining)) = self.parents_queued.last_mut() { - *remaining -= 1; - let parent = *parent; - if *remaining == 0 { - self.parents_queued.pop(); - } - self.link_child(id, parent).unwrap(); + let id = self.create_node(node); + self.stack.push(id); + nodes_updated.push((id, NodeMask::ALL)); + } + CreatePlaceholder { id } => { + let node = Node::new(NodeType::Placeholder); + let node_id = self.create_node(node); + self.set_element_id(node_id, id); + self.stack.push(node_id); + nodes_updated.push((node_id, NodeMask::ALL)); + } + CreateStaticPlaceholder => { + let node = Node::new(NodeType::Placeholder); + let id = self.create_node(node); + self.stack.push(id); + nodes_updated.push((id, NodeMask::ALL)); + } + CreateStaticText { value } => { + let node = Node::new(NodeType::Text { + text: value.to_string(), + }); + let id = self.create_node(node); + self.stack.push(id); + nodes_updated.push((id, NodeMask::new().with_text())); + } + CreateTextNode { value, id } => { + let node = Node::new(NodeType::Text { + text: value.to_string(), + }); + let node_id = self.create_node(node); + let node = self.tree.get_mut(node_id).unwrap(); + node.node_data.element_id = Some(id); + self.stack.push(node_id); + nodes_updated.push((node_id, NodeMask::new().with_text())); + } + HydrateText { path, value, id } => { + let node_id = self.load_child(path); + self.set_element_id(node_id, id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Text { text } = &mut node.node_data.node_type { + *text = value.to_string(); } - self.last = Some(id); - if children > 0 { - self.parents_queued.push((id, children)); + nodes_updated.push((node_id, NodeMask::new().with_text())); + } + LoadTemplate { name, index, id } => { + let template_id = self.templates[name][index]; + let clone_id = self.clone_node(template_id); + self.set_element_id(clone_id, id); + } + ReplaceWith { id, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.element_to_node_id(id); + for new in new_nodes { + self.tree.insert_after(old_node_id, new); + nodes_updated.push((new, NodeMask::ALL)); + } + self.tree.remove(old_node_id); + } + ReplacePlaceholder { path, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.load_child(path); + for new in new_nodes { + self.tree.insert_after(old_node_id, new); + nodes_updated.push((new, NodeMask::ALL)); + } + self.tree.remove(old_node_id); + } + InsertAfter { id, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.element_to_node_id(id); + for new in new_nodes { + self.tree.insert_after(old_node_id, new); + nodes_updated.push((new, NodeMask::ALL)); } } - CreatePlaceholder { root } => { - let n = Node::new(NodeType::Placeholder); - let id = self.insert(n, root, &mut nodes_updated); - self.mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - if let Some((parent, remaining)) = self.parents_queued.last_mut() { - *remaining -= 1; - let parent = *parent; - if *remaining == 0 { - self.parents_queued.pop(); - } - self.link_child(id, parent).unwrap(); + InsertBefore { id, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.element_to_node_id(id); + for new in new_nodes { + self.tree.insert_before(old_node_id, new); + nodes_updated.push((new, NodeMask::ALL)); } - self.last = Some(id); + } + SaveTemplate { name, m } => { + let template = self.stack.split_off(m); + self.templates.insert(name.to_string(), template); + } + SetAttribute { + name, + value, + id, + ns, + } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type + { + attributes.insert( + OwnedAttributeDiscription { + name: name.to_string(), + namespace: ns.map(|s| s.to_string()), + volatile: false, + }, + crate::node::OwnedAttributeValue::Text(value.to_string()), + ); + nodes_updated.push(( + node_id, + NodeMask::new_with_attrs(AttributeMask::single(name)), + )); + } + } + SetStaticAttribute { name, value, ns } => { + let node_id = self.stack.last().unwrap(); + let node = self.tree.get_mut(*node_id).unwrap(); + if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type + { + attributes.insert( + OwnedAttributeDiscription { + name: name.to_string(), + namespace: ns.map(|s| s.to_string()), + volatile: false, + }, + crate::node::OwnedAttributeValue::Text(value.to_string()), + ); + nodes_updated.push(( + *node_id, + NodeMask::new_with_attrs(AttributeMask::single(name)), + )); + } + } + SetBoolAttribute { name, value, id } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type + { + attributes.insert( + OwnedAttributeDiscription { + name: name.to_string(), + namespace: None, + volatile: false, + }, + crate::node::OwnedAttributeValue::Bool(value), + ); + nodes_updated.push(( + node_id, + NodeMask::new_with_attrs(AttributeMask::single(name)), + )); + } + } + SetInnerText { value } => { + let node_id = *self.stack.last().unwrap(); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { .. } = &mut node.node_data.node_type { + self.tree.remove_all_children(node_id); + let text_node = Node::new(NodeType::Text { + text: value.to_string(), + }); + let text_node_id = self.create_node(text_node); + self.tree.add_child(node_id, text_node_id); + } + } + SetText { value, id } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Text { text } = &mut node.node_data.node_type { + *text = value.to_string(); + } + nodes_updated.push((node_id, NodeMask::new().with_text())); } NewEventListener { event_name, scope: _, - root, + id, } => { - let id = self.resolve_maybe_id(root); - self.mark_dirty(id, NodeMask::new().with_listeners(), &mut nodes_updated); - match &mut self[id].node_data.node_type { - NodeType::Text { .. } => panic!("Text nodes cannot have listeners"), - NodeType::Element { listeners, .. } => { - listeners.insert(event_name.to_string()); + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { + match self.nodes_listening.get_mut(event_name) { + Some(hs) => { + hs.insert(node_id); + } + None => { + let mut hs = FxHashSet::default(); + hs.insert(node_id); + self.nodes_listening.insert(event_name.to_string(), hs); + } } - NodeType::Placeholder => panic!("Placeholder cannot have listeners"), - } - if let Some(v) = self.nodes_listening.get_mut(event_name) { - v.insert(id); - } else { - let mut hs = FxHashSet::default(); - hs.insert(id); - self.nodes_listening.insert(event_name, hs); + listeners.insert(event_name.to_string()); } } - RemoveEventListener { root, event } => { - let id = self.resolve_maybe_id(root); - self.mark_dirty(id, NodeMask::new().with_listeners(), &mut nodes_updated); - let v = self.nodes_listening.get_mut(event).unwrap(); - v.remove(&id); - } - SetText { - root, - text: new_text, - } => { - let id = self.resolve_maybe_id(root); - self.mark_dirty(id, NodeMask::new().with_text(), &mut nodes_updated); - let target = &mut self[id]; - match &mut target.node_data.node_type { - NodeType::Text { text } => { - *text = new_text.to_string(); - } - _ => unreachable!(), + RemoveEventListener { id, event } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { + listeners.remove(event); } + self.nodes_listening + .get_mut(event) + .unwrap() + .remove(&node_id); } - SetAttribute { - root, - field, - ns, - value, - } => { - let id = self.resolve_maybe_id(root); - if let NodeType::Element { attributes, .. } = - &mut self[id].node_data.node_type - { - attributes.insert( - OwnedAttributeDiscription { - name: field.to_owned(), - namespace: ns.map(|a| a.to_owned()), - volatile: false, - }, - value.into(), - ); - } else { - panic!("tried to call set attribute on a non element"); - } - self.mark_dirty( - id, - NodeMask::new_with_attrs(AttributeMask::single(field)), - &mut nodes_updated, - ); - } - RemoveAttribute { - root, name: field, .. - } => { - let id = self.resolve_maybe_id(root); - self.mark_dirty( - id, - NodeMask::new_with_attrs(AttributeMask::single(field)), - &mut nodes_updated, - ); - } - CloneNode { id, new_id } => { - let id = self.resolve_maybe_id(id); - self.clone_node_into(id, &mut nodes_updated, Some(new_id)); - } - CloneNodeChildren { id, new_ids } => { - let id = self.resolve_maybe_id(id); - let bounded_self: &mut Self = self; - let unbounded_self: &mut Self = - unsafe { std::mem::transmute(bounded_self) }; - if let NodeType::Element { children, .. } = &self[id].node_data.node_type { - for (old_id, new_id) in children.iter().zip(new_ids) { - let child_id = unbounded_self.clone_node_into( - *old_id, - &mut nodes_updated, - Some(new_id), - ); - unbounded_self[child_id].node_data.parent = None; - } - } - } - FirstChild {} => { - if let NodeType::Element { children, .. } = - &self[self.last.unwrap()].node_data.node_type - { - self.last = Some(children[0]); - } else { - panic!("tried to call first child on a non element"); - } - } - NextSibling {} => { - let id = self.last.unwrap(); - if let Some(parent) = self.parent(id) { - if let NodeType::Element { children, .. } = - &self[parent].node_data.node_type - { - let index = children.iter().position(|a| *a == id).unwrap(); - self.last = Some(children[index + 1]); - } - } else { - panic!("tried to call next sibling on a non element"); - } - } - ParentNode {} => { - if let Some(parent) = self.parent(self.last.unwrap()) { - self.last = Some(parent); - } else { - panic!("tried to call parent node on a non element"); - } - } - StoreWithId { id } => { - let old_id = self.last.unwrap(); - let node = self.internal_nodes.remove(old_id.as_unaccessable_id()); - let new_id = self.insert(*node, Some(id), &mut nodes_updated); - self.update_id(old_id, new_id, &mut nodes_updated); - } - SetLastNode { id } => { - self.last = - Some(RealNodeId::ElementId(dioxus_core::ElementId(id as usize))); + Remove { id } => { + let node_id = self.element_to_node_id(id); + self.remove(node_id); } } } } // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - nodes_updated.retain(|n| match n.0 { - RealNodeId::ElementId(id) => self.nodes.get(id.0).and_then(|o| o.as_ref()).is_some(), - RealNodeId::UnaccessableId(id) => self.internal_nodes.get(id).is_some(), - }); + nodes_updated.retain(|n| self.tree.get(n.0).is_some()); nodes_updated } - /// Update refrences to an old node id to a new node id - fn update_id( - &mut self, - old_id: RealNodeId, - new_id: RealNodeId, - nodes_updated: &mut Vec<(RealNodeId, NodeMask)>, - ) { - // this is safe because a node cannot have itself as a child or parent - let unbouned_self = unsafe { &mut *(self as *mut Self) }; - // update parent's link to child id - if let Some(parent) = self[new_id].node_data.parent { - if let NodeType::Element { children, .. } = &mut self[parent].node_data.node_type { - for c in children { - if *c == old_id { - *c = new_id; - break; - } - } - } - } - // update child's link to parent id - if let NodeType::Element { children, .. } = &self[new_id].node_data.node_type { - for child_id in children { - unbouned_self[*child_id].node_data.parent = Some(new_id); - } - } - // update dirty nodes - for (node, _) in nodes_updated { - if *node == old_id { - *node = new_id; - } - } - // update nodes listening - for v in self.nodes_listening.values_mut() { - if v.contains(&old_id) { - v.remove(&old_id); - v.insert(new_id); - } - } - // update last - if let Some(last) = self.last { - if last == old_id { - self.last = Some(new_id); - } - } - } - - fn mark_dirty( - &self, - gid: RealNodeId, - mask: NodeMask, - dirty_nodes: &mut Vec<(RealNodeId, NodeMask)>, - ) { - dirty_nodes.push((gid, mask)); - } - /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts. pub fn update_state( &mut self, @@ -405,140 +343,6 @@ impl RealDom { S::update(&nodes_updated, &mut state_tree, &node_tree, &ctx) } - /// Link a child and parent together - fn link_child(&mut self, child_id: RealNodeId, parent_id: RealNodeId) -> Option<()> { - let parent = &mut self[parent_id]; - parent.add_child(child_id); - let parent_height = parent.node_data.height; - self[child_id].set_parent(parent_id); - self.set_height(child_id, parent_height + 1); - - Some(()) - } - - /// Link a child and parent together with the child inserted before a marker - fn link_child_before( - &mut self, - child_id: RealNodeId, - parent_id: RealNodeId, - marker: RealNodeId, - ) -> Option<()> { - let parent = &mut self[parent_id]; - if let NodeType::Element { children, .. } = &mut parent.node_data.node_type { - let index = children.iter().position(|a| *a == marker)?; - children.insert(index, child_id); - } - let parent_height = parent.node_data.height; - self[child_id].set_parent(parent_id); - self.set_height(child_id, parent_height + 1); - - Some(()) - } - - /// Link a child and parent together with the child inserted after a marker - fn link_child_after( - &mut self, - child_id: RealNodeId, - parent_id: RealNodeId, - marker: RealNodeId, - ) -> Option<()> { - let parent = &mut self[parent_id]; - if let NodeType::Element { children, .. } = &mut parent.node_data.node_type { - let index = children.iter().position(|a| *a == marker)?; - children.insert(index + 1, child_id); - } - let parent_height = parent.node_data.height; - self[child_id].set_parent(parent_id); - self.set_height(child_id, parent_height + 1); - - Some(()) - } - - /// Recursively increase the height of a node and its children - fn set_height(&mut self, id: RealNodeId, height: u16) { - let node = &mut self[id]; - node.node_data.height = height; - if let NodeType::Element { children, .. } = &node.node_data.node_type { - for c in children.clone() { - self.set_height(c, height + 1); - } - } - } - - // remove a node and it's children from the dom. - fn remove(&mut self, id: RealNodeId) -> Option> { - // We do not need to remove the node from the parent's children list for children. - fn inner(dom: &mut RealDom, id: RealNodeId) -> Option> { - let mut node = match id { - RealNodeId::ElementId(id) => *dom.nodes[id.0].take()?, - RealNodeId::UnaccessableId(id) => *dom.internal_nodes.remove(id), - }; - if let NodeType::Element { children, .. } = &mut node.node_data.node_type { - for c in children { - inner(dom, *c); - } - } - Some(node) - } - let mut node = match id { - RealNodeId::ElementId(id) => *self.nodes[id.0].take()?, - RealNodeId::UnaccessableId(id) => *self.internal_nodes.remove(id), - }; - if let Some(parent) = node.node_data.parent { - let parent = &mut self[parent]; - parent.remove_child(id); - } - if let NodeType::Element { children, .. } = &mut node.node_data.node_type { - for c in children { - inner(self, *c)?; - } - } - Some(node) - } - - fn resize_to(&mut self, id: usize) { - let current_len = self.nodes.len(); - if current_len - 1 < id { - self.nodes.extend((0..1 + id - current_len).map(|_| None)); - } - } - - fn insert( - &mut self, - mut node: Node, - id: Option, - nodes_updated: &mut Vec<(RealNodeId, NodeMask)>, - ) -> RealNodeId { - match id { - Some(id) => { - let id = id as usize; - self.resize_to(id); - let real_id = RealNodeId::ElementId(ElementId(id)); - node.node_data.id = Some(real_id); - // move the old node to a new unaccessable id - if let Some(mut old) = self.nodes[id].take() { - let old_id = old.node_data.id.unwrap(); - let entry = self.internal_nodes.vacant_entry(); - let id = entry.key(); - let new_id = RealNodeId::UnaccessableId(id); - old.node_data.id = Some(real_id); - entry.insert(old); - self.update_id(old_id, new_id, nodes_updated); - } - self.nodes[id] = Some(Box::new(node)); - real_id - } - None => { - let entry = self.internal_nodes.vacant_entry(); - let id = entry.key(); - let real_id = RealNodeId::UnaccessableId(id); - node.node_data.id = Some(real_id); - entry.insert(Box::new(node)); - real_id - } - } - } - /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up. /// This can be useful to avoid creating duplicate events. pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node> { @@ -551,162 +355,37 @@ impl RealDom { } } - /// Check if the dom contains a node and its children. - pub fn contains_node(&self, node: &VNode) -> bool { - match node { - VNode::Component(_) => { - todo!() - } - VNode::Element(e) => { - if let Some(id) = e.id.get() { - let dom_node = &self[id]; - match &dom_node.node_data.node_type { - NodeType::Element { - tag, - namespace, - children, - attributes, - listeners, - } => { - tag == e.tag - && namespace == &e.namespace - && children - .iter() - .zip( - e.children - .iter() - .map(|c| RealNodeId::ElementId(c.mounted_id())), - ) - .all(|(c1, c2)| *c1 == c2) - && e.children.iter().all(|c| { - self.contains_node(c) - && self[RealNodeId::ElementId(c.mounted_id())] - .node_data - .parent - == e.id.get().map(RealNodeId::ElementId) - }) - && attributes - .iter() - .zip(e.attributes.iter()) - .all(|((disc, val), b)| *disc == b.attribute && *val == b.value) - && listeners - .iter() - .zip(e.listeners.iter()) - .all(|(a, b)| *a == b.event) - } - _ => false, - } - } else { - true - } - } - VNode::Fragment(f) => f.children.iter().all(|c| self.contains_node(c)), - VNode::Placeholder(_) => true, - VNode::Text(t) => { - if let Some(id) = t.id.get() { - let dom_node = &self[RealNodeId::ElementId(id)]; - match &dom_node.node_data.node_type { - NodeType::Text { text } => t.text == text, - _ => false, - } - } else { - true - } - } - VNode::TemplateRef(_) => todo!(), - } - } - /// Return the number of nodes in the dom. pub fn size(&self) -> usize { // The dom has a root node, ignore it. - self.nodes.iter().filter(|n| n.is_some()).count() - 1 + self.tree.size() - 1 } /// Returns the id of the root node. - pub fn root_id(&self) -> usize { - self.root + pub fn root_id(&self) -> NodeId { + self.tree.root() } - /// Call a function for each node in the dom, depth first. - pub fn traverse_depth_first(&self, mut f: impl FnMut(&Node)) { - fn inner(dom: &RealDom, id: RealNodeId, f: &mut impl FnMut(&Node)) { - let node = &dom[id]; - f(node); - if let NodeType::Element { children, .. } = &node.node_data.node_type { - for c in children { - inner(dom, *c, f); - } - } - } - if let NodeType::Element { children, .. } = &self - [RealNodeId::ElementId(ElementId(self.root))] - .node_data - .node_type - { - for c in children { - inner(self, *c, &mut f); - } - } - } - - /// Call a function for each node in the dom, depth first. - pub fn traverse_depth_first_mut(&mut self, mut f: impl FnMut(&mut Node)) { - fn inner(dom: &mut RealDom, id: RealNodeId, f: &mut impl FnMut(&mut Node)) { - let node = &mut dom[id]; - f(node); - if let NodeType::Element { children, .. } = &mut node.node_data.node_type { - for c in children.clone() { - inner(dom, c, f); - } - } - } - let root = self.root; - if let NodeType::Element { children, .. } = &mut self - [RealNodeId::ElementId(ElementId(root))] - .node_data - .node_type - { - for c in children.clone() { - inner(self, c, &mut f); - } - } - } - - pub fn split( - &mut self, - ) -> ( - impl Traversable + '_, - impl Traversable + '_, - ) { + pub fn split(&mut self) -> (impl TreeView + '_, impl TreeView + '_) { let raw = self as *mut Self; - // this is safe beacuse the traversable trait does not allow mutation of the position of elements, and within elements the access is disjoint. + // this is safe beacuse the treeview trait does not allow mutation of the position of elements, and within elements the access is disjoint. ( unsafe { &mut *raw }.map(|n| &n.state, |n| &mut n.state), unsafe { &mut *raw }.map(|n| &n.node_data, |n| &mut n.node_data), ) } - /// Recurively clones a node and marks it and it's children as dirty. - fn clone_node_into( - &mut self, - id: RealNodeId, - nodes_updated: &mut Vec<(RealNodeId, NodeMask)>, - new_id: Option, - ) -> RealNodeId { - let new_id = self.insert(self[id].clone(), new_id, nodes_updated); - nodes_updated.push((new_id, NodeMask::ALL)); - // this is safe because no node has itself as a child. - let unbounded_self = unsafe { &mut *(self as *mut Self) }; - let mut node = &mut self[new_id]; - node.node_data.height = 0; - if let NodeType::Element { children, .. } = &mut node.node_data.node_type { - for c in children { - let child_id = unbounded_self.clone_node_into(*c, nodes_updated, None); - *c = child_id; - let parent_height = node.node_data.height; - unbounded_self[child_id].set_parent(new_id); - unbounded_self.set_height(child_id, parent_height + 1); + fn clone_node(&mut self, node_id: NodeId) -> RealNodeId { + let node = self.tree.get(node_id).unwrap(); + let new_node = node.clone(); + let new_id = self.create_node(new_node); + let self_ptr = self as *mut Self; + for child in self.tree.children_ids(node_id).unwrap() { + unsafe { + // this is safe because no node has itself as a child + let self_mut = &mut *self_ptr; + let child_id = self_mut.clone_node(*child); + self_mut.tree.add_child(new_id, child_id); } } new_id @@ -716,8 +395,8 @@ impl RealDom { impl Index for RealDom { type Output = Node; - fn index(&self, idx: ElementId) -> &Self::Output { - self.get(RealNodeId::ElementId(idx)).unwrap() + fn index(&self, id: ElementId) -> &Self::Output { + self.tree.get(self.element_to_node_id(id)).unwrap() } } @@ -725,62 +404,32 @@ impl Index for RealDom { type Output = Node; fn index(&self, idx: RealNodeId) -> &Self::Output { - self.get(idx).unwrap() + self.tree.get(idx).unwrap() } } impl IndexMut for RealDom { - fn index_mut(&mut self, idx: ElementId) -> &mut Self::Output { - self.get_mut(RealNodeId::ElementId(idx)).unwrap() + fn index_mut(&mut self, id: ElementId) -> &mut Self::Output { + self.tree.get_mut(self.element_to_node_id(id)).unwrap() } } impl IndexMut for RealDom { fn index_mut(&mut self, idx: RealNodeId) -> &mut Self::Output { - self.get_mut(idx).unwrap() + self.tree.get_mut(idx).unwrap() } } -impl Traversable for RealDom { - type Id = RealNodeId; - type Node = Node; +impl Deref for RealDom { + type Target = Tree>; - fn height(&self, id: Self::Id) -> Option { - let node = ::get(self, id); - Some(node?.node_data.height) + fn deref(&self) -> &Self::Target { + &self.tree } +} - fn get(&self, id: Self::Id) -> Option<&Self::Node> { - match id { - RealNodeId::ElementId(id) => { - self.nodes.get(id.0).and_then(|b| b.as_ref().map(|b| &**b)) - } - RealNodeId::UnaccessableId(id) => self.internal_nodes.get(id).map(|b| &**b), - } - } - - fn get_mut(&mut self, id: Self::Id) -> Option<&mut Self::Node> { - match id { - RealNodeId::ElementId(id) => self - .nodes - .get_mut(id.0) - .and_then(|b| b.as_mut().map(|b| &mut **b)), - RealNodeId::UnaccessableId(id) => self.internal_nodes.get_mut(id).map(|b| &mut **b), - } - } - - fn children(&self, id: Self::Id) -> &[Self::Id] { - if let Some(node) = ::get(self, id) { - match &node.node_data.node_type { - NodeType::Element { children, .. } => children, - _ => &[], - } - } else { - &[] - } - } - - fn parent(&self, id: Self::Id) -> Option { - self.get(id).and_then(|n| n.node_data.parent) +impl DerefMut for RealDom { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.tree } } diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index d858a2a78..9657a1c21 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -1,17 +1,18 @@ -use std::{cmp::Ordering, fmt::Debug}; +use std::cmp::Ordering; use crate::node::NodeData; use crate::node_ref::{NodeMask, NodeView}; +use crate::tree::TreeView; use crate::RealNodeId; use anymap::AnyMap; use rustc_hash::FxHashSet; /// Join two sorted iterators -pub(crate) fn union_ordered_iter( - s_iter: impl Iterator, - o_iter: impl Iterator, +pub(crate) fn union_ordered_iter<'a>( + s_iter: impl Iterator, + o_iter: impl Iterator, new_len_guess: usize, -) -> Vec { +) -> Vec { let mut s_peekable = s_iter.peekable(); let mut o_peekable = o_iter.peekable(); let mut v = Vec::with_capacity(new_len_guess); @@ -22,7 +23,7 @@ pub(crate) fn union_ordered_iter( break; } Ordering::Less => { - v.push(o_peekable.next().unwrap()); + v.push(o_peekable.next().unwrap().to_string()); } Ordering::Equal => { o_peekable.next(); @@ -30,10 +31,10 @@ pub(crate) fn union_ordered_iter( } } } - v.push(s_peekable.next().unwrap()); + v.push(s_peekable.next().unwrap().to_string()); } for o_i in o_peekable { - v.push(o_i); + v.push(o_i.to_string()); } for w in v.windows(2) { debug_assert!(w[1] > w[0]); @@ -207,11 +208,7 @@ pub trait NodeDepState { /// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom]. pub trait State: Default + Clone { #[doc(hidden)] - fn update< - 'a, - T: Traversable, - T2: Traversable, - >( + fn update<'a, T: TreeView, T2: TreeView>( dirty: &[(RealNodeId, NodeMask)], state_tree: &'a mut T, rdom: &'a T2, diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 76769ee54..6931d2e23 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -7,7 +7,7 @@ use std::collections::VecDeque; use std::marker::PhantomData; use std::sync::Arc; -#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)] +#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)] pub struct NodeId(pub usize); #[derive(PartialEq, Eq, Clone, Debug)] @@ -23,6 +23,31 @@ pub struct Tree { root: NodeId, } +impl Tree { + fn try_remove(&mut self, id: NodeId) -> Option> { + self.nodes.try_remove(id.0).map(|node| { + if let Some(parent) = node.parent { + self.nodes + .get_mut(parent.0) + .unwrap() + .children + .retain(|child| child != &id); + } + for child in &node.children { + self.remove_recursive(*child); + } + node + }) + } + + fn remove_recursive(&mut self, node: NodeId) { + let node = self.nodes.remove(node.0); + for child in node.children { + self.remove_recursive(child); + } + } +} + pub trait TreeView: Sized { type Iterator<'a>: Iterator where @@ -152,15 +177,19 @@ pub trait TreeView: Sized { pub trait TreeLike: TreeView { fn new(root: T) -> Self; - fn add_child(&mut self, parent: NodeId, value: T) -> NodeId; + fn create_node(&mut self, value: T) -> NodeId; + + fn add_child(&mut self, parent: NodeId, child: NodeId); fn remove(&mut self, id: NodeId) -> Option; - fn replace(&mut self, id: NodeId, value: T); + fn remove_all_children(&mut self, id: NodeId) -> Vec; - fn insert_before(&mut self, id: NodeId, value: T) -> NodeId; + fn replace(&mut self, old: NodeId, new: NodeId); - fn insert_after(&mut self, id: NodeId, value: T) -> NodeId; + fn insert_before(&mut self, id: NodeId, new: NodeId); + + fn insert_after(&mut self, id: NodeId, new: NodeId); } pub struct ChildNodeIterator<'a, T, Tr: TreeView> { @@ -304,78 +333,75 @@ impl TreeLike for Tree { Self { nodes, root } } - fn add_child(&mut self, parent: NodeId, value: T) -> NodeId { - let node = Node { + fn create_node(&mut self, value: T) -> NodeId { + NodeId(self.nodes.insert(Node { value, - parent: Some(parent), + parent: None, children: Vec::new(), - }; - let id = self.nodes.insert(node); - self.nodes - .get_mut(parent.0) - .unwrap() - .children - .push(NodeId(id)); - NodeId(id) + })) + } + + fn add_child(&mut self, parent: NodeId, new: NodeId) { + self.nodes.get_mut(parent.0).unwrap().children.push(new); + self.nodes.get_mut(new.0).unwrap().parent = Some(parent); } fn remove(&mut self, id: NodeId) -> Option { - self.nodes.try_remove(id.0).map(|node| { - if let Some(parent) = node.parent { - self.nodes - .get_mut(parent.0) - .unwrap() - .children - .retain(|child| child != &id); + self.try_remove(id).map(|node| node.value) + } + + fn remove_all_children(&mut self, id: NodeId) -> Vec { + let mut children = Vec::new(); + let self_mut = self as *mut Self; + for child in self.children_ids(id).unwrap() { + unsafe { + // Safety: No node has itself as a child + children.push((*self_mut).remove(*child).unwrap()); } - node.value - }) + } + children } - fn replace(&mut self, id: NodeId, value: T) { + fn replace(&mut self, old_id: NodeId, new_id: NodeId) { + // remove the old node let old = self - .nodes - .get_mut(id.0) + .try_remove(old_id) .expect("tried to replace a node that doesn't exist"); - old.value = value; + // update the parent's link to the child + if let Some(parent_id) = old.parent { + let parent = self.nodes.get_mut(parent_id.0).unwrap(); + for id in &mut parent.children { + if *id == old_id { + *id = new_id; + } + } + } } - fn insert_before(&mut self, id: NodeId, value: T) -> NodeId { + fn insert_before(&mut self, id: NodeId, new: NodeId) { let node = self.nodes.get(id.0).unwrap(); let parent_id = node.parent.expect("tried to insert before root"); - let new = Node { - value, - parent: Some(parent_id), - children: Vec::new(), - }; - let new_id = NodeId(self.nodes.insert(new)); + self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id); let parent = self.nodes.get_mut(parent_id.0).unwrap(); let index = parent .children .iter() .position(|child| child == &id) .unwrap(); - parent.children.insert(index, new_id); - new_id + parent.children.insert(index, new); } - fn insert_after(&mut self, id: NodeId, value: T) -> NodeId { + fn insert_after(&mut self, id: NodeId, new: NodeId) { let node = self.nodes.get(id.0).unwrap(); let parent_id = node.parent.expect("tried to insert before root"); - let new = Node { - value, - parent: Some(parent_id), - children: Vec::new(), - }; - let new_id = NodeId(self.nodes.insert(new)); + self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id); let parent = self.nodes.get_mut(parent_id.0).unwrap(); let index = parent .children .iter() .position(|child| child == &id) .unwrap(); - parent.children.insert(index + 1, new_id); - new_id + parent.children.insert(index + 1, new); } } @@ -498,7 +524,7 @@ pub struct SharedView<'a, T, Tr: TreeView> { impl<'a, T, Tr: TreeView> SharedView<'a, T, Tr> { /// Checks if a node is currently locked. Returns None if the node does not exist. pub fn check_lock(&self, id: NodeId) -> Option { - let mut locks = self.node_locks.read(); + let locks = self.node_locks.read(); locks.get(id.0).map(|lock| lock.is_locked()) } } @@ -616,7 +642,8 @@ impl<'a, T, Tr: TreeView> TreeView for SharedView<'a, T, Tr> { fn creation() { let mut tree = Tree::new(1); let parent = tree.root(); - let child = tree.add_child(parent, 0); + let child = tree.create_node(0); + tree.add_child(parent, child); println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 2); @@ -631,9 +658,12 @@ fn creation() { fn insertion() { let mut tree = Tree::new(0); let parent = tree.root(); - let child = tree.add_child(parent, 2); - let before = tree.insert_before(child, 1); - let after = tree.insert_after(child, 3); + let child = tree.create_node(2); + tree.add_child(parent, child); + let before = tree.create_node(1); + tree.insert_before(child, before); + let after = tree.create_node(3); + tree.insert_after(child, after); println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 4); @@ -651,9 +681,12 @@ fn insertion() { fn deletion() { let mut tree = Tree::new(0); let parent = tree.root(); - let child = tree.add_child(parent, 2); - let before = tree.insert_before(child, 1); - let after = tree.insert_after(child, 3); + let child = tree.create_node(2); + tree.add_child(parent, child); + let before = tree.create_node(1); + tree.insert_before(child, before); + let after = tree.create_node(3); + tree.insert_after(child, after); println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 4); @@ -702,7 +735,8 @@ fn shared_view() { use std::thread; let mut tree = Tree::new(1); let parent = tree.root(); - let child = tree.add_child(parent, 0); + let child = tree.create_node(0); + tree.add_child(parent, child); let shared = SharedView::new(&mut tree); @@ -737,7 +771,8 @@ fn map() { } let mut tree = Tree::new(Value::new(1)); let parent = tree.root(); - let child = tree.add_child(parent, Value::new(0)); + let child = tree.create_node(Value::new(0)); + tree.add_child(parent, child); let mut mapped = tree.map(|x| &x.value, |x| &mut x.value); @@ -752,10 +787,14 @@ fn map() { fn traverse_depth_first() { let mut tree = Tree::new(0); let parent = tree.root(); - let child1 = tree.add_child(parent, 1); - tree.add_child(child1, 2); - let child2 = tree.add_child(parent, 3); - tree.add_child(child2, 4); + let child1 = tree.create_node(1); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(2); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(3); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(4); + tree.add_child(child2, grandchild2); let mut node_count = 0; tree.traverse_depth_first(move |node| { @@ -768,10 +807,14 @@ fn traverse_depth_first() { fn traverse_breadth_first() { let mut tree = Tree::new(0); let parent = tree.root(); - let child1 = tree.add_child(parent, 1); - tree.add_child(child1, 3); - let child2 = tree.add_child(parent, 2); - tree.add_child(child2, 4); + let child1 = tree.create_node(1); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(3); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(2); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(4); + tree.add_child(child2, grandchild2); let mut node_count = 0; tree.traverse_breadth_first(move |node| { From f9059c3f30defe5a65f16ebf9823650a4f0ad706 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 24 Nov 2022 17:41:07 -0600 Subject: [PATCH 03/43] handle height --- packages/native-core/src/node.rs | 2 - packages/native-core/src/real_dom.rs | 118 ++++++++++++++---------- packages/native-core/src/state.rs | 82 +++++++++++----- packages/native-core/src/tree.rs | 29 +++--- packages/native-core/src/worker_pool.rs | 1 + 5 files changed, 146 insertions(+), 86 deletions(-) create mode 100644 packages/native-core/src/worker_pool.rs diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index 09dd03e71..f73e0dc07 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -21,7 +21,6 @@ pub struct NodeData { pub node_type: NodeType, /// The number of parents before the root node. The root node has height 1. pub height: u16, - height_dirty: bool, } /// A type of node with data specific to the node type. The types are a subset of the [VNode] types. @@ -48,7 +47,6 @@ impl Node { node_type, height: 0, node_id: NodeId(0), - height_dirty: true, }, } } diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index cbeb80f2b..164fcd3e1 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,7 +1,7 @@ use anymap::AnyMap; use dioxus_core::{ElementId, Mutations}; use rustc_hash::{FxHashMap, FxHashSet}; -use std::ops::{Deref, DerefMut, Index, IndexMut}; +use std::ops::{Index, IndexMut}; use crate::node::{Node, NodeData, NodeType, OwnedAttributeDiscription}; use crate::node_ref::{AttributeMask, NodeMask}; @@ -9,6 +9,18 @@ use crate::state::State; use crate::tree::{NodeId, Tree, TreeLike, TreeView}; use crate::RealNodeId; +fn mark_dirty( + node_id: NodeId, + mask: NodeMask, + nodes_updated: &mut FxHashMap, +) { + if let Some(node) = nodes_updated.get_mut(&node_id) { + *node = node.union(&mask); + } else { + nodes_updated.insert(node_id, mask); + } +} + /// A Dom that can sync with the VirtualDom mutations intended for use in lazy renderers. /// The render state passes from parent to children and or accumulates state from children to parents. /// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes. @@ -79,12 +91,31 @@ impl RealDom { node_id } + fn add_child(&mut self, node_id: RealNodeId, child_id: RealNodeId) { + self.tree.add_child(node_id, child_id); + self.resolve_height(child_id); + } + + fn resolve_height(&mut self, node_id: RealNodeId) { + if let Some((node, parent)) = self.tree.node_parent_mut(node_id) { + let height = parent.node_data.height; + node.node_data.height = height + 1; + unsafe { + let self_mut = self as *mut Self; + // Safety: No node will have itself as a child + for child in self.tree.children_ids(node_id).unwrap() { + (*self_mut).resolve_height(*child); + } + } + } + } + /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. - pub fn apply_mutations<'a>( + pub fn apply_mutations( &mut self, - mutations_vec: Vec>, - ) -> Vec<(RealNodeId, NodeMask)> { - let mut nodes_updated: Vec<(RealNodeId, NodeMask)> = Vec::new(); + mutations_vec: Vec, + ) -> FxHashMap { + let mut nodes_updated: FxHashMap = FxHashMap::default(); for mutations in mutations_vec { for e in mutations.edits { use dioxus_core::Mutation::*; @@ -93,12 +124,12 @@ impl RealDom { let data = self.stack.split_off(m); let (parent, children) = data.split_first().unwrap(); for child in children { - self.tree.add_child(*parent, *child); - nodes_updated.push((*parent, NodeMask::ALL)); + self.add_child(*parent, *child); + mark_dirty(*parent, NodeMask::ALL, &mut nodes_updated); } } AssignId { path, id } => { - self.set_element_id(self.load_child(&path), id); + self.set_element_id(self.load_child(path), id); } CreateElement { name } => { let node = Node::new(NodeType::Element { @@ -109,7 +140,7 @@ impl RealDom { }); let id = self.create_node(node); self.stack.push(id); - nodes_updated.push((id, NodeMask::ALL)); + mark_dirty(id, NodeMask::ALL, &mut nodes_updated); } CreateElementNamespace { name, namespace } => { let node = Node::new(NodeType::Element { @@ -120,20 +151,20 @@ impl RealDom { }); let id = self.create_node(node); self.stack.push(id); - nodes_updated.push((id, NodeMask::ALL)); + mark_dirty(id, NodeMask::ALL, &mut nodes_updated); } CreatePlaceholder { id } => { let node = Node::new(NodeType::Placeholder); let node_id = self.create_node(node); self.set_element_id(node_id, id); self.stack.push(node_id); - nodes_updated.push((node_id, NodeMask::ALL)); + mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated); } CreateStaticPlaceholder => { let node = Node::new(NodeType::Placeholder); let id = self.create_node(node); self.stack.push(id); - nodes_updated.push((id, NodeMask::ALL)); + mark_dirty(id, NodeMask::ALL, &mut nodes_updated); } CreateStaticText { value } => { let node = Node::new(NodeType::Text { @@ -141,7 +172,7 @@ impl RealDom { }); let id = self.create_node(node); self.stack.push(id); - nodes_updated.push((id, NodeMask::new().with_text())); + mark_dirty(id, NodeMask::new().with_text(), &mut nodes_updated); } CreateTextNode { value, id } => { let node = Node::new(NodeType::Text { @@ -151,7 +182,7 @@ impl RealDom { let node = self.tree.get_mut(node_id).unwrap(); node.node_data.element_id = Some(id); self.stack.push(node_id); - nodes_updated.push((node_id, NodeMask::new().with_text())); + mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); } HydrateText { path, value, id } => { let node_id = self.load_child(path); @@ -160,7 +191,7 @@ impl RealDom { if let NodeType::Text { text } = &mut node.node_data.node_type { *text = value.to_string(); } - nodes_updated.push((node_id, NodeMask::new().with_text())); + mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); } LoadTemplate { name, index, id } => { let template_id = self.templates[name][index]; @@ -172,7 +203,7 @@ impl RealDom { let old_node_id = self.element_to_node_id(id); for new in new_nodes { self.tree.insert_after(old_node_id, new); - nodes_updated.push((new, NodeMask::ALL)); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } self.tree.remove(old_node_id); } @@ -181,7 +212,7 @@ impl RealDom { let old_node_id = self.load_child(path); for new in new_nodes { self.tree.insert_after(old_node_id, new); - nodes_updated.push((new, NodeMask::ALL)); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } self.tree.remove(old_node_id); } @@ -190,7 +221,7 @@ impl RealDom { let old_node_id = self.element_to_node_id(id); for new in new_nodes { self.tree.insert_after(old_node_id, new); - nodes_updated.push((new, NodeMask::ALL)); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } } InsertBefore { id, m } => { @@ -198,7 +229,7 @@ impl RealDom { let old_node_id = self.element_to_node_id(id); for new in new_nodes { self.tree.insert_before(old_node_id, new); - nodes_updated.push((new, NodeMask::ALL)); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } } SaveTemplate { name, m } => { @@ -223,10 +254,11 @@ impl RealDom { }, crate::node::OwnedAttributeValue::Text(value.to_string()), ); - nodes_updated.push(( + mark_dirty( node_id, NodeMask::new_with_attrs(AttributeMask::single(name)), - )); + &mut nodes_updated, + ); } } SetStaticAttribute { name, value, ns } => { @@ -242,10 +274,11 @@ impl RealDom { }, crate::node::OwnedAttributeValue::Text(value.to_string()), ); - nodes_updated.push(( + mark_dirty( *node_id, NodeMask::new_with_attrs(AttributeMask::single(name)), - )); + &mut nodes_updated, + ); } } SetBoolAttribute { name, value, id } => { @@ -261,10 +294,11 @@ impl RealDom { }, crate::node::OwnedAttributeValue::Bool(value), ); - nodes_updated.push(( + mark_dirty( node_id, NodeMask::new_with_attrs(AttributeMask::single(name)), - )); + &mut nodes_updated, + ); } } SetInnerText { value } => { @@ -276,7 +310,7 @@ impl RealDom { text: value.to_string(), }); let text_node_id = self.create_node(text_node); - self.tree.add_child(node_id, text_node_id); + self.add_child(node_id, text_node_id); } } SetText { value, id } => { @@ -285,7 +319,7 @@ impl RealDom { if let NodeType::Text { text } = &mut node.node_data.node_type { *text = value.to_string(); } - nodes_updated.push((node_id, NodeMask::new().with_text())); + mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); } NewEventListener { event_name, @@ -321,14 +355,14 @@ impl RealDom { } Remove { id } => { let node_id = self.element_to_node_id(id); - self.remove(node_id); + self.tree.remove(node_id); } } } } // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - nodes_updated.retain(|n| self.tree.get(n.0).is_some()); + nodes_updated.retain(|k, _| self.tree.get(*k).is_some()); nodes_updated } @@ -336,7 +370,7 @@ impl RealDom { /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts. pub fn update_state( &mut self, - nodes_updated: Vec<(RealNodeId, NodeMask)>, + nodes_updated: FxHashMap, ctx: AnyMap, ) -> FxHashSet { let (mut state_tree, node_tree) = self.split(); @@ -370,8 +404,12 @@ impl RealDom { let raw = self as *mut Self; // this is safe beacuse the treeview trait does not allow mutation of the position of elements, and within elements the access is disjoint. ( - unsafe { &mut *raw }.map(|n| &n.state, |n| &mut n.state), - unsafe { &mut *raw }.map(|n| &n.node_data, |n| &mut n.node_data), + unsafe { &mut *raw } + .tree + .map(|n| &n.state, |n| &mut n.state), + unsafe { &mut *raw } + .tree + .map(|n| &n.node_data, |n| &mut n.node_data), ) } @@ -385,7 +423,7 @@ impl RealDom { // this is safe because no node has itself as a child let self_mut = &mut *self_ptr; let child_id = self_mut.clone_node(*child); - self_mut.tree.add_child(new_id, child_id); + self_mut.add_child(new_id, child_id); } } new_id @@ -419,17 +457,3 @@ impl IndexMut for RealDom { self.tree.get_mut(idx).unwrap() } } - -impl Deref for RealDom { - type Target = Tree>; - - fn deref(&self) -> &Self::Target { - &self.tree - } -} - -impl DerefMut for RealDom { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.tree - } -} diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index 9657a1c21..18d405ed7 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -5,7 +5,7 @@ use crate::node_ref::{NodeMask, NodeView}; use crate::tree::TreeView; use crate::RealNodeId; use anymap::AnyMap; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; /// Join two sorted iterators pub(crate) fn union_ordered_iter<'a>( @@ -81,15 +81,15 @@ pub trait ChildDepState { /// The context is passed to the [ChildDepState::reduce] when it is resolved. type Ctx; /// A state from each child node that this node depends on. Typically this is Self, but it could be any state that is within the state tree. - /// This must be either a [ChildDepState], or [NodeDepState]. - type DepState; + /// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState] or [NodeDepState]. + type DepState: ElementBorrowable; /// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated. const NODE_MASK: NodeMask = NodeMask::NONE; /// Resolve the state current node's state from the state of the children, the state of the node, and some external context. fn reduce<'a>( &mut self, node: NodeView, - children: impl Iterator, + children: impl Iterator::ElementBorrowed<'a>>, ctx: &Self::Ctx, ) -> bool where @@ -143,15 +143,15 @@ pub trait ParentDepState { /// The context is passed to the [ParentDepState::reduce] when it is resolved. type Ctx; /// A state from from the parent node that this node depends on. Typically this is Self, but it could be any state that is within the state tree. - /// This must be either a [ParentDepState] or [NodeDepState] - type DepState; + /// Depstate must be a tuple containing any number of borrowed elements that are either [ParentDepState] or [NodeDepState]. + type DepState: ElementBorrowable; /// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated. const NODE_MASK: NodeMask = NodeMask::NONE; /// Resolve the state current node's state from the state of the parent node, the state of the node, and some external context. fn reduce<'a>( &mut self, node: NodeView, - parent: Option<&'a Self::DepState>, + parent: Option<::ElementBorrowed<'a>>, ctx: &Self::Ctx, ) -> bool; } @@ -193,23 +193,27 @@ pub trait ParentDepState { /// } /// } /// ``` -/// The generic argument (Depstate) must be a tuple containing any number of borrowed elements that are either a [ChildDepState], [ParentDepState] or [NodeDepState]. -// Todo: once GATs land we can model multable dependencies better - -pub trait NodeDepState { +pub trait NodeDepState { + /// Depstate must be a tuple containing any number of borrowed elements that are either [ChildDepState], [ParentDepState] or [NodeDepState]. + type DepState: ElementBorrowable; /// The state passed to [NodeDepState::reduce] when it is resolved. type Ctx; /// The part of a node that this state cares about. This is used to determine if the state should be updated when a node is updated. const NODE_MASK: NodeMask = NodeMask::NONE; /// Resolve the state current node's state from the state of the sibling states, the state of the node, and some external context. - fn reduce(&mut self, node: NodeView, siblings: DepState, ctx: &Self::Ctx) -> bool; + fn reduce<'a>( + &mut self, + node: NodeView, + siblings: ::ElementBorrowed<'a>, + ctx: &Self::Ctx, + ) -> bool; } /// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom]. pub trait State: Default + Clone { #[doc(hidden)] fn update<'a, T: TreeView, T2: TreeView>( - dirty: &[(RealNodeId, NodeMask)], + dirty: &FxHashMap, state_tree: &'a mut T, rdom: &'a T2, ctx: &AnyMap, @@ -219,12 +223,7 @@ pub trait State: Default + Clone { impl ChildDepState for () { type Ctx = (); type DepState = (); - fn reduce<'a>( - &mut self, - _: NodeView, - _: impl Iterator, - _: &Self::Ctx, - ) -> bool + fn reduce<'a>(&mut self, _: NodeView, _: impl Iterator, _: &Self::Ctx) -> bool where Self::DepState: 'a, { @@ -235,14 +234,55 @@ impl ChildDepState for () { impl ParentDepState for () { type Ctx = (); type DepState = (); - fn reduce<'a>(&mut self, _: NodeView, _: Option<&'a Self::DepState>, _: &Self::Ctx) -> bool { + fn reduce<'a>(&mut self, _: NodeView, _: Option<()>, _: &Self::Ctx) -> bool { false } } -impl NodeDepState<()> for () { +impl NodeDepState for () { + type DepState = (); type Ctx = (); fn reduce(&mut self, _: NodeView, _sibling: (), _: &Self::Ctx) -> bool { false } } + +pub trait ElementBorrowable { + type ElementBorrowed<'a> + where + Self: 'a; + + fn borrow_elements(&self) -> Self::ElementBorrowed<'_>; +} + +macro_rules! impl_element_borrowable { + ($($t:ident),*) => { + impl< $($t),* > ElementBorrowable for ($($t,)*) { + type ElementBorrowed<'a> = ($(&'a $t,)*) where Self: 'a; + + #[allow(clippy::unused_unit, non_snake_case)] + fn borrow_elements<'a>(&'a self) -> Self::ElementBorrowed<'a> { + let ($($t,)*) = self; + ($(&$t,)*) + } + } + }; +} + +impl_element_borrowable!(); +impl_element_borrowable!(A); +impl_element_borrowable!(A, B); +impl_element_borrowable!(A, B, C); +impl_element_borrowable!(A, B, C, D); +impl_element_borrowable!(A, B, C, D, E); +impl_element_borrowable!(A, B, C, D, E, F); +impl_element_borrowable!(A, B, C, D, E, F, G); +impl_element_borrowable!(A, B, C, D, E, F, G, H); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_element_borrowable!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 6931d2e23..04138b855 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -72,14 +72,11 @@ pub trait TreeView: Sized { unsafe { self.get_mut(id).unwrap_unchecked() } } - fn children<'a>(&'a self, id: NodeId) -> Option>; + fn children(&self, id: NodeId) -> Option>; - fn children_mut<'a>(&'a mut self, id: NodeId) -> Option>; + fn children_mut(&mut self, id: NodeId) -> Option>; - fn parent_child_mut<'a>( - &'a mut self, - id: NodeId, - ) -> Option<(&'a mut T, Self::IteratorMut<'a>)> { + fn parent_child_mut(&mut self, id: NodeId) -> Option<(&mut T, Self::IteratorMut<'_>)> { let mut_ptr: *mut Self = self; unsafe { // Safety: No node has itself as a child. @@ -91,7 +88,7 @@ pub trait TreeView: Sized { } } - fn children_ids<'a>(&'a self, id: NodeId) -> Option<&'a [NodeId]>; + fn children_ids(&self, id: NodeId) -> Option<&[NodeId]>; fn parent(&self, id: NodeId) -> Option<&T>; @@ -257,7 +254,7 @@ impl TreeView for Tree { self.nodes.get_mut(id.0).map(|node| &mut node.value) } - fn children<'a>(&'a self, id: NodeId) -> Option> { + fn children(&self, id: NodeId) -> Option> { self.children_ids(id).map(|children_ids| ChildNodeIterator { tree: self, children_ids, @@ -266,7 +263,7 @@ impl TreeView for Tree { }) } - fn children_mut<'a>(&'a mut self, id: NodeId) -> Option> { + fn children_mut(&mut self, id: NodeId) -> Option> { let raw_ptr = self as *mut Self; unsafe { // Safety: No node will appear as a child twice @@ -280,7 +277,7 @@ impl TreeView for Tree { } } - fn children_ids<'a>(&'a self, id: NodeId) -> Option<&'a [NodeId]> { + fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> { self.nodes.get(id.0).map(|node| node.children.as_slice()) } @@ -462,7 +459,7 @@ where self.tree.get_mut(id).map(|node| (self.map_mut)(node)) } - fn children<'b>(&'b self, id: NodeId) -> Option> { + fn children(&self, id: NodeId) -> Option> { self.children_ids(id).map(|children_ids| ChildNodeIterator { tree: self, children_ids, @@ -471,7 +468,7 @@ where }) } - fn children_mut<'b>(&'b mut self, id: NodeId) -> Option> { + fn children_mut(&mut self, id: NodeId) -> Option> { let raw_ptr = self as *mut Self; unsafe { // Safety: No node can be a child twice. @@ -485,7 +482,7 @@ where } } - fn children_ids<'b>(&'b self, id: NodeId) -> Option<&'b [NodeId]> { + fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> { self.tree.children_ids(id) } @@ -609,15 +606,15 @@ impl<'a, T, Tr: TreeView> TreeView for SharedView<'a, T, Tr> { self.with_node(id, |t| t.get_mut(id)) } - fn children<'b>(&'b self, id: NodeId) -> Option> { + fn children(&self, id: NodeId) -> Option> { self.with_node(id, |t| t.children(id)) } - fn children_mut<'b>(&'b mut self, id: NodeId) -> Option> { + fn children_mut(&mut self, id: NodeId) -> Option> { self.with_node(id, |t| t.children_mut(id)) } - fn children_ids<'b>(&'b self, id: NodeId) -> Option<&'b [NodeId]> { + fn children_ids(&self, id: NodeId) -> Option<&[NodeId]> { self.with_node(id, |t| t.children_ids(id)) } diff --git a/packages/native-core/src/worker_pool.rs b/packages/native-core/src/worker_pool.rs new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/packages/native-core/src/worker_pool.rs @@ -0,0 +1 @@ + From 5f4b08496cda242cbc20ea91570e937bd411f9a3 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 24 Nov 2022 17:41:12 -0600 Subject: [PATCH 04/43] update to taffy 0.2 --- packages/native-core/Cargo.toml | 3 +- packages/native-core/src/layout_attributes.rs | 42 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 09c08bc61..496faa893 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -15,12 +15,13 @@ dioxus-core = { path = "../core", version = "^0.2.1" } dioxus-html = { path = "../html", version = "^0.2.1" } dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" } -taffy = "0.1.0" +taffy = "0.2.0" smallvec = "1.6" rustc-hash = "1.1.0" anymap = "0.12.1" slab = "0.4" parking_lot = "0.12.1" +crossbeam-deque = "0.8.2" [dev-dependencies] rand = "0.8.5" \ No newline at end of file diff --git a/packages/native-core/src/layout_attributes.rs b/packages/native-core/src/layout_attributes.rs index da23e937f..b59177e4e 100644 --- a/packages/native-core/src/layout_attributes.rs +++ b/packages/native-core/src/layout_attributes.rs @@ -343,14 +343,14 @@ fn apply_border(name: &str, value: &str, style: &mut Style) { "border-left" => {} "border-left-color" => {} "border-left-style" => { - if style.border.start == Dimension::default() { + if style.border.left == Dimension::default() { let v = Dimension::Points(1.0); - style.border.start = v; + style.border.left = v; } } "border-left-width" => { if let Some(v) = parse_value(value) { - style.border.start = v; + style.border.left = v; } } "border-radius" => {} @@ -358,11 +358,11 @@ fn apply_border(name: &str, value: &str, style: &mut Style) { "border-right-color" => {} "border-right-style" => { let v = Dimension::Points(1.0); - style.border.end = v; + style.border.right = v; } "border-right-width" => { if let Some(v) = parse_value(value) { - style.border.end = v; + style.border.right = v; } } "border-spacing" => {} @@ -375,13 +375,13 @@ fn apply_border(name: &str, value: &str, style: &mut Style) { let v = Dimension::Points(1.0); style.border.bottom = v; } - if style.border.start == Dimension::default() { + if style.border.left == Dimension::default() { let v = Dimension::Points(1.0); - style.border.start = v; + style.border.left = v; } - if style.border.end == Dimension::default() { + if style.border.right == Dimension::default() { let v = Dimension::Points(1.0); - style.border.end = v; + style.border.right = v; } } "border-top" => {} @@ -404,8 +404,8 @@ fn apply_border(name: &str, value: &str, style: &mut Style) { if values.len() == 1 { if let Some(dim) = parse_value(values[0]) { style.border = Rect { - start: dim, - end: dim, + right: dim, + left: dim, top: dim, bottom: dim, }; @@ -414,8 +414,8 @@ fn apply_border(name: &str, value: &str, style: &mut Style) { let border_widths = [ &mut style.border.top, &mut style.border.bottom, - &mut style.border.start, - &mut style.border.end, + &mut style.border.left, + &mut style.border.right, ]; for (v, width) in values.into_iter().zip(border_widths) { if let Some(w) = parse_value(v) { @@ -512,12 +512,12 @@ fn apply_padding(name: &str, value: &str, style: &mut Style) { "padding" => { style.padding.top = v; style.padding.bottom = v; - style.padding.start = v; - style.padding.end = v; + style.padding.left = v; + style.padding.right = v; } "padding-bottom" => style.padding.bottom = v, - "padding-left" => style.padding.start = v, - "padding-right" => style.padding.end = v, + "padding-left" => style.padding.left = v, + "padding-right" => style.padding.right = v, "padding-top" => style.padding.top = v, _ => {} } @@ -578,13 +578,13 @@ fn apply_margin(name: &str, value: &str, style: &mut Style) { "margin" => { style.margin.top = dim; style.margin.bottom = dim; - style.margin.start = dim; - style.margin.end = dim; + style.margin.left = dim; + style.margin.right = dim; } "margin-top" => style.margin.top = dim, "margin-bottom" => style.margin.bottom = dim, - "margin-left" => style.margin.start = dim, - "margin-right" => style.margin.end = dim, + "margin-left" => style.margin.left = dim, + "margin-right" => style.margin.right = dim, _ => {} } } From 21edea62be2fd90026f29c9bb392cfc07f4f6458 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 24 Nov 2022 17:43:55 -0600 Subject: [PATCH 05/43] add as_varient functions to OwnedAttributeValue --- packages/native-core/src/node.rs | 37 +++++++++++++++++++++++++ packages/native-core/src/worker_pool.rs | 19 +++++++++++++ 2 files changed, 56 insertions(+) diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index f73e0dc07..fc06862f9 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -83,3 +83,40 @@ pub enum OwnedAttributeValue { Bool(bool), None, } + +impl OwnedAttributeValue { + pub fn as_text(&self) -> Option<&str> { + match self { + OwnedAttributeValue::Text(text) => Some(text), + _ => None, + } + } + + pub fn as_float(&self) -> Option { + match self { + OwnedAttributeValue::Float(float) => Some(*float), + _ => None, + } + } + + pub fn as_int(&self) -> Option { + match self { + OwnedAttributeValue::Int(int) => Some(*int), + _ => None, + } + } + + pub fn as_bool(&self) -> Option { + match self { + OwnedAttributeValue::Bool(bool) => Some(*bool), + _ => None, + } + } + + pub fn as_none(&self) -> Option<()> { + match self { + OwnedAttributeValue::None => Some(()), + _ => None, + } + } +} diff --git a/packages/native-core/src/worker_pool.rs b/packages/native-core/src/worker_pool.rs index 8b1378917..5ecf377aa 100644 --- a/packages/native-core/src/worker_pool.rs +++ b/packages/native-core/src/worker_pool.rs @@ -1 +1,20 @@ +use crossbeam_deque::{Injector, Stealer, Worker}; +use std::iter; +fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { + // Pop a task from the local queue, if not empty. + local.pop().or_else(|| { + // Otherwise, we need to look for a task elsewhere. + iter::repeat_with(|| { + // Try stealing a batch of tasks from the global queue. + global + .steal_batch_and_pop(local) + // Or try stealing a task from one of the other threads. + .or_else(|| stealers.iter().map(|s| s.steal()).collect()) + }) + // Loop while no task was stolen and any steal operation needs to be retried. + .find(|s| !s.is_retry()) + // Extract the stolen task, if there is one. + .and_then(|s| s.success()) + }) +} From e08a2186a6ecf4f8b23cf92b586e5751295eb95c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 24 Nov 2022 22:16:27 -0600 Subject: [PATCH 06/43] make get node parent mut optionally return a parent --- packages/native-core/src/real_dom.rs | 2 +- packages/native-core/src/tree.rs | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 164fcd3e1..e80510aa4 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -97,7 +97,7 @@ impl RealDom { } fn resolve_height(&mut self, node_id: RealNodeId) { - if let Some((node, parent)) = self.tree.node_parent_mut(node_id) { + if let Some((node, Some(parent))) = self.tree.node_parent_mut(node_id) { let height = parent.node_data.height; node.node_data.height = height + 1; unsafe { diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 04138b855..c77724891 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -94,13 +94,13 @@ pub trait TreeView: Sized { fn parent_mut(&mut self, id: NodeId) -> Option<&mut T>; - fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, &mut T)> { + fn node_parent_mut(&mut self, id: NodeId) -> Option<(&mut T, Option<&mut T>)> { let mut_ptr: *mut Self = self; unsafe { // Safety: No node has itself as a parent. (*mut_ptr) .get_mut(id) - .and_then(|parent| (*mut_ptr).parent_mut(id).map(|children| (parent, children))) + .map(|node| (node, (*mut_ptr).parent_mut(id).map(|parent| parent))) } } @@ -819,3 +819,20 @@ fn traverse_breadth_first() { node_count += 1; }); } + +trait UpwardPass { + fn upward_pass(&mut self, node: &mut T, parent: Option<&mut T>) -> bool; + + fn resolve_pass(&mut self, tree: &mut impl TreeView, starting_nodes: &[NodeId]) { + let mut stack = Vec::new(); + for node in starting_nodes { + stack.push(*node); + } + while let Some(node_id) = stack.pop() { + let (node, parent) = tree.node_parent_mut(node_id).unwrap(); + if self.upward_pass(node, parent) { + stack.push(tree.parent_id(node_id).unwrap()); + } + } + } +} From 4096861c597a003ec247bafccf82b7c224b91bf2 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 24 Nov 2022 22:56:19 -0600 Subject: [PATCH 07/43] work on upward pass --- packages/native-core/src/tree.rs | 52 +++++++++++++++++++++---- packages/native-core/src/worker_pool.rs | 20 ---------- 2 files changed, 45 insertions(+), 27 deletions(-) delete mode 100644 packages/native-core/src/worker_pool.rs diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index c77724891..6645eabd3 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -1,4 +1,5 @@ use core::panic; +use crossbeam_deque::{Injector, Stealer, Worker}; use parking_lot::lock_api::RawMutex as _; use parking_lot::{RawMutex, RwLock}; use slab::Slab; @@ -6,6 +7,7 @@ use std::cell::UnsafeCell; use std::collections::VecDeque; use std::marker::PhantomData; use std::sync::Arc; +use std::thread; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)] pub struct NodeId(pub usize); @@ -824,15 +826,51 @@ trait UpwardPass { fn upward_pass(&mut self, node: &mut T, parent: Option<&mut T>) -> bool; fn resolve_pass(&mut self, tree: &mut impl TreeView, starting_nodes: &[NodeId]) { - let mut stack = Vec::new(); + let global = Injector::default(); for node in starting_nodes { - stack.push(*node); + global.push(*node); } - while let Some(node_id) = stack.pop() { - let (node, parent) = tree.node_parent_mut(node_id).unwrap(); - if self.upward_pass(node, parent) { - stack.push(tree.parent_id(node_id).unwrap()); + + let core_count = thread::available_parallelism() + .map(|c| c.get()) + .unwrap_or(1); + let workers: Vec<_> = (0..core_count).map(|_| Worker::new_fifo()).collect(); + let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); + let shared_view = SharedView::new(tree); + thread::scope(|s| { + let global = &global; + let stealers = &stealers; + for (_, w) in (0..core_count).zip(workers.into_iter()) { + let mut shared_view = shared_view.clone(); + s.spawn(move || { + while let Some(id) = find_task(&w, &global, &stealers) { + let (node, parent) = shared_view.node_parent_mut(id).unwrap(); + if self.upward_pass(node, parent) { + if let Some(id) = shared_view.parent_id(id) { + w.push(id); + } + } + } + }); } - } + }); } } + +fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { + // Pop a task from the local queue, if not empty. + local.pop().or_else(|| { + // Otherwise, we need to look for a task elsewhere. + std::iter::repeat_with(|| { + // Try stealing a batch of tasks from the global queue. + global + .steal_batch_and_pop(local) + // Or try stealing a task from one of the other threads. + .or_else(|| stealers.iter().map(|s| s.steal()).collect()) + }) + // Loop while no task was stolen and any steal operation needs to be retried. + .find(|s| !s.is_retry()) + // Extract the stolen task, if there is one. + .and_then(|s| s.success()) + }) +} diff --git a/packages/native-core/src/worker_pool.rs b/packages/native-core/src/worker_pool.rs deleted file mode 100644 index 5ecf377aa..000000000 --- a/packages/native-core/src/worker_pool.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crossbeam_deque::{Injector, Stealer, Worker}; -use std::iter; - -fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { - // Pop a task from the local queue, if not empty. - local.pop().or_else(|| { - // Otherwise, we need to look for a task elsewhere. - iter::repeat_with(|| { - // Try stealing a batch of tasks from the global queue. - global - .steal_batch_and_pop(local) - // Or try stealing a task from one of the other threads. - .or_else(|| stealers.iter().map(|s| s.steal()).collect()) - }) - // Loop while no task was stolen and any steal operation needs to be retried. - .find(|s| !s.is_retry()) - // Extract the stolen task, if there is one. - .and_then(|s| s.success()) - }) -} From c7eeeef68e5875170ce543c68bee01e66a2f3fa5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Nov 2022 11:44:46 -0600 Subject: [PATCH 08/43] add more code for parrellel passes --- packages/native-core/Cargo.toml | 1 + packages/native-core/src/tree.rs | 238 +++++++++++++++++++++++++++---- 2 files changed, 213 insertions(+), 26 deletions(-) diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 496faa893..0dc7e0d6e 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -22,6 +22,7 @@ anymap = "0.12.1" slab = "0.4" parking_lot = "0.12.1" crossbeam-deque = "0.8.2" +dashmap = "5.4.0" [dev-dependencies] rand = "0.8.5" \ No newline at end of file diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 6645eabd3..f4ad67f2e 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -1,7 +1,14 @@ use core::panic; +use std::hash::BuildHasherDefault; +use std::sync::atomic::{AtomicU64, Ordering}; + use crossbeam_deque::{Injector, Stealer, Worker}; +use dashmap::DashSet; +use dioxus_core::ScopeId; +use dioxus_html::u; use parking_lot::lock_api::RawMutex as _; -use parking_lot::{RawMutex, RwLock}; +use parking_lot::{Mutex, RawMutex, RwLock, RwLockWriteGuard}; +use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use slab::Slab; use std::cell::UnsafeCell; use std::collections::VecDeque; @@ -822,39 +829,218 @@ fn traverse_breadth_first() { }); } +enum PassDirection { + Up, + Down, + Node, +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +struct PassId(u64); + trait UpwardPass { - fn upward_pass(&mut self, node: &mut T, parent: Option<&mut T>) -> bool; + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn upward_pass(&self, node: &mut T, parent: Option<&mut T>) -> bool; +} - fn resolve_pass(&mut self, tree: &mut impl TreeView, starting_nodes: &[NodeId]) { - let global = Injector::default(); - for node in starting_nodes { - global.push(*node); +trait DownwardPass { + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn downward_pass<'a>( + &self, + node: &mut T, + children: &mut dyn Iterator, + ) -> bool; +} + +trait NodePass { + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn node_pass(&self, node: &mut T) -> bool; +} + +enum AnyPass { + Upward(Box + Send + Sync>), + Downward(Box + Send + Sync>), + Node(Box + Send + Sync>), +} + +impl AnyPass { + fn pass_id(&self) -> PassId { + match self { + Self::Upward(pass) => pass.pass_id(), + Self::Downward(pass) => pass.pass_id(), + Self::Node(pass) => pass.pass_id(), } + } - let core_count = thread::available_parallelism() - .map(|c| c.get()) - .unwrap_or(1); - let workers: Vec<_> = (0..core_count).map(|_| Worker::new_fifo()).collect(); - let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); - let shared_view = SharedView::new(tree); - thread::scope(|s| { - let global = &global; - let stealers = &stealers; - for (_, w) in (0..core_count).zip(workers.into_iter()) { - let mut shared_view = shared_view.clone(); - s.spawn(move || { - while let Some(id) = find_task(&w, &global, &stealers) { - let (node, parent) = shared_view.node_parent_mut(id).unwrap(); - if self.upward_pass(node, parent) { - if let Some(id) = shared_view.parent_id(id) { - w.push(id); + fn dependancies(&self) -> &'static [PassId] { + match self { + Self::Upward(pass) => pass.dependancies(), + Self::Downward(pass) => pass.dependancies(), + Self::Node(pass) => pass.dependancies(), + } + } + + fn dependants(&self) -> &'static [PassId] { + match self { + Self::Upward(pass) => pass.dependants(), + Self::Downward(pass) => pass.dependants(), + Self::Node(pass) => pass.dependants(), + } + } +} + +type FxDashSet = dashmap::DashSet>; +type FxDashMap = dashmap::DashMap>; + +#[derive(Default)] +struct DirtyNodeStates { + dirty: FxDashMap>, +} + +impl DirtyNodeStates { + fn new(starting_nodes: FxHashMap>) -> Self { + let mut this = Self::default(); + for (node, nodes) in starting_nodes { + for pass_id in nodes { + this.insert(pass_id, node); + } + } + this + } + + fn insert(&self, pass_id: PassId, node_id: NodeId) { + let pass_id = pass_id.0; + let index = pass_id / 64; + let bit = pass_id % 64; + let encoded = 1 << bit; + if let Some(dirty) = self.dirty.get(&node_id) { + if let Some(atomic) = dirty.get(index as usize) { + atomic.fetch_or(encoded, Ordering::Relaxed); + } else { + drop(dirty); + let mut write = self.dirty.get_mut(&node_id).unwrap(); + write.resize_with(index as usize + 1, || AtomicU64::new(0)); + write[index as usize].fetch_or(encoded, Ordering::Relaxed); + } + } else { + self.dirty.insert(node_id, vec![AtomicU64::new(encoded)]); + } + } + + fn all_dirty(&self, pass_id: PassId) -> impl Iterator + '_ { + let pass_id = pass_id.0; + let index = pass_id / 64; + let bit = pass_id % 64; + let encoded = 1 << bit; + self.dirty.iter().filter_map(move |entry| { + let node_id = entry.key(); + let dirty = entry.value(); + if let Some(atomic) = dirty.get(index as usize) { + if atomic.load(Ordering::Relaxed) & encoded != 0 { + Some(*node_id) + } else { + None + } + } else { + None + } + }) + } +} + +fn resolve_passes( + tree: &mut impl TreeView, + starting_nodes: FxHashMap>, + mut passes: Vec>, +) { + let dirty_nodes: Arc = Arc::new(DirtyNodeStates::new(starting_nodes)); + let global = Injector::default(); + + let core_count = thread::available_parallelism() + .map(|c| c.get()) + .unwrap_or(1); + let workers: Vec> = (0..core_count).map(|_| Worker::new_fifo()).collect(); + let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); + let shared_view = SharedView::new(tree); + let mut resolved_passes: FxHashSet = FxHashSet::default(); + let current_pass: Arc>>> = Arc::new(RwLock::new(None)); + thread::scope(|s| { + let global = &global; + let stealers = &stealers; + for (_, w) in (0..core_count).zip(workers.into_iter()) { + let mut shared_view = shared_view.clone(); + let current_pass = current_pass.clone(); + let dirty_nodes = dirty_nodes.clone(); + s.spawn(move || { + while let Some(current_pass) = &*current_pass.read() { + match current_pass { + AnyPass::Upward(pass) => { + while let Some(id) = find_task(&w, global, stealers) { + let (node, parent) = shared_view.node_parent_mut(id).unwrap(); + if pass.upward_pass(node, parent) { + if let Some(id) = shared_view.parent_id(id) { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, id); + } + w.push(id); + } + } + } + } + AnyPass::Downward(pass) => { + while let Some(id) = find_task(&w, global, stealers) { + let (node, mut children) = + shared_view.parent_child_mut(id).unwrap(); + if pass.downward_pass(node, &mut children) { + drop(children); + for id in shared_view.children_ids(id).unwrap() { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, *id); + } + w.push(*id); + } + } + } + } + AnyPass::Node(pass) => { + while let Some(id) = find_task(&w, global, stealers) { + let node = shared_view.get_mut(id).unwrap(); + if pass.node_pass(node) { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, id); + } + } } } } - }); + } + }); + } + while !passes.is_empty() { + for i in 0..passes.len() { + if passes[i] + .dependancies() + .iter() + .all(|id| resolved_passes.contains(id)) + { + let pass = passes.remove(i); + let pass_id = pass.pass_id(); + for node in dirty_nodes.all_dirty(pass_id) { + global.push(node); + } + resolved_passes.insert(pass_id); + break; + } } - }); - } + } + *current_pass.write() = None; + }); } fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { From 26d4e48480f22589e5e13a48953d4a853b2d7f7d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Nov 2022 11:45:47 -0600 Subject: [PATCH 09/43] make resolve passes public --- packages/native-core/src/tree.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index f4ad67f2e..f5b4c09bf 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -829,23 +829,17 @@ fn traverse_breadth_first() { }); } -enum PassDirection { - Up, - Down, - Node, -} - #[derive(PartialEq, Eq, Hash, Clone, Copy)] -struct PassId(u64); +pub struct PassId(u64); -trait UpwardPass { +pub trait UpwardPass { fn pass_id(&self) -> PassId; fn dependancies(&self) -> &'static [PassId]; fn dependants(&self) -> &'static [PassId]; fn upward_pass(&self, node: &mut T, parent: Option<&mut T>) -> bool; } -trait DownwardPass { +pub trait DownwardPass { fn pass_id(&self) -> PassId; fn dependancies(&self) -> &'static [PassId]; fn dependants(&self) -> &'static [PassId]; @@ -856,14 +850,14 @@ trait DownwardPass { ) -> bool; } -trait NodePass { +pub trait NodePass { fn pass_id(&self) -> PassId; fn dependancies(&self) -> &'static [PassId]; fn dependants(&self) -> &'static [PassId]; fn node_pass(&self, node: &mut T) -> bool; } -enum AnyPass { +pub enum AnyPass { Upward(Box + Send + Sync>), Downward(Box + Send + Sync>), Node(Box + Send + Sync>), @@ -954,7 +948,7 @@ impl DirtyNodeStates { } } -fn resolve_passes( +pub fn resolve_passes( tree: &mut impl TreeView, starting_nodes: FxHashMap>, mut passes: Vec>, From 9e47468c3ac40ab04719fc46aa33e9cfa2966841 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Nov 2022 13:38:01 -0600 Subject: [PATCH 10/43] more work on parallel passes --- packages/native-core/src/lib.rs | 1 + packages/native-core/src/passes.rs | 432 +++++++++++++++++++++++++++++ packages/native-core/src/tree.rs | 259 +---------------- 3 files changed, 435 insertions(+), 257 deletions(-) create mode 100644 packages/native-core/src/passes.rs diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 895bb7dc9..56c4a6a73 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -3,6 +3,7 @@ use tree::NodeId; pub mod layout_attributes; pub mod node; pub mod node_ref; +pub mod passes; pub mod real_dom; pub mod state; pub mod tree; diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs new file mode 100644 index 000000000..aa25a81f2 --- /dev/null +++ b/packages/native-core/src/passes.rs @@ -0,0 +1,432 @@ +use crossbeam_deque::{Injector, Stealer, Worker}; +use parking_lot::RwLock; +use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use std::hash::BuildHasherDefault; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use crate::tree::{NodeId, SharedView, TreeView}; + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct PassId(u64); + +pub trait UpwardPass { + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn pass<'a>(&self, node: &mut T, children: &mut dyn Iterator) -> bool; +} + +pub trait DownwardPass { + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn pass(&self, node: &mut T, parent: Option<&mut T>) -> bool; +} + +pub trait NodePass { + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn pass(&self, node: &mut T) -> bool; +} + +pub enum AnyPass { + Upward(Box + Send + Sync>), + Downward(Box + Send + Sync>), + Node(Box + Send + Sync>), +} + +impl AnyPass { + fn pass_id(&self) -> PassId { + match self { + Self::Upward(pass) => pass.pass_id(), + Self::Downward(pass) => pass.pass_id(), + Self::Node(pass) => pass.pass_id(), + } + } + + fn dependancies(&self) -> &'static [PassId] { + match self { + Self::Upward(pass) => pass.dependancies(), + Self::Downward(pass) => pass.dependancies(), + Self::Node(pass) => pass.dependancies(), + } + } +} + +type FxDashMap = dashmap::DashMap>; + +#[derive(Default)] +struct DirtyNodeStates { + dirty: FxDashMap>, +} + +impl DirtyNodeStates { + fn new(starting_nodes: FxHashMap>) -> Self { + let this = Self::default(); + for (node, nodes) in starting_nodes { + for pass_id in nodes { + this.insert(pass_id, node); + } + } + this + } + + fn insert(&self, pass_id: PassId, node_id: NodeId) { + let pass_id = pass_id.0; + let index = pass_id / 64; + let bit = pass_id % 64; + let encoded = 1 << bit; + if let Some(dirty) = self.dirty.get(&node_id) { + if let Some(atomic) = dirty.get(index as usize) { + atomic.fetch_or(encoded, Ordering::Relaxed); + } else { + drop(dirty); + let mut write = self.dirty.get_mut(&node_id).unwrap(); + write.resize_with(index as usize + 1, || AtomicU64::new(0)); + write[index as usize].fetch_or(encoded, Ordering::Relaxed); + } + } else { + let mut v = Vec::with_capacity(index as usize + 1); + v.resize_with(index as usize + 1, || AtomicU64::new(0)); + v[index as usize].fetch_or(encoded, Ordering::Relaxed); + self.dirty.insert(node_id, v); + } + } + + fn all_dirty(&self, pass_id: PassId) -> impl Iterator + '_ { + let pass_id = pass_id.0; + let index = pass_id / 64; + let bit = pass_id % 64; + let encoded = 1 << bit; + self.dirty.iter().filter_map(move |entry| { + let node_id = entry.key(); + let dirty = entry.value(); + if let Some(atomic) = dirty.get(index as usize) { + if atomic.load(Ordering::Relaxed) & encoded != 0 { + Some(*node_id) + } else { + None + } + } else { + None + } + }) + } +} + +fn get_pass>( + passes: &mut Vec>, + resolved_passes: &mut FxHashSet, + dirty_nodes: &DirtyNodeStates, + shared_view: &mut SharedView, + global: &Injector, + current_pass: &RwLock>>, +) { + for i in 0..passes.len() { + if passes[i] + .dependancies() + .iter() + .all(|id| resolved_passes.contains(id)) + { + let pass = passes.remove(i); + let pass_id = pass.pass_id(); + resolved_passes.insert(pass_id); + match pass { + AnyPass::Upward(pass) => { + // Upward passes are more difficult. Right now we limit them to only one thread. + let worker = Worker::new_fifo(); + let mut queued_nodes = FxHashSet::default(); + for node in dirty_nodes.all_dirty(pass_id) { + queued_nodes.insert(node); + worker.push(node); + } + while let Some(id) = worker.pop() { + let (node, mut children) = shared_view.parent_child_mut(id).unwrap(); + if pass.pass(node, &mut children) { + drop(children); + if let Some(id) = shared_view.parent_id(id) { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, id); + } + if !queued_nodes.contains(&id) { + queued_nodes.insert(id); + worker.push(id); + } + } + } + } + } + _ => { + for node in dirty_nodes.all_dirty(pass_id) { + global.push(node); + } + current_pass.write().replace(pass); + } + } + + break; + } + } +} + +pub fn resolve_passes( + tree: &mut impl TreeView, + starting_nodes: FxHashMap>, + mut passes: Vec>, +) { + let dirty_nodes: Arc = Arc::new(DirtyNodeStates::new(starting_nodes)); + let global = Injector::default(); + + let core_count = thread::available_parallelism() + .map(|c| c.get()) + .unwrap_or(1); + let workers: Vec> = (0..core_count).map(|_| Worker::new_fifo()).collect(); + let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); + let mut shared_view = SharedView::new(tree); + let mut resolved_passes: FxHashSet = FxHashSet::default(); + let current_pass: Arc>>> = Arc::new(RwLock::new(None)); + + thread::scope(|s| { + get_pass( + &mut passes, + &mut resolved_passes, + &dirty_nodes, + &mut shared_view, + &global, + ¤t_pass, + ); + let global = &global; + let stealers = &stealers; + for (_, w) in (0..core_count).zip(workers.into_iter()) { + let mut shared_view = shared_view.clone(); + let current_pass = current_pass.clone(); + let dirty_nodes = dirty_nodes.clone(); + s.spawn(move || { + while let Some(current_pass) = &*current_pass.read() { + match current_pass { + AnyPass::Upward(_) => { + todo!("Upward passes are single threaded") + } + AnyPass::Node(pass) => { + // Node passes are the easiest to parallelize. We just run the pass on each node. + while let Some(id) = find_task(&w, global, stealers) { + let node = shared_view.get_mut(id).unwrap(); + if pass.pass(node) { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, id); + } + } + } + } + AnyPass::Downward(pass) => { + // Downward passes are easy to parallelize. We try to keep trees localized to one thread, but allow work stealing to balance the load. + while let Some(id) = find_task(&w, global, stealers) { + let (node, parent) = shared_view.node_parent_mut(id).unwrap(); + if pass.pass(node, parent) { + for id in shared_view.children_ids(id).unwrap() { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, *id); + } + w.push(*id); + } + } + } + } + } + } + }); + } + while !passes.is_empty() { + while !stealers.iter().all(|s| s.is_empty()) { + std::thread::sleep(Duration::from_millis(50)); + } + get_pass( + &mut passes, + &mut resolved_passes, + &dirty_nodes, + &mut shared_view, + global, + ¤t_pass, + ); + } + *current_pass.write() = None; + }); +} + +fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { + // Pop a task from the local queue, if not empty. + local.pop().or_else(|| { + // Otherwise, we need to look for a task elsewhere. + std::iter::repeat_with(|| { + // Try stealing a batch of tasks from the global queue. + global + .steal_batch_and_pop(local) + // Or try stealing a task from one of the other threads. + .or_else(|| stealers.iter().map(|s| s.steal()).collect()) + }) + // Loop while no task was stolen and any steal operation needs to be retried. + .find(|s| !s.is_retry()) + // Extract the stolen task, if there is one. + .and_then(|s| s.success()) + }) +} + +#[test] +fn node_pass() { + use crate::tree::{Tree, TreeLike}; + let mut tree = Tree::new(0); + let parent = tree.root(); + let child1 = tree.create_node(1); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(3); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(2); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(4); + tree.add_child(child2, grandchild2); + + struct AddPass; + + impl NodePass for AddPass { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn pass(&self, node: &mut i32) -> bool { + *node += 1; + true + } + } + + let passes = vec![AnyPass::Node(Box::new(AddPass))]; + let mut dirty_nodes: FxHashMap> = FxHashMap::default(); + dirty_nodes.insert(tree.root(), [PassId(0)].into_iter().collect()); + resolve_passes(&mut tree, dirty_nodes, passes); + + assert_eq!(tree.get(tree.root()).unwrap(), &1); +} + +#[test] +fn down_pass() { + use crate::tree::{Tree, TreeLike}; + let mut tree = Tree::new(1); + let parent = tree.root(); + let child1 = tree.create_node(1); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(1); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(1); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(1); + tree.add_child(child2, grandchild2); + + struct AddPass; + + impl DownwardPass for AddPass { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { + if let Some(parent) = parent { + *node += *parent; + } + true + } + } + + let passes = vec![AnyPass::Downward(Box::new(AddPass))]; + let mut dirty_nodes: FxHashMap> = FxHashMap::default(); + dirty_nodes.insert(tree.root(), [PassId(0)].into_iter().collect()); + resolve_passes(&mut tree, dirty_nodes, passes); + + assert_eq!(tree.get(tree.root()).unwrap(), &1); + assert_eq!(tree.get(child1).unwrap(), &2); + assert_eq!(tree.get(grandchild1).unwrap(), &3); + assert_eq!(tree.get(child2).unwrap(), &2); + assert_eq!(tree.get(grandchild2).unwrap(), &3); +} + +#[test] +fn up_pass() { + use crate::tree::{Tree, TreeLike}; + // Tree before: + // 0=\ + // 0=\ + // 1 + // 0=\ + // 1 + // Tree after: + // 2=\ + // 1=\ + // 1 + // 1=\ + // 1 + let mut tree = Tree::new(0); + let parent = tree.root(); + let child1 = tree.create_node(0); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(1); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(0); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(1); + tree.add_child(child2, grandchild2); + + struct AddPass; + + impl UpwardPass for AddPass { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn pass<'a>( + &self, + node: &mut i32, + children: &mut dyn Iterator, + ) -> bool { + *node += children.map(|i| *i).sum::(); + true + } + } + + let passes = vec![AnyPass::Upward(Box::new(AddPass))]; + let mut dirty_nodes: FxHashMap> = FxHashMap::default(); + dirty_nodes.insert(grandchild1, [PassId(0)].into_iter().collect()); + dirty_nodes.insert(grandchild2, [PassId(0)].into_iter().collect()); + resolve_passes(&mut tree, dirty_nodes, passes); + + assert_eq!(tree.get(tree.root()).unwrap(), &2); + assert_eq!(tree.get(child1).unwrap(), &1); + assert_eq!(tree.get(grandchild1).unwrap(), &1); + assert_eq!(tree.get(child2).unwrap(), &1); + assert_eq!(tree.get(grandchild2).unwrap(), &1); +} diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index f5b4c09bf..4bec046be 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -1,20 +1,11 @@ use core::panic; -use std::hash::BuildHasherDefault; -use std::sync::atomic::{AtomicU64, Ordering}; - -use crossbeam_deque::{Injector, Stealer, Worker}; -use dashmap::DashSet; -use dioxus_core::ScopeId; -use dioxus_html::u; use parking_lot::lock_api::RawMutex as _; -use parking_lot::{Mutex, RawMutex, RwLock, RwLockWriteGuard}; -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use parking_lot::{RawMutex, RwLock}; use slab::Slab; use std::cell::UnsafeCell; use std::collections::VecDeque; use std::marker::PhantomData; use std::sync::Arc; -use std::thread; #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)] pub struct NodeId(pub usize); @@ -109,7 +100,7 @@ pub trait TreeView: Sized { // Safety: No node has itself as a parent. (*mut_ptr) .get_mut(id) - .map(|node| (node, (*mut_ptr).parent_mut(id).map(|parent| parent))) + .map(|node| (node, (*mut_ptr).parent_mut(id))) } } @@ -808,249 +799,3 @@ fn traverse_depth_first() { node_count += 1; }); } - -#[test] -fn traverse_breadth_first() { - let mut tree = Tree::new(0); - let parent = tree.root(); - let child1 = tree.create_node(1); - tree.add_child(parent, child1); - let grandchild1 = tree.create_node(3); - tree.add_child(child1, grandchild1); - let child2 = tree.create_node(2); - tree.add_child(parent, child2); - let grandchild2 = tree.create_node(4); - tree.add_child(child2, grandchild2); - - let mut node_count = 0; - tree.traverse_breadth_first(move |node| { - assert_eq!(*node, node_count); - node_count += 1; - }); -} - -#[derive(PartialEq, Eq, Hash, Clone, Copy)] -pub struct PassId(u64); - -pub trait UpwardPass { - fn pass_id(&self) -> PassId; - fn dependancies(&self) -> &'static [PassId]; - fn dependants(&self) -> &'static [PassId]; - fn upward_pass(&self, node: &mut T, parent: Option<&mut T>) -> bool; -} - -pub trait DownwardPass { - fn pass_id(&self) -> PassId; - fn dependancies(&self) -> &'static [PassId]; - fn dependants(&self) -> &'static [PassId]; - fn downward_pass<'a>( - &self, - node: &mut T, - children: &mut dyn Iterator, - ) -> bool; -} - -pub trait NodePass { - fn pass_id(&self) -> PassId; - fn dependancies(&self) -> &'static [PassId]; - fn dependants(&self) -> &'static [PassId]; - fn node_pass(&self, node: &mut T) -> bool; -} - -pub enum AnyPass { - Upward(Box + Send + Sync>), - Downward(Box + Send + Sync>), - Node(Box + Send + Sync>), -} - -impl AnyPass { - fn pass_id(&self) -> PassId { - match self { - Self::Upward(pass) => pass.pass_id(), - Self::Downward(pass) => pass.pass_id(), - Self::Node(pass) => pass.pass_id(), - } - } - - fn dependancies(&self) -> &'static [PassId] { - match self { - Self::Upward(pass) => pass.dependancies(), - Self::Downward(pass) => pass.dependancies(), - Self::Node(pass) => pass.dependancies(), - } - } - - fn dependants(&self) -> &'static [PassId] { - match self { - Self::Upward(pass) => pass.dependants(), - Self::Downward(pass) => pass.dependants(), - Self::Node(pass) => pass.dependants(), - } - } -} - -type FxDashSet = dashmap::DashSet>; -type FxDashMap = dashmap::DashMap>; - -#[derive(Default)] -struct DirtyNodeStates { - dirty: FxDashMap>, -} - -impl DirtyNodeStates { - fn new(starting_nodes: FxHashMap>) -> Self { - let mut this = Self::default(); - for (node, nodes) in starting_nodes { - for pass_id in nodes { - this.insert(pass_id, node); - } - } - this - } - - fn insert(&self, pass_id: PassId, node_id: NodeId) { - let pass_id = pass_id.0; - let index = pass_id / 64; - let bit = pass_id % 64; - let encoded = 1 << bit; - if let Some(dirty) = self.dirty.get(&node_id) { - if let Some(atomic) = dirty.get(index as usize) { - atomic.fetch_or(encoded, Ordering::Relaxed); - } else { - drop(dirty); - let mut write = self.dirty.get_mut(&node_id).unwrap(); - write.resize_with(index as usize + 1, || AtomicU64::new(0)); - write[index as usize].fetch_or(encoded, Ordering::Relaxed); - } - } else { - self.dirty.insert(node_id, vec![AtomicU64::new(encoded)]); - } - } - - fn all_dirty(&self, pass_id: PassId) -> impl Iterator + '_ { - let pass_id = pass_id.0; - let index = pass_id / 64; - let bit = pass_id % 64; - let encoded = 1 << bit; - self.dirty.iter().filter_map(move |entry| { - let node_id = entry.key(); - let dirty = entry.value(); - if let Some(atomic) = dirty.get(index as usize) { - if atomic.load(Ordering::Relaxed) & encoded != 0 { - Some(*node_id) - } else { - None - } - } else { - None - } - }) - } -} - -pub fn resolve_passes( - tree: &mut impl TreeView, - starting_nodes: FxHashMap>, - mut passes: Vec>, -) { - let dirty_nodes: Arc = Arc::new(DirtyNodeStates::new(starting_nodes)); - let global = Injector::default(); - - let core_count = thread::available_parallelism() - .map(|c| c.get()) - .unwrap_or(1); - let workers: Vec> = (0..core_count).map(|_| Worker::new_fifo()).collect(); - let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); - let shared_view = SharedView::new(tree); - let mut resolved_passes: FxHashSet = FxHashSet::default(); - let current_pass: Arc>>> = Arc::new(RwLock::new(None)); - thread::scope(|s| { - let global = &global; - let stealers = &stealers; - for (_, w) in (0..core_count).zip(workers.into_iter()) { - let mut shared_view = shared_view.clone(); - let current_pass = current_pass.clone(); - let dirty_nodes = dirty_nodes.clone(); - s.spawn(move || { - while let Some(current_pass) = &*current_pass.read() { - match current_pass { - AnyPass::Upward(pass) => { - while let Some(id) = find_task(&w, global, stealers) { - let (node, parent) = shared_view.node_parent_mut(id).unwrap(); - if pass.upward_pass(node, parent) { - if let Some(id) = shared_view.parent_id(id) { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, id); - } - w.push(id); - } - } - } - } - AnyPass::Downward(pass) => { - while let Some(id) = find_task(&w, global, stealers) { - let (node, mut children) = - shared_view.parent_child_mut(id).unwrap(); - if pass.downward_pass(node, &mut children) { - drop(children); - for id in shared_view.children_ids(id).unwrap() { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, *id); - } - w.push(*id); - } - } - } - } - AnyPass::Node(pass) => { - while let Some(id) = find_task(&w, global, stealers) { - let node = shared_view.get_mut(id).unwrap(); - if pass.node_pass(node) { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, id); - } - } - } - } - } - } - }); - } - while !passes.is_empty() { - for i in 0..passes.len() { - if passes[i] - .dependancies() - .iter() - .all(|id| resolved_passes.contains(id)) - { - let pass = passes.remove(i); - let pass_id = pass.pass_id(); - for node in dirty_nodes.all_dirty(pass_id) { - global.push(node); - } - resolved_passes.insert(pass_id); - break; - } - } - } - *current_pass.write() = None; - }); -} - -fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { - // Pop a task from the local queue, if not empty. - local.pop().or_else(|| { - // Otherwise, we need to look for a task elsewhere. - std::iter::repeat_with(|| { - // Try stealing a batch of tasks from the global queue. - global - .steal_batch_and_pop(local) - // Or try stealing a task from one of the other threads. - .or_else(|| stealers.iter().map(|s| s.steal()).collect()) - }) - // Loop while no task was stolen and any steal operation needs to be retried. - .find(|s| !s.is_retry()) - // Extract the stolen task, if there is one. - .and_then(|s| s.success()) - }) -} From 7d3ac26fce80bcdb13bd216c0dfae7629edf16b0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Nov 2022 15:04:00 -0600 Subject: [PATCH 11/43] fix deadlock and add more tests --- packages/native-core/src/passes.rs | 295 ++++++++++++++++++++++++----- 1 file changed, 251 insertions(+), 44 deletions(-) diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index aa25a81f2..f7b416e5c 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -1,15 +1,14 @@ use crossbeam_deque::{Injector, Stealer, Worker}; -use parking_lot::RwLock; +use parking_lot::{Condvar, Mutex, RwLock}; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; use std::hash::BuildHasherDefault; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; use std::thread; -use std::time::Duration; use crate::tree::{NodeId, SharedView, TreeView}; -#[derive(PartialEq, Eq, Hash, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct PassId(u64); pub trait UpwardPass { @@ -124,7 +123,7 @@ fn get_pass>( dirty_nodes: &DirtyNodeStates, shared_view: &mut SharedView, global: &Injector, - current_pass: &RwLock>>, + current_pass: &mut Option>, ) { for i in 0..passes.len() { if passes[i] @@ -134,6 +133,7 @@ fn get_pass>( { let pass = passes.remove(i); let pass_id = pass.pass_id(); + println!("Running pass {:?}", pass_id); resolved_passes.insert(pass_id); match pass { AnyPass::Upward(pass) => { @@ -164,13 +164,21 @@ fn get_pass>( for node in dirty_nodes.all_dirty(pass_id) { global.push(node); } - current_pass.write().replace(pass); + println!( + "Task: {:?} {:?}", + pass_id, + dirty_nodes.all_dirty(pass_id).collect::>() + ); + current_pass.replace(pass); } } - - break; + return; } } + panic!( + "No pass found with all dependancies resolved in {:?}", + passes.iter().map(|p| p.pass_id()).collect::>() + ); } pub fn resolve_passes( @@ -178,6 +186,7 @@ pub fn resolve_passes( starting_nodes: FxHashMap>, mut passes: Vec>, ) { + assert!(!passes.is_empty()); let dirty_nodes: Arc = Arc::new(DirtyNodeStates::new(starting_nodes)); let global = Injector::default(); @@ -191,69 +200,105 @@ pub fn resolve_passes( let current_pass: Arc>>> = Arc::new(RwLock::new(None)); thread::scope(|s| { - get_pass( - &mut passes, - &mut resolved_passes, - &dirty_nodes, - &mut shared_view, - &global, - ¤t_pass, - ); + { + let mut write = current_pass.write(); + get_pass( + &mut passes, + &mut resolved_passes, + &dirty_nodes, + &mut shared_view, + &global, + &mut *write, + ); + } let global = &global; let stealers = &stealers; + let mut thread_handles = Vec::new(); + let threads_finished = Arc::new((Mutex::new(0), Condvar::new())); for (_, w) in (0..core_count).zip(workers.into_iter()) { let mut shared_view = shared_view.clone(); let current_pass = current_pass.clone(); let dirty_nodes = dirty_nodes.clone(); - s.spawn(move || { - while let Some(current_pass) = &*current_pass.read() { - match current_pass { - AnyPass::Upward(_) => { - todo!("Upward passes are single threaded") - } - AnyPass::Node(pass) => { - // Node passes are the easiest to parallelize. We just run the pass on each node. - while let Some(id) = find_task(&w, global, stealers) { - let node = shared_view.get_mut(id).unwrap(); - if pass.pass(node) { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, id); - } - } + let threads_finished = threads_finished.clone(); + thread_handles.push(s.spawn(move || { + loop { + let read = current_pass.read(); + if let Some(current_pass) = &*read { + match current_pass { + AnyPass::Upward(_) => { + todo!("Upward passes are single threaded") } - } - AnyPass::Downward(pass) => { - // Downward passes are easy to parallelize. We try to keep trees localized to one thread, but allow work stealing to balance the load. - while let Some(id) = find_task(&w, global, stealers) { - let (node, parent) = shared_view.node_parent_mut(id).unwrap(); - if pass.pass(node, parent) { - for id in shared_view.children_ids(id).unwrap() { + AnyPass::Node(pass) => { + // Node passes are the easiest to parallelize. We just run the pass on each node. + while let Some(id) = find_task(&w, global, stealers) { + let node = shared_view.get_mut(id).unwrap(); + if pass.pass(node) { for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, *id); + dirty_nodes.insert(*dependant, id); + } + } + } + } + AnyPass::Downward(pass) => { + // Downward passes are easy to parallelize. We try to keep trees localized to one thread, but allow work stealing to balance the load. + while let Some(id) = find_task(&w, global, stealers) { + let (node, parent) = shared_view.node_parent_mut(id).unwrap(); + if pass.pass(node, parent) { + for id in shared_view.children_ids(id).unwrap() { + for dependant in pass.dependants() { + dirty_nodes.insert(*dependant, *id); + } + w.push(*id); } - w.push(*id); } } } } + // unblock the rwlock + drop(read); + { + let (count, cvar) = &*threads_finished; + let mut lock = count.lock(); + *lock += 1; + if *lock == core_count { + cvar.notify_all(); + } + } + // wait for the main thread to pick the next pass + thread::park(); + } else { + break; } } - }); + })); } + let wait_for_thread_to_finish = move || { + // this will block until all threads are done with the current pass + let (count, cvar) = &*threads_finished; + let mut count = count.lock(); + cvar.wait(&mut count); + }; while !passes.is_empty() { - while !stealers.iter().all(|s| s.is_empty()) { - std::thread::sleep(Duration::from_millis(50)); - } + wait_for_thread_to_finish(); + let mut write = current_pass.write(); + println!("Threads finished"); get_pass( &mut passes, &mut resolved_passes, &dirty_nodes, &mut shared_view, global, - ¤t_pass, + &mut *write, ); + // notify all threads to start the next pass + for thread in &thread_handles { + thread.thread().unpark(); + } } *current_pass.write() = None; + for thread in &thread_handles { + thread.thread().unpark(); + } }); } @@ -318,6 +363,75 @@ fn node_pass() { assert_eq!(tree.get(tree.root()).unwrap(), &1); } +#[test] +fn dependant_node_pass() { + use crate::tree::{Tree, TreeLike}; + let mut tree = Tree::new(0); + let parent = tree.root(); + let child1 = tree.create_node(1); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(3); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(2); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(4); + tree.add_child(child2, grandchild2); + + struct AddPass; + + impl NodePass for AddPass { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[PassId(1)] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn pass(&self, node: &mut i32) -> bool { + println!("AddPass: {}", node); + *node += 1; + true + } + } + + struct SubtractPass; + + impl NodePass for SubtractPass { + fn pass_id(&self) -> PassId { + PassId(1) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[PassId(0)] + } + + fn pass(&self, node: &mut i32) -> bool { + println!("SubtractPass: {}", node); + *node -= 1; + true + } + } + + let passes = vec![ + AnyPass::Node(Box::new(AddPass)), + AnyPass::Node(Box::new(SubtractPass)), + ]; + let mut dirty_nodes: FxHashMap> = FxHashMap::default(); + dirty_nodes.insert(tree.root(), [PassId(1)].into_iter().collect()); + resolve_passes(&mut tree, dirty_nodes, passes); + + assert_eq!(*tree.get(tree.root()).unwrap(), 0); +} + #[test] fn down_pass() { use crate::tree::{Tree, TreeLike}; @@ -367,6 +481,99 @@ fn down_pass() { assert_eq!(tree.get(grandchild2).unwrap(), &3); } +#[test] +fn dependant_down_pass() { + use crate::tree::{Tree, TreeLike}; + let mut tree = Tree::new(1); + let parent = tree.root(); + let child1 = tree.create_node(1); + tree.add_child(parent, child1); + let grandchild1 = tree.create_node(1); + tree.add_child(child1, grandchild1); + let child2 = tree.create_node(1); + tree.add_child(parent, child2); + let grandchild2 = tree.create_node(1); + tree.add_child(child2, grandchild2); + + struct AddPass; + + impl DownwardPass for AddPass { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[PassId(1)] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { + if let Some(parent) = parent { + *node += *parent; + } + true + } + } + + struct SubtractPass; + + impl DownwardPass for SubtractPass { + fn pass_id(&self) -> PassId { + PassId(1) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[PassId(0)] + } + + fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { + if let Some(parent) = parent { + *node -= *parent; + } + true + } + } + + let passes = vec![ + AnyPass::Downward(Box::new(AddPass)), + AnyPass::Downward(Box::new(SubtractPass)), + ]; + let mut dirty_nodes: FxHashMap> = FxHashMap::default(); + dirty_nodes.insert(tree.root(), [PassId(1)].into_iter().collect()); + resolve_passes(&mut tree, dirty_nodes, passes); + + // Tree before: + // 1=\ + // 1=\ + // 1 + // 1=\ + // 1 + // Tree after subtract: + // 1=\ + // 0=\ + // 1 + // 0=\ + // 1 + // Tree after add: + // 1=\ + // 1=\ + // 2 + // 1=\ + // 2 + assert_eq!(tree.get(tree.root()).unwrap(), &1); + assert_eq!(tree.get(child1).unwrap(), &1); + assert_eq!(tree.get(grandchild1).unwrap(), &2); + assert_eq!(tree.get(child2).unwrap(), &1); + assert_eq!(tree.get(grandchild2).unwrap(), &2); +} + #[test] fn up_pass() { use crate::tree::{Tree, TreeLike}; From 8af59d6969a34519ff0231ddbd086880f1fe49a0 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 25 Nov 2022 22:22:56 -0600 Subject: [PATCH 12/43] move height into the tree instead of the realdom --- packages/native-core/src/node.rs | 3 -- packages/native-core/src/passes.rs | 53 ++++++++++++++++++++++++-- packages/native-core/src/real_dom.rs | 21 +++-------- packages/native-core/src/tree.rs | 56 +++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 23 deletions(-) diff --git a/packages/native-core/src/node.rs b/packages/native-core/src/node.rs index fc06862f9..4530fd350 100644 --- a/packages/native-core/src/node.rs +++ b/packages/native-core/src/node.rs @@ -19,8 +19,6 @@ pub struct NodeData { pub element_id: Option, /// Additional inforation specific to the node type pub node_type: NodeType, - /// The number of parents before the root node. The root node has height 1. - pub height: u16, } /// A type of node with data specific to the node type. The types are a subset of the [VNode] types. @@ -45,7 +43,6 @@ impl Node { node_data: NodeData { element_id: None, node_type, - height: 0, node_id: NodeId(0), }, } diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index f7b416e5c..da01dfb95 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -96,6 +96,22 @@ impl DirtyNodeStates { } } + fn get(&self, pass_id: PassId, node_id: NodeId) -> bool { + let pass_id = pass_id.0; + let index = pass_id / 64; + let bit = pass_id % 64; + let encoded = 1 << bit; + if let Some(dirty) = self.dirty.get(&node_id) { + if let Some(atomic) = dirty.get(index as usize) { + atomic.load(Ordering::SeqCst) & encoded != 0 + } else { + false + } + } else { + false + } + } + fn all_dirty(&self, pass_id: PassId) -> impl Iterator + '_ { let pass_id = pass_id.0; let index = pass_id / 64; @@ -160,7 +176,23 @@ fn get_pass>( } } } - _ => { + AnyPass::Downward(pass) => { + let mut sorted: Vec<_> = dirty_nodes.all_dirty(pass_id).collect(); + sorted.sort_unstable_by_key(|id| shared_view.height(*id)); + println!( + "Task: {:?} {:?}", + pass_id, + sorted + .iter() + .map(|id| (id, shared_view.height(*id))) + .collect::>() + ); + for node in sorted.into_iter() { + global.push(node); + } + current_pass.replace(AnyPass::Downward(pass)); + } + AnyPass::Node(pass) => { for node in dirty_nodes.all_dirty(pass_id) { global.push(node); } @@ -169,7 +201,7 @@ fn get_pass>( pass_id, dirty_nodes.all_dirty(pass_id).collect::>() ); - current_pass.replace(pass); + current_pass.replace(AnyPass::Node(pass)); } } return; @@ -224,6 +256,7 @@ pub fn resolve_passes( loop { let read = current_pass.read(); if let Some(current_pass) = &*read { + let current_pass_id = current_pass.pass_id(); match current_pass { AnyPass::Upward(_) => { todo!("Upward passes are single threaded") @@ -248,7 +281,9 @@ pub fn resolve_passes( for dependant in pass.dependants() { dirty_nodes.insert(*dependant, *id); } - w.push(*id); + if !dirty_nodes.get(current_pass_id, *id) { + w.push(*id); + } } } } @@ -333,6 +368,7 @@ fn node_pass() { tree.add_child(parent, child2); let grandchild2 = tree.create_node(4); tree.add_child(child2, grandchild2); + println!("{:#?}", tree); struct AddPass; @@ -484,14 +520,19 @@ fn down_pass() { #[test] fn dependant_down_pass() { use crate::tree::{Tree, TreeLike}; + // 0 let mut tree = Tree::new(1); let parent = tree.root(); + // 1 let child1 = tree.create_node(1); tree.add_child(parent, child1); + // 2 let grandchild1 = tree.create_node(1); tree.add_child(child1, grandchild1); + // 3 let child2 = tree.create_node(1); tree.add_child(parent, child2); + // 4 let grandchild2 = tree.create_node(1); tree.add_child(child2, grandchild2); @@ -512,7 +553,10 @@ fn dependant_down_pass() { fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { if let Some(parent) = parent { + println!("AddPass: {} -> {}", node, *node + *parent); *node += *parent; + } else { + println!("AddPass: {}", node); } true } @@ -535,7 +579,10 @@ fn dependant_down_pass() { fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { if let Some(parent) = parent { + println!("SubtractPass: {} -> {}", node, *node - *parent); *node -= *parent; + } else { + println!("SubtractPass: {}", node); } true } diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index e80510aa4..683d3bb65 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -93,21 +93,6 @@ impl RealDom { fn add_child(&mut self, node_id: RealNodeId, child_id: RealNodeId) { self.tree.add_child(node_id, child_id); - self.resolve_height(child_id); - } - - fn resolve_height(&mut self, node_id: RealNodeId) { - if let Some((node, Some(parent))) = self.tree.node_parent_mut(node_id) { - let height = parent.node_data.height; - node.node_data.height = height + 1; - unsafe { - let self_mut = self as *mut Self; - // Safety: No node will have itself as a child - for child in self.tree.children_ids(node_id).unwrap() { - (*self_mut).resolve_height(*child); - } - } - } } /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. @@ -382,7 +367,11 @@ impl RealDom { pub fn get_listening_sorted(&self, event: &'static str) -> Vec<&Node> { if let Some(nodes) = self.nodes_listening.get(event) { let mut listening: Vec<_> = nodes.iter().map(|id| &self[*id]).collect(); - listening.sort_by(|n1, n2| (n1.node_data.height).cmp(&n2.node_data.height).reverse()); + listening.sort_by(|n1, n2| { + (self.tree.height(n1.node_data.node_id)) + .cmp(&self.tree.height(n2.node_data.node_id)) + .reverse() + }); listening } else { Vec::new() diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 4bec046be..53f81ba54 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -15,6 +15,7 @@ pub struct Node { value: T, parent: Option, children: Vec, + height: u16, } #[derive(Debug)] @@ -46,6 +47,18 @@ impl Tree { self.remove_recursive(child); } } + + fn set_height(&mut self, node: NodeId, height: u16) { + let self_mut = self as *mut Self; + let node = self.nodes.get_mut(node.0).unwrap(); + node.height = height; + unsafe { + // Safety: No node has itself as a child + for child in &node.children { + (*self_mut).set_height(*child, height + 1); + } + } + } } pub trait TreeView: Sized { @@ -106,6 +119,8 @@ pub trait TreeView: Sized { fn parent_id(&self, id: NodeId) -> Option; + fn height(&self, id: NodeId) -> Option; + fn map &T2, FMut: Fn(&mut T) -> &mut T2>( &mut self, map: F, @@ -306,6 +321,10 @@ impl TreeView for Tree { self.nodes.get(id.0).and_then(|node| node.parent) } + fn height(&self, id: NodeId) -> Option { + self.nodes.get(id.0).map(|n| n.height) + } + fn get_unchecked(&self, id: NodeId) -> &T { unsafe { &self.nodes.get_unchecked(id.0).value } } @@ -326,6 +345,7 @@ impl TreeLike for Tree { value: root, parent: None, children: Vec::new(), + height: 0, })); Self { nodes, root } } @@ -335,12 +355,16 @@ impl TreeLike for Tree { value, parent: None, children: Vec::new(), + height: 0, })) } fn add_child(&mut self, parent: NodeId, new: NodeId) { - self.nodes.get_mut(parent.0).unwrap().children.push(new); self.nodes.get_mut(new.0).unwrap().parent = Some(parent); + let parent = self.nodes.get_mut(parent.0).unwrap(); + parent.children.push(new); + let height = parent.height + 1; + self.set_height(new, height); } fn remove(&mut self, id: NodeId) -> Option { @@ -372,6 +396,8 @@ impl TreeLike for Tree { *id = new_id; } } + let height = parent.height + 1; + self.set_height(new_id, height); } } @@ -386,6 +412,8 @@ impl TreeLike for Tree { .position(|child| child == &id) .unwrap(); parent.children.insert(index, new); + let height = parent.height + 1; + self.set_height(new, height); } fn insert_after(&mut self, id: NodeId, new: NodeId) { @@ -399,6 +427,8 @@ impl TreeLike for Tree { .position(|child| child == &id) .unwrap(); parent.children.insert(index + 1, new); + let height = parent.height + 1; + self.set_height(new, height); } } @@ -498,6 +528,10 @@ where self.tree.parent_id(id) } + fn height(&self, id: NodeId) -> Option { + self.tree.height(id) + } + fn get_unchecked(&self, id: NodeId) -> &T2 { (self.map)(self.tree.get_unchecked(id)) } @@ -630,6 +664,10 @@ impl<'a, T, Tr: TreeView> TreeView for SharedView<'a, T, Tr> { self.with_node(id, |t| t.parent_id(id)) } + fn height(&self, id: NodeId) -> Option { + unsafe { (*self.tree.get()).height(id) } + } + fn size(&self) -> usize { unsafe { (*self.tree.get()).size() } } @@ -644,6 +682,8 @@ fn creation() { println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 2); + assert_eq!(tree.height(parent), Some(0)); + assert_eq!(tree.height(child), Some(1)); assert_eq!(*tree.get(parent).unwrap(), 1); assert_eq!(*tree.get(child).unwrap(), 0); assert_eq!(tree.parent_id(parent), None); @@ -664,6 +704,10 @@ fn insertion() { println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 4); + assert_eq!(tree.height(parent), Some(0)); + assert_eq!(tree.height(child), Some(1)); + assert_eq!(tree.height(before), Some(1)); + assert_eq!(tree.height(after), Some(1)); assert_eq!(*tree.get(parent).unwrap(), 0); assert_eq!(*tree.get(before).unwrap(), 1); assert_eq!(*tree.get(child).unwrap(), 2); @@ -687,6 +731,10 @@ fn deletion() { println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 4); + assert_eq!(tree.height(parent), Some(0)); + assert_eq!(tree.height(child), Some(1)); + assert_eq!(tree.height(before), Some(1)); + assert_eq!(tree.height(after), Some(1)); assert_eq!(*tree.get(parent).unwrap(), 0); assert_eq!(*tree.get(before).unwrap(), 1); assert_eq!(*tree.get(child).unwrap(), 2); @@ -700,6 +748,9 @@ fn deletion() { println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 3); + assert_eq!(tree.height(parent), Some(0)); + assert_eq!(tree.height(before), Some(1)); + assert_eq!(tree.height(after), Some(1)); assert_eq!(*tree.get(parent).unwrap(), 0); assert_eq!(*tree.get(before).unwrap(), 1); assert_eq!(tree.get(child), None); @@ -712,6 +763,8 @@ fn deletion() { println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 2); + assert_eq!(tree.height(parent), Some(0)); + assert_eq!(tree.height(after), Some(1)); assert_eq!(*tree.get(parent).unwrap(), 0); assert_eq!(tree.get(before), None); assert_eq!(*tree.get(after).unwrap(), 3); @@ -722,6 +775,7 @@ fn deletion() { println!("Tree: {:#?}", tree); assert_eq!(tree.size(), 1); + assert_eq!(tree.height(parent), Some(0)); assert_eq!(*tree.get(parent).unwrap(), 0); assert_eq!(tree.get(after), None); assert_eq!(tree.children_ids(parent).unwrap(), &[]); From 51f643c5dc36335b522b25331c3af833768e8c41 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 26 Nov 2022 11:50:54 -0600 Subject: [PATCH 13/43] make passes exicute in parallel instead of executing invidual passes in parellel --- packages/native-core/src/passes.rs | 637 ++++++++++++++++------------- 1 file changed, 342 insertions(+), 295 deletions(-) diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index da01dfb95..b08a65cf5 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -1,59 +1,61 @@ use crossbeam_deque::{Injector, Stealer, Worker}; -use parking_lot::{Condvar, Mutex, RwLock}; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use std::collections::{BTreeMap, HashMap}; use std::hash::BuildHasherDefault; +use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; +use std::os::raw; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -use std::thread; -use crate::tree::{NodeId, SharedView, TreeView}; +use crate::tree::{NodeId, TreeView}; -#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub struct PassId(u64); - -pub trait UpwardPass { - fn pass_id(&self) -> PassId; - fn dependancies(&self) -> &'static [PassId]; - fn dependants(&self) -> &'static [PassId]; - fn pass<'a>(&self, node: &mut T, children: &mut dyn Iterator) -> bool; +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct DirtyNodes { + map: BTreeMap>, } -pub trait DownwardPass { - fn pass_id(&self) -> PassId; - fn dependancies(&self) -> &'static [PassId]; - fn dependants(&self) -> &'static [PassId]; - fn pass(&self, node: &mut T, parent: Option<&mut T>) -> bool; -} - -pub trait NodePass { - fn pass_id(&self) -> PassId; - fn dependancies(&self) -> &'static [PassId]; - fn dependants(&self) -> &'static [PassId]; - fn pass(&self, node: &mut T) -> bool; -} - -pub enum AnyPass { - Upward(Box + Send + Sync>), - Downward(Box + Send + Sync>), - Node(Box + Send + Sync>), -} - -impl AnyPass { - fn pass_id(&self) -> PassId { - match self { - Self::Upward(pass) => pass.pass_id(), - Self::Downward(pass) => pass.pass_id(), - Self::Node(pass) => pass.pass_id(), - } +impl DirtyNodes { + pub fn insert(&mut self, depth: u16, node_id: NodeId) { + self.map + .entry(depth) + .or_insert_with(FxHashSet::default) + .insert(node_id); } - fn dependancies(&self) -> &'static [PassId] { - match self { - Self::Upward(pass) => pass.dependancies(), - Self::Downward(pass) => pass.dependancies(), - Self::Node(pass) => pass.dependancies(), + fn pop_front(&mut self) -> Option { + let (&depth, values) = self.map.iter_mut().next()?; + let key = *values.iter().next()?; + let node_id = values.take(&key)?; + if values.is_empty() { + self.map.remove(&depth); } + Some(node_id) } + + fn pop_back(&mut self) -> Option { + let (&depth, values) = self.map.iter_mut().rev().next()?; + let key = *values.iter().next()?; + let node_id = values.take(&key)?; + if values.is_empty() { + self.map.remove(&depth); + } + Some(node_id) + } +} + +#[test] +fn dirty_nodes() { + let mut dirty_nodes = DirtyNodes::default(); + + dirty_nodes.insert(1, NodeId(1)); + dirty_nodes.insert(0, NodeId(0)); + dirty_nodes.insert(2, NodeId(3)); + dirty_nodes.insert(1, NodeId(2)); + + assert_eq!(dirty_nodes.pop_front(), Some(NodeId(0))); + assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2)))); + assert!(matches!(dirty_nodes.pop_front(), Some(NodeId(1 | 2)))); + assert_eq!(dirty_nodes.pop_front(), Some(NodeId(3))); } type FxDashMap = dashmap::DashMap>; @@ -112,247 +114,248 @@ impl DirtyNodeStates { } } - fn all_dirty(&self, pass_id: PassId) -> impl Iterator + '_ { + fn all_dirty(&self, pass_id: PassId, dirty_nodes: &mut DirtyNodes, tree: &impl TreeView) { let pass_id = pass_id.0; let index = pass_id / 64; let bit = pass_id % 64; let encoded = 1 << bit; - self.dirty.iter().filter_map(move |entry| { + for entry in self.dirty.iter() { let node_id = entry.key(); let dirty = entry.value(); if let Some(atomic) = dirty.get(index as usize) { if atomic.load(Ordering::Relaxed) & encoded != 0 { - Some(*node_id) - } else { - None - } - } else { - None - } - }) - } -} - -fn get_pass>( - passes: &mut Vec>, - resolved_passes: &mut FxHashSet, - dirty_nodes: &DirtyNodeStates, - shared_view: &mut SharedView, - global: &Injector, - current_pass: &mut Option>, -) { - for i in 0..passes.len() { - if passes[i] - .dependancies() - .iter() - .all(|id| resolved_passes.contains(id)) - { - let pass = passes.remove(i); - let pass_id = pass.pass_id(); - println!("Running pass {:?}", pass_id); - resolved_passes.insert(pass_id); - match pass { - AnyPass::Upward(pass) => { - // Upward passes are more difficult. Right now we limit them to only one thread. - let worker = Worker::new_fifo(); - let mut queued_nodes = FxHashSet::default(); - for node in dirty_nodes.all_dirty(pass_id) { - queued_nodes.insert(node); - worker.push(node); - } - while let Some(id) = worker.pop() { - let (node, mut children) = shared_view.parent_child_mut(id).unwrap(); - if pass.pass(node, &mut children) { - drop(children); - if let Some(id) = shared_view.parent_id(id) { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, id); - } - if !queued_nodes.contains(&id) { - queued_nodes.insert(id); - worker.push(id); - } - } - } - } - } - AnyPass::Downward(pass) => { - let mut sorted: Vec<_> = dirty_nodes.all_dirty(pass_id).collect(); - sorted.sort_unstable_by_key(|id| shared_view.height(*id)); - println!( - "Task: {:?} {:?}", - pass_id, - sorted - .iter() - .map(|id| (id, shared_view.height(*id))) - .collect::>() - ); - for node in sorted.into_iter() { - global.push(node); - } - current_pass.replace(AnyPass::Downward(pass)); - } - AnyPass::Node(pass) => { - for node in dirty_nodes.all_dirty(pass_id) { - global.push(node); - } - println!( - "Task: {:?} {:?}", - pass_id, - dirty_nodes.all_dirty(pass_id).collect::>() - ); - current_pass.replace(AnyPass::Node(pass)); + dirty_nodes.insert(tree.height(*node_id).unwrap(), *node_id); } } - return; } } - panic!( - "No pass found with all dependancies resolved in {:?}", - passes.iter().map(|p| p.pass_id()).collect::>() - ); } -pub fn resolve_passes( +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)] +pub struct PassId(u64); + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] +pub struct MemberMask(u64); + +impl MemberMask { + pub fn overlaps(&self, other: Self) -> bool { + (*self & other).0 != 0 + } +} + +impl BitAndAssign for MemberMask { + fn bitand_assign(&mut self, rhs: Self) { + self.0 &= rhs.0; + } +} + +impl BitAnd for MemberMask { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + MemberMask(self.0 & rhs.0) + } +} + +impl BitOrAssign for MemberMask { + fn bitor_assign(&mut self, rhs: Self) { + self.0 |= rhs.0; + } +} + +impl BitOr for MemberMask { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + Self(self.0 | rhs.0) + } +} + +pub struct PassReturn { + progress: bool, + mark_dirty: bool, +} + +pub trait Pass { + fn pass_id(&self) -> PassId; + fn dependancies(&self) -> &'static [PassId]; + fn dependants(&self) -> &'static [PassId]; + fn mask(&self) -> MemberMask; +} + +pub trait UpwardPass: Pass { + fn pass<'a>(&self, node: &mut T, children: &mut dyn Iterator) -> PassReturn; +} + +fn resolve_upward_pass + ?Sized>( tree: &mut impl TreeView, - starting_nodes: FxHashMap>, + pass: &P, + mut dirty: DirtyNodes, + dirty_states: &DirtyNodeStates, +) { + while let Some(id) = dirty.pop_back() { + let (node, mut children) = tree.parent_child_mut(id).unwrap(); + let result = pass.pass(node, &mut children); + drop(children); + if result.progress || result.mark_dirty { + if let Some(id) = tree.parent_id(id) { + if result.mark_dirty { + for dependant in pass.dependants() { + dirty_states.insert(*dependant, id); + } + } + if result.progress { + let height = tree.height(id).unwrap(); + dirty.insert(height, id); + } + } + } + } +} + +pub trait DownwardPass: Pass { + fn pass(&self, node: &mut T, parent: Option<&mut T>) -> PassReturn; +} + +fn resolve_downward_pass + ?Sized>( + tree: &mut impl TreeView, + pass: &P, + mut dirty: DirtyNodes, + dirty_states: &DirtyNodeStates, +) { + while let Some(id) = dirty.pop_front() { + let (node, parent) = tree.node_parent_mut(id).unwrap(); + let result = pass.pass(node, parent); + if result.mark_dirty || result.progress { + for id in tree.children_ids(id).unwrap() { + if result.mark_dirty { + for dependant in pass.dependants() { + dirty_states.insert(*dependant, *id); + } + } + if result.progress { + let height = tree.height(*id).unwrap(); + dirty.insert(height, *id); + } + } + } + } +} + +pub trait NodePass: Pass { + fn pass(&self, node: &mut T) -> bool; +} + +fn resolve_node_pass + ?Sized>( + tree: &mut impl TreeView, + pass: &P, + mut dirty: DirtyNodes, + dirty_states: &DirtyNodeStates, +) { + while let Some(id) = dirty.pop_back() { + let node = tree.get_mut(id).unwrap(); + if pass.pass(node) { + for dependant in pass.dependants() { + dirty_states.insert(*dependant, id); + } + } + } +} + +pub enum AnyPass { + Upward(Box + Send + Sync>), + Downward(Box + Send + Sync>), + Node(Box + Send + Sync>), +} + +impl AnyPass { + fn pass_id(&self) -> PassId { + match self { + Self::Upward(pass) => pass.pass_id(), + Self::Downward(pass) => pass.pass_id(), + Self::Node(pass) => pass.pass_id(), + } + } + + fn dependancies(&self) -> &'static [PassId] { + match self { + Self::Upward(pass) => pass.dependancies(), + Self::Downward(pass) => pass.dependancies(), + Self::Node(pass) => pass.dependancies(), + } + } + + fn mask(&self) -> MemberMask { + match self { + Self::Upward(pass) => pass.mask(), + Self::Downward(pass) => pass.mask(), + Self::Node(pass) => pass.mask(), + } + } + + fn resolve( + &self, + tree: &mut impl TreeView, + dirty: DirtyNodes, + dirty_states: &DirtyNodeStates, + ) { + match self { + Self::Downward(pass) => resolve_downward_pass(tree, pass.as_ref(), dirty, dirty_states), + Self::Upward(pass) => resolve_upward_pass(tree, pass.as_ref(), dirty, dirty_states), + Self::Node(pass) => resolve_node_pass(tree, pass.as_ref(), dirty, dirty_states), + } + } +} + +struct RawPointer(*mut T); +unsafe impl Send for RawPointer {} +unsafe impl Sync for RawPointer {} + +fn resolve_passes>( + tree: &mut Tr, + dirty_nodes: DirtyNodeStates, mut passes: Vec>, ) { - assert!(!passes.is_empty()); - let dirty_nodes: Arc = Arc::new(DirtyNodeStates::new(starting_nodes)); - let global = Injector::default(); - - let core_count = thread::available_parallelism() - .map(|c| c.get()) - .unwrap_or(1); - let workers: Vec> = (0..core_count).map(|_| Worker::new_fifo()).collect(); - let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect(); - let mut shared_view = SharedView::new(tree); + let dirty_states = Arc::new(dirty_nodes); let mut resolved_passes: FxHashSet = FxHashSet::default(); - let current_pass: Arc>>> = Arc::new(RwLock::new(None)); - - thread::scope(|s| { - { - let mut write = current_pass.write(); - get_pass( - &mut passes, - &mut resolved_passes, - &dirty_nodes, - &mut shared_view, - &global, - &mut *write, - ); - } - let global = &global; - let stealers = &stealers; - let mut thread_handles = Vec::new(); - let threads_finished = Arc::new((Mutex::new(0), Condvar::new())); - for (_, w) in (0..core_count).zip(workers.into_iter()) { - let mut shared_view = shared_view.clone(); - let current_pass = current_pass.clone(); - let dirty_nodes = dirty_nodes.clone(); - let threads_finished = threads_finished.clone(); - thread_handles.push(s.spawn(move || { - loop { - let read = current_pass.read(); - if let Some(current_pass) = &*read { - let current_pass_id = current_pass.pass_id(); - match current_pass { - AnyPass::Upward(_) => { - todo!("Upward passes are single threaded") - } - AnyPass::Node(pass) => { - // Node passes are the easiest to parallelize. We just run the pass on each node. - while let Some(id) = find_task(&w, global, stealers) { - let node = shared_view.get_mut(id).unwrap(); - if pass.pass(node) { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, id); - } - } - } - } - AnyPass::Downward(pass) => { - // Downward passes are easy to parallelize. We try to keep trees localized to one thread, but allow work stealing to balance the load. - while let Some(id) = find_task(&w, global, stealers) { - let (node, parent) = shared_view.node_parent_mut(id).unwrap(); - if pass.pass(node, parent) { - for id in shared_view.children_ids(id).unwrap() { - for dependant in pass.dependants() { - dirty_nodes.insert(*dependant, *id); - } - if !dirty_nodes.get(current_pass_id, *id) { - w.push(*id); - } - } - } - } - } - } - // unblock the rwlock - drop(read); - { - let (count, cvar) = &*threads_finished; - let mut lock = count.lock(); - *lock += 1; - if *lock == core_count { - cvar.notify_all(); - } - } - // wait for the main thread to pick the next pass - thread::park(); - } else { - break; - } + let mut resolving = Vec::new(); + while !passes.is_empty() { + let mut currently_borrowed = MemberMask::default(); + std::thread::scope(|s| { + let mut i = 0; + while i < passes.len() { + let pass = &passes[i]; + let pass_id = pass.pass_id(); + let pass_mask = pass.mask(); + if pass + .dependancies() + .iter() + .all(|d| resolved_passes.contains(d)) + && !pass_mask.overlaps(currently_borrowed) + { + let pass = passes.remove(i); + resolving.push(pass_id); + currently_borrowed |= pass_mask; + let tree_mut = tree as *mut _; + let raw_ptr = RawPointer(tree_mut); + let dirty_states = dirty_states.clone(); + s.spawn(move || unsafe { + // let tree_mut: &mut Tr = &mut *raw_ptr.0; + let raw = raw_ptr; + let tree_mut: &mut Tr = &mut *raw.0; + let mut dirty = DirtyNodes::default(); + dirty_states.all_dirty(pass_id, &mut dirty, tree_mut); + pass.resolve(tree_mut, dirty, &dirty_states); + }); + } else { + i += 1; } - })); - } - let wait_for_thread_to_finish = move || { - // this will block until all threads are done with the current pass - let (count, cvar) = &*threads_finished; - let mut count = count.lock(); - cvar.wait(&mut count); - }; - while !passes.is_empty() { - wait_for_thread_to_finish(); - let mut write = current_pass.write(); - println!("Threads finished"); - get_pass( - &mut passes, - &mut resolved_passes, - &dirty_nodes, - &mut shared_view, - global, - &mut *write, - ); - // notify all threads to start the next pass - for thread in &thread_handles { - thread.thread().unpark(); } - } - *current_pass.write() = None; - for thread in &thread_handles { - thread.thread().unpark(); - } - }); -} - -fn find_task(local: &Worker, global: &Injector, stealers: &[Stealer]) -> Option { - // Pop a task from the local queue, if not empty. - local.pop().or_else(|| { - // Otherwise, we need to look for a task elsewhere. - std::iter::repeat_with(|| { - // Try stealing a batch of tasks from the global queue. - global - .steal_batch_and_pop(local) - // Or try stealing a task from one of the other threads. - .or_else(|| stealers.iter().map(|s| s.steal()).collect()) - }) - // Loop while no task was stolen and any steal operation needs to be retried. - .find(|s| !s.is_retry()) - // Extract the stolen task, if there is one. - .and_then(|s| s.success()) - }) + // all passes are resolved at the end of the scope + }); + resolved_passes.extend(resolving.iter().copied()); + resolving.clear() + } } #[test] @@ -371,8 +374,7 @@ fn node_pass() { println!("{:#?}", tree); struct AddPass; - - impl NodePass for AddPass { + impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } @@ -385,6 +387,12 @@ fn node_pass() { &[] } + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + + impl NodePass for AddPass { fn pass(&self, node: &mut i32) -> bool { *node += 1; true @@ -392,8 +400,8 @@ fn node_pass() { } let passes = vec![AnyPass::Node(Box::new(AddPass))]; - let mut dirty_nodes: FxHashMap> = FxHashMap::default(); - dirty_nodes.insert(tree.root(), [PassId(0)].into_iter().collect()); + let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(0), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); assert_eq!(tree.get(tree.root()).unwrap(), &1); @@ -414,8 +422,7 @@ fn dependant_node_pass() { tree.add_child(child2, grandchild2); struct AddPass; - - impl NodePass for AddPass { + impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } @@ -428,6 +435,12 @@ fn dependant_node_pass() { &[] } + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + + impl NodePass for AddPass { fn pass(&self, node: &mut i32) -> bool { println!("AddPass: {}", node); *node += 1; @@ -437,7 +450,7 @@ fn dependant_node_pass() { struct SubtractPass; - impl NodePass for SubtractPass { + impl Pass for SubtractPass { fn pass_id(&self) -> PassId { PassId(1) } @@ -450,6 +463,11 @@ fn dependant_node_pass() { &[PassId(0)] } + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl NodePass for SubtractPass { fn pass(&self, node: &mut i32) -> bool { println!("SubtractPass: {}", node); *node -= 1; @@ -461,8 +479,8 @@ fn dependant_node_pass() { AnyPass::Node(Box::new(AddPass)), AnyPass::Node(Box::new(SubtractPass)), ]; - let mut dirty_nodes: FxHashMap> = FxHashMap::default(); - dirty_nodes.insert(tree.root(), [PassId(1)].into_iter().collect()); + let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); assert_eq!(*tree.get(tree.root()).unwrap(), 0); @@ -484,7 +502,7 @@ fn down_pass() { struct AddPass; - impl DownwardPass for AddPass { + impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } @@ -497,17 +515,25 @@ fn down_pass() { &[] } - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl DownwardPass for AddPass { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { if let Some(parent) = parent { *node += *parent; } - true + PassReturn { + progress: true, + mark_dirty: true, + } } } let passes = vec![AnyPass::Downward(Box::new(AddPass))]; - let mut dirty_nodes: FxHashMap> = FxHashMap::default(); - dirty_nodes.insert(tree.root(), [PassId(0)].into_iter().collect()); + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(0), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); assert_eq!(tree.get(tree.root()).unwrap(), &1); @@ -537,8 +563,7 @@ fn dependant_down_pass() { tree.add_child(child2, grandchild2); struct AddPass; - - impl DownwardPass for AddPass { + impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } @@ -551,20 +576,27 @@ fn dependant_down_pass() { &[] } - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl DownwardPass for AddPass { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { if let Some(parent) = parent { println!("AddPass: {} -> {}", node, *node + *parent); *node += *parent; } else { println!("AddPass: {}", node); } - true + PassReturn { + progress: true, + mark_dirty: true, + } } } struct SubtractPass; - - impl DownwardPass for SubtractPass { + impl Pass for SubtractPass { fn pass_id(&self) -> PassId { PassId(1) } @@ -577,14 +609,22 @@ fn dependant_down_pass() { &[PassId(0)] } - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> bool { + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl DownwardPass for SubtractPass { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { if let Some(parent) = parent { println!("SubtractPass: {} -> {}", node, *node - *parent); *node -= *parent; } else { println!("SubtractPass: {}", node); } - true + PassReturn { + progress: true, + mark_dirty: true, + } } } @@ -592,8 +632,8 @@ fn dependant_down_pass() { AnyPass::Downward(Box::new(AddPass)), AnyPass::Downward(Box::new(SubtractPass)), ]; - let mut dirty_nodes: FxHashMap> = FxHashMap::default(); - dirty_nodes.insert(tree.root(), [PassId(1)].into_iter().collect()); + let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); // Tree before: @@ -648,8 +688,7 @@ fn up_pass() { tree.add_child(child2, grandchild2); struct AddPass; - - impl UpwardPass for AddPass { + impl Pass for AddPass { fn pass_id(&self) -> PassId { PassId(0) } @@ -662,20 +701,28 @@ fn up_pass() { &[] } + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl UpwardPass for AddPass { fn pass<'a>( &self, node: &mut i32, children: &mut dyn Iterator, - ) -> bool { + ) -> PassReturn { *node += children.map(|i| *i).sum::(); - true + PassReturn { + progress: true, + mark_dirty: true, + } } } let passes = vec![AnyPass::Upward(Box::new(AddPass))]; - let mut dirty_nodes: FxHashMap> = FxHashMap::default(); - dirty_nodes.insert(grandchild1, [PassId(0)].into_iter().collect()); - dirty_nodes.insert(grandchild2, [PassId(0)].into_iter().collect()); + let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(0), grandchild1); + dirty_nodes.insert(PassId(0), grandchild2); resolve_passes(&mut tree, dirty_nodes, passes); assert_eq!(tree.get(tree.root()).unwrap(), &2); From bb3be32b1958e8cabc0afb2262633ea151c7427d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 26 Nov 2022 11:51:52 -0600 Subject: [PATCH 14/43] fix some warnings --- packages/native-core/src/passes.rs | 37 ++++++++---------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index b08a65cf5..99dec696d 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -1,9 +1,8 @@ -use crossbeam_deque::{Injector, Stealer, Worker}; use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use std::hash::BuildHasherDefault; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; -use std::os::raw; + use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; @@ -61,12 +60,12 @@ fn dirty_nodes() { type FxDashMap = dashmap::DashMap>; #[derive(Default)] -struct DirtyNodeStates { +pub struct DirtyNodeStates { dirty: FxDashMap>, } impl DirtyNodeStates { - fn new(starting_nodes: FxHashMap>) -> Self { + pub fn new(starting_nodes: FxHashMap>) -> Self { let this = Self::default(); for (node, nodes) in starting_nodes { for pass_id in nodes { @@ -76,7 +75,7 @@ impl DirtyNodeStates { this } - fn insert(&self, pass_id: PassId, node_id: NodeId) { + pub fn insert(&self, pass_id: PassId, node_id: NodeId) { let pass_id = pass_id.0; let index = pass_id / 64; let bit = pass_id % 64; @@ -98,22 +97,6 @@ impl DirtyNodeStates { } } - fn get(&self, pass_id: PassId, node_id: NodeId) -> bool { - let pass_id = pass_id.0; - let index = pass_id / 64; - let bit = pass_id % 64; - let encoded = 1 << bit; - if let Some(dirty) = self.dirty.get(&node_id) { - if let Some(atomic) = dirty.get(index as usize) { - atomic.load(Ordering::SeqCst) & encoded != 0 - } else { - false - } - } else { - false - } - } - fn all_dirty(&self, pass_id: PassId, dirty_nodes: &mut DirtyNodes, tree: &impl TreeView) { let pass_id = pass_id.0; let index = pass_id / 64; @@ -311,7 +294,7 @@ struct RawPointer(*mut T); unsafe impl Send for RawPointer {} unsafe impl Sync for RawPointer {} -fn resolve_passes>( +pub fn resolve_passes>( tree: &mut Tr, dirty_nodes: DirtyNodeStates, mut passes: Vec>, @@ -400,7 +383,7 @@ fn node_pass() { } let passes = vec![AnyPass::Node(Box::new(AddPass))]; - let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); @@ -479,7 +462,7 @@ fn dependant_node_pass() { AnyPass::Node(Box::new(AddPass)), AnyPass::Node(Box::new(SubtractPass)), ]; - let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); @@ -632,7 +615,7 @@ fn dependant_down_pass() { AnyPass::Downward(Box::new(AddPass)), AnyPass::Downward(Box::new(SubtractPass)), ]; - let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); resolve_passes(&mut tree, dirty_nodes, passes); @@ -720,7 +703,7 @@ fn up_pass() { } let passes = vec![AnyPass::Upward(Box::new(AddPass))]; - let mut dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), grandchild1); dirty_nodes.insert(PassId(0), grandchild2); resolve_passes(&mut tree, dirty_nodes, passes); From 42888f0cbc05045d2129c16dde48a9b50942d4b4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 26 Nov 2022 12:13:37 -0600 Subject: [PATCH 15/43] add up dependant test --- packages/native-core/src/passes.rs | 117 +++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 99dec696d..a21d6920a 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -714,3 +714,120 @@ fn up_pass() { assert_eq!(tree.get(child2).unwrap(), &1); assert_eq!(tree.get(grandchild2).unwrap(), &1); } + +#[test] +fn dependant_up_pass() { + use crate::tree::{Tree, TreeLike}; + // 0 + let mut tree = Tree::new(0); + let parent = tree.root(); + // 1 + let child1 = tree.create_node(0); + tree.add_child(parent, child1); + // 2 + let grandchild1 = tree.create_node(1); + tree.add_child(child1, grandchild1); + // 3 + let child2 = tree.create_node(0); + tree.add_child(parent, child2); + // 4 + let grandchild2 = tree.create_node(1); + tree.add_child(child2, grandchild2); + + struct AddPass; + impl Pass for AddPass { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[PassId(1)] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl UpwardPass for AddPass { + fn pass<'a>( + &self, + node: &mut i32, + children: &mut dyn Iterator, + ) -> PassReturn { + *node += children.map(|i| *i).sum::(); + PassReturn { + progress: true, + mark_dirty: true, + } + } + } + + struct SubtractPass; + impl Pass for SubtractPass { + fn pass_id(&self) -> PassId { + PassId(1) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[PassId(0)] + } + + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + impl UpwardPass for SubtractPass { + fn pass<'a>( + &self, + node: &mut i32, + children: &mut dyn Iterator, + ) -> PassReturn { + *node -= children.map(|i| *i).sum::(); + PassReturn { + progress: true, + mark_dirty: true, + } + } + } + + let passes = vec![ + AnyPass::Upward(Box::new(AddPass)), + AnyPass::Upward(Box::new(SubtractPass)), + ]; + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(1), grandchild1); + dirty_nodes.insert(PassId(1), grandchild2); + resolve_passes(&mut tree, dirty_nodes, passes); + + // Tree before: + // 0=\ + // 0=\ + // 1 + // 0=\ + // 1 + // Tree after subtract: + // 2=\ + // -1=\ + // 1 + // -1=\ + // 1 + // Tree after add: + // 2=\ + // 0=\ + // 1 + // 0=\ + // 1 + assert_eq!(tree.get(tree.root()).unwrap(), &2); + assert_eq!(tree.get(child1).unwrap(), &0); + assert_eq!(tree.get(grandchild1).unwrap(), &1); + assert_eq!(tree.get(child2).unwrap(), &0); + assert_eq!(tree.get(grandchild2).unwrap(), &1); +} From 4812b5124a4e243c7961ade510e46c6fc1cbc1fb Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 26 Nov 2022 12:18:23 -0600 Subject: [PATCH 16/43] clean up examples --- packages/native-core/src/passes.rs | 104 +++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index a21d6920a..6c8582170 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -346,14 +346,6 @@ fn node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(0); let parent = tree.root(); - let child1 = tree.create_node(1); - tree.add_child(parent, child1); - let grandchild1 = tree.create_node(3); - tree.add_child(child1, grandchild1); - let child2 = tree.create_node(2); - tree.add_child(parent, child2); - let grandchild2 = tree.create_node(4); - tree.add_child(child2, grandchild2); println!("{:#?}", tree); struct AddPass; @@ -371,7 +363,7 @@ fn node_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } @@ -394,15 +386,6 @@ fn node_pass() { fn dependant_node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(0); - let parent = tree.root(); - let child1 = tree.create_node(1); - tree.add_child(parent, child1); - let grandchild1 = tree.create_node(3); - tree.add_child(child1, grandchild1); - let child2 = tree.create_node(2); - tree.add_child(parent, child2); - let grandchild2 = tree.create_node(4); - tree.add_child(child2, grandchild2); struct AddPass; impl Pass for AddPass { @@ -419,7 +402,7 @@ fn dependant_node_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } @@ -447,7 +430,7 @@ fn dependant_node_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl NodePass for SubtractPass { @@ -469,6 +452,75 @@ fn dependant_node_pass() { assert_eq!(*tree.get(tree.root()).unwrap(), 0); } +#[test] +fn independant_node_pass() { + use crate::tree::{Tree, TreeLike}; + let mut tree = Tree::new((0, 0)); + + struct AddPass1; + impl Pass for AddPass1 { + fn pass_id(&self) -> PassId { + PassId(0) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn mask(&self) -> MemberMask { + MemberMask(0) + } + } + + impl NodePass<(i32, i32)> for AddPass1 { + fn pass(&self, node: &mut (i32, i32)) -> bool { + node.0 += 1; + true + } + } + + struct AddPass2; + impl Pass for AddPass2 { + fn pass_id(&self) -> PassId { + PassId(1) + } + + fn dependancies(&self) -> &'static [PassId] { + &[] + } + + fn dependants(&self) -> &'static [PassId] { + &[] + } + + fn mask(&self) -> MemberMask { + MemberMask(1) + } + } + + impl NodePass<(i32, i32)> for AddPass2 { + fn pass(&self, node: &mut (i32, i32)) -> bool { + node.1 += 1; + true + } + } + + let passes = vec![ + AnyPass::Node(Box::new(AddPass1)), + AnyPass::Node(Box::new(AddPass2)), + ]; + let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); + dirty_nodes.insert(PassId(0), tree.root()); + dirty_nodes.insert(PassId(1), tree.root()); + resolve_passes(&mut tree, dirty_nodes, passes); + + assert_eq!(tree.get(tree.root()).unwrap(), &(1, 1)); +} + #[test] fn down_pass() { use crate::tree::{Tree, TreeLike}; @@ -499,7 +551,7 @@ fn down_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl DownwardPass for AddPass { @@ -560,7 +612,7 @@ fn dependant_down_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl DownwardPass for AddPass { @@ -593,7 +645,7 @@ fn dependant_down_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl DownwardPass for SubtractPass { @@ -685,7 +737,7 @@ fn up_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl UpwardPass for AddPass { @@ -749,7 +801,7 @@ fn dependant_up_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl UpwardPass for AddPass { @@ -781,7 +833,7 @@ fn dependant_up_pass() { } fn mask(&self) -> MemberMask { - MemberMask(1) + MemberMask(0) } } impl UpwardPass for SubtractPass { From d78af904e63132848d39be49f1879c5fad383ebc Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 26 Nov 2022 15:25:02 -0600 Subject: [PATCH 17/43] work on intigrating state with passes --- packages/native-core/src/lib.rs | 8 +- packages/native-core/src/passes.rs | 153 +++++++++++++++------------ packages/native-core/src/real_dom.rs | 48 ++++----- packages/native-core/src/state.rs | 32 +++--- packages/native-core/src/tree.rs | 4 + 5 files changed, 137 insertions(+), 108 deletions(-) diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 56c4a6a73..14089c669 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -1,3 +1,6 @@ +use std::hash::BuildHasherDefault; + +use rustc_hash::FxHasher; use tree::NodeId; pub mod layout_attributes; @@ -10,7 +13,10 @@ pub mod tree; pub mod utils; /// A id for a node that lives in the real dom. -type RealNodeId = NodeId; +pub type RealNodeId = NodeId; +pub(crate) type FxDashMap = dashmap::DashMap>; +pub(crate) type FxDashSet = dashmap::DashSet>; +pub(crate) type SendAnyMap = anymap::Map; /// Used in derived state macros #[derive(Eq, PartialEq)] diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 6c8582170..86bb50537 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -1,13 +1,11 @@ -use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; +use crate::tree::{NodeId, TreeView}; +use crate::{FxDashMap, FxDashSet, SendAnyMap}; +use rustc_hash::{FxHashMap, FxHashSet}; use std::collections::BTreeMap; -use std::hash::BuildHasherDefault; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign}; - use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; -use crate::tree::{NodeId, TreeView}; - #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct DirtyNodes { map: BTreeMap>, @@ -57,8 +55,6 @@ fn dirty_nodes() { assert_eq!(dirty_nodes.pop_front(), Some(NodeId(3))); } -type FxDashMap = dashmap::DashMap>; - #[derive(Default)] pub struct DirtyNodeStates { dirty: FxDashMap>, @@ -167,7 +163,12 @@ pub trait Pass { } pub trait UpwardPass: Pass { - fn pass<'a>(&self, node: &mut T, children: &mut dyn Iterator) -> PassReturn; + fn pass<'a>( + &self, + node: &mut T, + children: &mut dyn Iterator, + ctx: &SendAnyMap, + ) -> PassReturn; } fn resolve_upward_pass + ?Sized>( @@ -175,12 +176,15 @@ fn resolve_upward_pass + ?Sized>( pass: &P, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { while let Some(id) = dirty.pop_back() { let (node, mut children) = tree.parent_child_mut(id).unwrap(); - let result = pass.pass(node, &mut children); + let result = pass.pass(node, &mut children, ctx); drop(children); if result.progress || result.mark_dirty { + nodes_updated.insert(id); if let Some(id) = tree.parent_id(id) { if result.mark_dirty { for dependant in pass.dependants() { @@ -197,7 +201,7 @@ fn resolve_upward_pass + ?Sized>( } pub trait DownwardPass: Pass { - fn pass(&self, node: &mut T, parent: Option<&mut T>) -> PassReturn; + fn pass(&self, node: &mut T, parent: Option<&mut T>, ctx: &SendAnyMap) -> PassReturn; } fn resolve_downward_pass + ?Sized>( @@ -205,11 +209,14 @@ fn resolve_downward_pass + ?Sized>( pass: &P, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { while let Some(id) = dirty.pop_front() { let (node, parent) = tree.node_parent_mut(id).unwrap(); - let result = pass.pass(node, parent); + let result = pass.pass(node, parent, ctx); if result.mark_dirty || result.progress { + nodes_updated.insert(id); for id in tree.children_ids(id).unwrap() { if result.mark_dirty { for dependant in pass.dependants() { @@ -226,7 +233,7 @@ fn resolve_downward_pass + ?Sized>( } pub trait NodePass: Pass { - fn pass(&self, node: &mut T) -> bool; + fn pass(&self, node: &mut T, ctx: &SendAnyMap) -> bool; } fn resolve_node_pass + ?Sized>( @@ -234,10 +241,13 @@ fn resolve_node_pass + ?Sized>( pass: &P, mut dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { while let Some(id) = dirty.pop_back() { let node = tree.get_mut(id).unwrap(); - if pass.pass(node) { + if pass.pass(node, ctx) { + nodes_updated.insert(id); for dependant in pass.dependants() { dirty_states.insert(*dependant, id); } @@ -245,14 +255,14 @@ fn resolve_node_pass + ?Sized>( } } -pub enum AnyPass { - Upward(Box + Send + Sync>), - Downward(Box + Send + Sync>), - Node(Box + Send + Sync>), +pub enum AnyPass { + Upward(&'static (dyn UpwardPass + Send + Sync + 'static)), + Downward(&'static (dyn DownwardPass + Send + Sync + 'static)), + Node(&'static (dyn NodePass + Send + Sync + 'static)), } impl AnyPass { - fn pass_id(&self) -> PassId { + pub fn pass_id(&self) -> PassId { match self { Self::Upward(pass) => pass.pass_id(), Self::Downward(pass) => pass.pass_id(), @@ -260,7 +270,7 @@ impl AnyPass { } } - fn dependancies(&self) -> &'static [PassId] { + pub fn dependancies(&self) -> &'static [PassId] { match self { Self::Upward(pass) => pass.dependancies(), Self::Downward(pass) => pass.dependancies(), @@ -281,11 +291,19 @@ impl AnyPass { tree: &mut impl TreeView, dirty: DirtyNodes, dirty_states: &DirtyNodeStates, + nodes_updated: &FxDashSet, + ctx: &SendAnyMap, ) { match self { - Self::Downward(pass) => resolve_downward_pass(tree, pass.as_ref(), dirty, dirty_states), - Self::Upward(pass) => resolve_upward_pass(tree, pass.as_ref(), dirty, dirty_states), - Self::Node(pass) => resolve_node_pass(tree, pass.as_ref(), dirty, dirty_states), + Self::Downward(pass) => { + resolve_downward_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx) + } + Self::Upward(pass) => { + resolve_upward_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx) + } + Self::Node(pass) => { + resolve_node_pass(tree, *pass, dirty, dirty_states, nodes_updated, ctx) + } } } } @@ -297,11 +315,14 @@ unsafe impl Sync for RawPointer {} pub fn resolve_passes>( tree: &mut Tr, dirty_nodes: DirtyNodeStates, - mut passes: Vec>, -) { + mut passes: Vec<&AnyPass>, + ctx: SendAnyMap, +) -> FxDashSet { let dirty_states = Arc::new(dirty_nodes); let mut resolved_passes: FxHashSet = FxHashSet::default(); let mut resolving = Vec::new(); + let nodes_updated = Arc::new(FxDashSet::default()); + let ctx = Arc::new(ctx); while !passes.is_empty() { let mut currently_borrowed = MemberMask::default(); std::thread::scope(|s| { @@ -322,13 +343,16 @@ pub fn resolve_passes>( let tree_mut = tree as *mut _; let raw_ptr = RawPointer(tree_mut); let dirty_states = dirty_states.clone(); + let nodes_updated = nodes_updated.clone(); + let ctx = ctx.clone(); s.spawn(move || unsafe { // let tree_mut: &mut Tr = &mut *raw_ptr.0; let raw = raw_ptr; + // this is safe because the member_mask acts as a per-member mutex and we have verified that the pass does not overlap with any other pass let tree_mut: &mut Tr = &mut *raw.0; let mut dirty = DirtyNodes::default(); dirty_states.all_dirty(pass_id, &mut dirty, tree_mut); - pass.resolve(tree_mut, dirty, &dirty_states); + pass.resolve(tree_mut, dirty, &dirty_states, &nodes_updated, &ctx); }); } else { i += 1; @@ -339,14 +363,13 @@ pub fn resolve_passes>( resolved_passes.extend(resolving.iter().copied()); resolving.clear() } + std::sync::Arc::try_unwrap(nodes_updated).unwrap() } #[test] fn node_pass() { use crate::tree::{Tree, TreeLike}; let mut tree = Tree::new(0); - let parent = tree.root(); - println!("{:#?}", tree); struct AddPass; impl Pass for AddPass { @@ -368,16 +391,17 @@ fn node_pass() { } impl NodePass for AddPass { - fn pass(&self, node: &mut i32) -> bool { + fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node += 1; true } } - let passes = vec![AnyPass::Node(Box::new(AddPass))]; + let add_pass = AnyPass::Node(&AddPass); + let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &1); } @@ -407,8 +431,7 @@ fn dependant_node_pass() { } impl NodePass for AddPass { - fn pass(&self, node: &mut i32) -> bool { - println!("AddPass: {}", node); + fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node += 1; true } @@ -434,20 +457,18 @@ fn dependant_node_pass() { } } impl NodePass for SubtractPass { - fn pass(&self, node: &mut i32) -> bool { - println!("SubtractPass: {}", node); + fn pass(&self, node: &mut i32, _: &SendAnyMap) -> bool { *node -= 1; true } } - let passes = vec![ - AnyPass::Node(Box::new(AddPass)), - AnyPass::Node(Box::new(SubtractPass)), - ]; + let add_pass = AnyPass::Node(&AddPass); + let subtract_pass = AnyPass::Node(&SubtractPass); + let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(*tree.get(tree.root()).unwrap(), 0); } @@ -477,7 +498,7 @@ fn independant_node_pass() { } impl NodePass<(i32, i32)> for AddPass1 { - fn pass(&self, node: &mut (i32, i32)) -> bool { + fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool { node.0 += 1; true } @@ -503,20 +524,19 @@ fn independant_node_pass() { } impl NodePass<(i32, i32)> for AddPass2 { - fn pass(&self, node: &mut (i32, i32)) -> bool { + fn pass(&self, node: &mut (i32, i32), _: &SendAnyMap) -> bool { node.1 += 1; true } } - let passes = vec![ - AnyPass::Node(Box::new(AddPass1)), - AnyPass::Node(Box::new(AddPass2)), - ]; + let add_pass1 = AnyPass::Node(&AddPass1); + let add_pass2 = AnyPass::Node(&AddPass2); + let passes = vec![&add_pass1, &add_pass2]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); dirty_nodes.insert(PassId(1), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &(1, 1)); } @@ -555,7 +575,7 @@ fn down_pass() { } } impl DownwardPass for AddPass { - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { *node += *parent; } @@ -566,10 +586,11 @@ fn down_pass() { } } - let passes = vec![AnyPass::Downward(Box::new(AddPass))]; + let add_pass = AnyPass::Downward(&AddPass); + let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &1); assert_eq!(tree.get(child1).unwrap(), &2); @@ -616,12 +637,10 @@ fn dependant_down_pass() { } } impl DownwardPass for AddPass { - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { - println!("AddPass: {} -> {}", node, *node + *parent); *node += *parent; } else { - println!("AddPass: {}", node); } PassReturn { progress: true, @@ -649,12 +668,10 @@ fn dependant_down_pass() { } } impl DownwardPass for SubtractPass { - fn pass(&self, node: &mut i32, parent: Option<&mut i32>) -> PassReturn { + fn pass(&self, node: &mut i32, parent: Option<&mut i32>, _: &SendAnyMap) -> PassReturn { if let Some(parent) = parent { - println!("SubtractPass: {} -> {}", node, *node - *parent); *node -= *parent; } else { - println!("SubtractPass: {}", node); } PassReturn { progress: true, @@ -663,13 +680,12 @@ fn dependant_down_pass() { } } - let passes = vec![ - AnyPass::Downward(Box::new(AddPass)), - AnyPass::Downward(Box::new(SubtractPass)), - ]; + let add_pass = AnyPass::Downward(&AddPass); + let subtract_pass = AnyPass::Downward(&SubtractPass); + let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), tree.root()); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); // Tree before: // 1=\ @@ -745,6 +761,7 @@ fn up_pass() { &self, node: &mut i32, children: &mut dyn Iterator, + _: &SendAnyMap, ) -> PassReturn { *node += children.map(|i| *i).sum::(); PassReturn { @@ -754,11 +771,12 @@ fn up_pass() { } } - let passes = vec![AnyPass::Upward(Box::new(AddPass))]; + let add_pass = AnyPass::Upward(&AddPass); + let passes = vec![&add_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(0), grandchild1); dirty_nodes.insert(PassId(0), grandchild2); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); assert_eq!(tree.get(tree.root()).unwrap(), &2); assert_eq!(tree.get(child1).unwrap(), &1); @@ -809,6 +827,7 @@ fn dependant_up_pass() { &self, node: &mut i32, children: &mut dyn Iterator, + _: &SendAnyMap, ) -> PassReturn { *node += children.map(|i| *i).sum::(); PassReturn { @@ -841,6 +860,7 @@ fn dependant_up_pass() { &self, node: &mut i32, children: &mut dyn Iterator, + _: &SendAnyMap, ) -> PassReturn { *node -= children.map(|i| *i).sum::(); PassReturn { @@ -850,14 +870,13 @@ fn dependant_up_pass() { } } - let passes = vec![ - AnyPass::Upward(Box::new(AddPass)), - AnyPass::Upward(Box::new(SubtractPass)), - ]; + let add_pass = AnyPass::Upward(&AddPass); + let subtract_pass = AnyPass::Upward(&SubtractPass); + let passes = vec![&add_pass, &subtract_pass]; let dirty_nodes: DirtyNodeStates = DirtyNodeStates::default(); dirty_nodes.insert(PassId(1), grandchild1); dirty_nodes.insert(PassId(1), grandchild2); - resolve_passes(&mut tree, dirty_nodes, passes); + resolve_passes(&mut tree, dirty_nodes, passes, SendAnyMap::new()); // Tree before: // 0=\ diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 683d3bb65..48c1febcc 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,13 +1,13 @@ -use anymap::AnyMap; use dioxus_core::{ElementId, Mutations}; use rustc_hash::{FxHashMap, FxHashSet}; use std::ops::{Index, IndexMut}; -use crate::node::{Node, NodeData, NodeType, OwnedAttributeDiscription}; +use crate::node::{Node, NodeType, OwnedAttributeDiscription}; use crate::node_ref::{AttributeMask, NodeMask}; +use crate::passes::DirtyNodeStates; use crate::state::State; use crate::tree::{NodeId, Tree, TreeLike, TreeView}; -use crate::RealNodeId; +use crate::{FxDashSet, RealNodeId, SendAnyMap}; fn mark_dirty( node_id: NodeId, @@ -96,10 +96,7 @@ impl RealDom { } /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. - pub fn apply_mutations( - &mut self, - mutations_vec: Vec, - ) -> FxHashMap { + pub fn apply_mutations(&mut self, mutations_vec: Vec) -> DirtyNodeStates { let mut nodes_updated: FxHashMap = FxHashMap::default(); for mutations in mutations_vec { for e in mutations.edits { @@ -346,20 +343,28 @@ impl RealDom { } } - // remove any nodes that were created and then removed in the same mutations from the dirty nodes list - nodes_updated.retain(|k, _| self.tree.get(*k).is_some()); + let dirty_nodes = DirtyNodeStates::default(); + for (n, mask) in nodes_updated { + // remove any nodes that were created and then removed in the same mutations from the dirty nodes list + if self.tree.contains(n) { + for (m, p) in S::MASKS.iter().zip(S::PASSES.iter()) { + if mask.overlaps(m) { + dirty_nodes.insert(p.pass_id(), n); + } + } + } + } - nodes_updated + dirty_nodes } /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts. pub fn update_state( &mut self, - nodes_updated: FxHashMap, - ctx: AnyMap, - ) -> FxHashSet { - let (mut state_tree, node_tree) = self.split(); - S::update(&nodes_updated, &mut state_tree, &node_tree, &ctx) + nodes_updated: DirtyNodeStates, + ctx: SendAnyMap, + ) -> FxDashSet { + S::update(nodes_updated, &mut self.tree, ctx) } /// Find all nodes that are listening for an event, sorted by there height in the dom progressing starting at the bottom and progressing up. @@ -389,19 +394,6 @@ impl RealDom { self.tree.root() } - pub fn split(&mut self) -> (impl TreeView + '_, impl TreeView + '_) { - let raw = self as *mut Self; - // this is safe beacuse the treeview trait does not allow mutation of the position of elements, and within elements the access is disjoint. - ( - unsafe { &mut *raw } - .tree - .map(|n| &n.state, |n| &mut n.state), - unsafe { &mut *raw } - .tree - .map(|n| &n.node_data, |n| &mut n.node_data), - ) - } - fn clone_node(&mut self, node_id: NodeId) -> RealNodeId { let node = self.tree.get(node_id).unwrap(); let new_node = node.clone(); diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index 18d405ed7..09ae4333b 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -1,11 +1,10 @@ use std::cmp::Ordering; -use crate::node::NodeData; +use crate::node::Node; use crate::node_ref::{NodeMask, NodeView}; +use crate::passes::{resolve_passes, AnyPass, DirtyNodeStates}; use crate::tree::TreeView; -use crate::RealNodeId; -use anymap::AnyMap; -use rustc_hash::{FxHashMap, FxHashSet}; +use crate::{FxDashSet, RealNodeId, SendAnyMap}; /// Join two sorted iterators pub(crate) fn union_ordered_iter<'a>( @@ -204,20 +203,29 @@ pub trait NodeDepState { fn reduce<'a>( &mut self, node: NodeView, - siblings: ::ElementBorrowed<'a>, + node_state: ::ElementBorrowed<'a>, ctx: &Self::Ctx, ) -> bool; } /// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom]. -pub trait State: Default + Clone { +pub trait State: Default + Clone + 'static { #[doc(hidden)] - fn update<'a, T: TreeView, T2: TreeView>( - dirty: &FxHashMap, - state_tree: &'a mut T, - rdom: &'a T2, - ctx: &AnyMap, - ) -> FxHashSet; + const PASSES: &'static [AnyPass>]; + #[doc(hidden)] + const MASKS: &'static [NodeMask]; + + #[doc(hidden)] + fn update>>( + dirty: DirtyNodeStates, + tree: &mut T, + ctx: SendAnyMap, + ) -> FxDashSet { + let set = FxDashSet::default(); + let passes = Self::PASSES.iter().collect(); + resolve_passes(tree, dirty, passes, ctx); + set + } } impl ChildDepState for () { diff --git a/packages/native-core/src/tree.rs b/packages/native-core/src/tree.rs index 53f81ba54..0547fc3a9 100644 --- a/packages/native-core/src/tree.rs +++ b/packages/native-core/src/tree.rs @@ -73,6 +73,10 @@ pub trait TreeView: Sized { fn root(&self) -> NodeId; + fn contains(&self, id: NodeId) -> bool { + self.get(id).is_some() + } + fn get(&self, id: NodeId) -> Option<&T>; fn get_unchecked(&self, id: NodeId) -> &T { From 0d780fee611aeb5c2a81186c22d88797d9c8a2b5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 3 Dec 2022 18:28:25 -0600 Subject: [PATCH 18/43] update to new mutations --- packages/native-core/src/real_dom.rs | 176 +++++++++++++-------------- 1 file changed, 83 insertions(+), 93 deletions(-) diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 48c1febcc..b9ca35c39 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,8 +1,8 @@ -use dioxus_core::{ElementId, Mutations}; +use dioxus_core::{ElementId, Mutations, TemplateNode}; use rustc_hash::{FxHashMap, FxHashSet}; use std::ops::{Index, IndexMut}; -use crate::node::{Node, NodeType, OwnedAttributeDiscription}; +use crate::node::{Node, NodeType, OwnedAttributeDiscription, OwnedAttributeValue}; use crate::node_ref::{AttributeMask, NodeMask}; use crate::passes::DirtyNodeStates; use crate::state::State; @@ -95,46 +95,89 @@ impl RealDom { self.tree.add_child(node_id, child_id); } + fn create_template_node( + &mut self, + node: &TemplateNode, + mutations_vec: &mut FxHashMap, + ) -> Option { + match node { + TemplateNode::Element { + tag, + namespace, + attrs, + children, + } => { + let node = Node::new(NodeType::Element { + tag: tag.to_string(), + namespace: namespace.map(|s| s.to_string()), + attributes: attrs + .iter() + .filter_map(|attr| match attr { + dioxus_core::TemplateAttribute::Static { + name, + value, + namespace, + } => Some(( + OwnedAttributeDiscription { + namespace: namespace.map(|s| s.to_string()), + name: name.to_string(), + volatile: false, + }, + OwnedAttributeValue::Text(value.to_string()), + )), + dioxus_core::TemplateAttribute::Dynamic(_) => None, + }) + .collect(), + listeners: FxHashSet::default(), + }); + let node_id = self.create_node(node); + for child in *children { + if let Some(child_id) = self.create_template_node(child, mutations_vec) { + self.add_child(node_id, child_id); + } + } + self.stack.push(node_id); + Some(node_id) + } + TemplateNode::Text(txt) => { + let node_id = self.create_node(Node::new(NodeType::Text { + text: txt.to_string(), + })); + self.stack.push(node_id); + Some(node_id) + } + _ => None, + } + } + /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. pub fn apply_mutations(&mut self, mutations_vec: Vec) -> DirtyNodeStates { let mut nodes_updated: FxHashMap = FxHashMap::default(); for mutations in mutations_vec { + for template in mutations.templates { + let mut template_root_ids = Vec::new(); + for root in template.roots { + if let Some(id) = self.create_template_node(root, &mut nodes_updated) { + template_root_ids.push(id); + } + } + self.templates + .insert(template.name.to_string(), template_root_ids); + } for e in mutations.edits { use dioxus_core::Mutation::*; match e { - AppendChildren { m } => { - let data = self.stack.split_off(m); - let (parent, children) = data.split_first().unwrap(); + AppendChildren { id, m } => { + let children = self.stack.split_off(m); + let parent = self.element_to_node_id(id); for child in children { - self.add_child(*parent, *child); - mark_dirty(*parent, NodeMask::ALL, &mut nodes_updated); + self.add_child(parent, child); + mark_dirty(parent, NodeMask::ALL, &mut nodes_updated); } } AssignId { path, id } => { self.set_element_id(self.load_child(path), id); } - CreateElement { name } => { - let node = Node::new(NodeType::Element { - tag: name.to_string(), - namespace: None, - attributes: FxHashMap::default(), - listeners: FxHashSet::default(), - }); - let id = self.create_node(node); - self.stack.push(id); - mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - } - CreateElementNamespace { name, namespace } => { - let node = Node::new(NodeType::Element { - tag: name.to_string(), - namespace: Some(namespace.to_string()), - attributes: FxHashMap::default(), - listeners: FxHashSet::default(), - }); - let id = self.create_node(node); - self.stack.push(id); - mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - } CreatePlaceholder { id } => { let node = Node::new(NodeType::Placeholder); let node_id = self.create_node(node); @@ -142,20 +185,6 @@ impl RealDom { self.stack.push(node_id); mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated); } - CreateStaticPlaceholder => { - let node = Node::new(NodeType::Placeholder); - let id = self.create_node(node); - self.stack.push(id); - mark_dirty(id, NodeMask::ALL, &mut nodes_updated); - } - CreateStaticText { value } => { - let node = Node::new(NodeType::Text { - text: value.to_string(), - }); - let id = self.create_node(node); - self.stack.push(id); - mark_dirty(id, NodeMask::new().with_text(), &mut nodes_updated); - } CreateTextNode { value, id } => { let node = Node::new(NodeType::Text { text: value.to_string(), @@ -214,10 +243,6 @@ impl RealDom { mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } } - SaveTemplate { name, m } => { - let template = self.stack.split_off(m); - self.templates.insert(name.to_string(), template); - } SetAttribute { name, value, @@ -243,26 +268,6 @@ impl RealDom { ); } } - SetStaticAttribute { name, value, ns } => { - let node_id = self.stack.last().unwrap(); - let node = self.tree.get_mut(*node_id).unwrap(); - if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type - { - attributes.insert( - OwnedAttributeDiscription { - name: name.to_string(), - namespace: ns.map(|s| s.to_string()), - volatile: false, - }, - crate::node::OwnedAttributeValue::Text(value.to_string()), - ); - mark_dirty( - *node_id, - NodeMask::new_with_attrs(AttributeMask::single(name)), - &mut nodes_updated, - ); - } - } SetBoolAttribute { name, value, id } => { let node_id = self.element_to_node_id(id); let node = self.tree.get_mut(node_id).unwrap(); @@ -283,18 +288,6 @@ impl RealDom { ); } } - SetInnerText { value } => { - let node_id = *self.stack.last().unwrap(); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Element { .. } = &mut node.node_data.node_type { - self.tree.remove_all_children(node_id); - let text_node = Node::new(NodeType::Text { - text: value.to_string(), - }); - let text_node_id = self.create_node(text_node); - self.add_child(node_id, text_node_id); - } - } SetText { value, id } => { let node_id = self.element_to_node_id(id); let node = self.tree.get_mut(node_id).unwrap(); @@ -303,42 +296,39 @@ impl RealDom { } mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); } - NewEventListener { - event_name, - scope: _, - id, - } => { + NewEventListener { name, scope: _, id } => { let node_id = self.element_to_node_id(id); let node = self.tree.get_mut(node_id).unwrap(); if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { - match self.nodes_listening.get_mut(event_name) { + match self.nodes_listening.get_mut(name) { Some(hs) => { hs.insert(node_id); } None => { let mut hs = FxHashSet::default(); hs.insert(node_id); - self.nodes_listening.insert(event_name.to_string(), hs); + self.nodes_listening.insert(name.to_string(), hs); } } - listeners.insert(event_name.to_string()); + listeners.insert(name.to_string()); } } - RemoveEventListener { id, event } => { + RemoveEventListener { id, name } => { let node_id = self.element_to_node_id(id); let node = self.tree.get_mut(node_id).unwrap(); if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { - listeners.remove(event); + listeners.remove(name); } - self.nodes_listening - .get_mut(event) - .unwrap() - .remove(&node_id); + self.nodes_listening.get_mut(name).unwrap().remove(&node_id); } Remove { id } => { let node_id = self.element_to_node_id(id); self.tree.remove(node_id); } + PushRoot { id } => { + let node_id = self.element_to_node_id(id); + self.stack.push(node_id); + } } } } From 53094a7bbd31c0a87f4d2a896d213b6294216210 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 3 Dec 2022 18:28:32 -0600 Subject: [PATCH 19/43] work on implementing macro --- packages/native-core-macro/src/lib.rs | 428 +++++++++----------------- packages/native-core/src/passes.rs | 4 +- 2 files changed, 144 insertions(+), 288 deletions(-) diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 852477fc8..d5bce0e92 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -154,77 +154,21 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { .map(|m| &m.mem.ident) .collect(); let member_types = state_strct.state_members.iter().map(|m| &m.mem.ty); - let resolve_members = state_strct + let impl_members = state_strct .state_members .iter() - .map(|m| state_strct.resolve(m)); + .map(|m| state_strct.impl_pass(m)); - let child_types = state_strct.child_states.iter().map(|s| &s.ty); - let child_members = state_strct.child_states.iter().map(|s| &s.ident); + // let child_types = state_strct.child_states.iter().map(|s| &s.ty); + // let child_members = state_strct.child_states.iter().map(|s| &s.ident); let gen = quote! { + #(#impl_members)* impl State for #type_name { - fn update<'a, T: dioxus_native_core::traversable::Traversable,T2: dioxus_native_core::traversable::Traversable>( - dirty: &[(dioxus_native_core::RealNodeId, dioxus_native_core::node_ref::NodeMask)], - state_tree: &'a mut T, - rdom: &'a T2, - ctx: &anymap::AnyMap, - ) -> rustc_hash::FxHashSet{ - #[derive(Clone, Copy)] - struct MembersDirty { - #(#members: bool, )* - } - - impl MembersDirty { - fn new() -> Self { - Self {#(#members: false),*} - } - - fn any(&self) -> bool { - #(self.#members || )* false - } - - fn union(self, other: Self) -> Self { - Self {#(#members: self.#members || other.#members),*} - } - } - - let mut dirty_elements = rustc_hash::FxHashSet::default(); - // the states of any elements that are dirty - let mut states: rustc_hash::FxHashMap = rustc_hash::FxHashMap::default(); - - for (id, mask) in dirty { - let members_dirty = MembersDirty { - #(#members: #member_types::NODE_MASK.overlaps(mask),)* - }; - if members_dirty.any(){ - if let Some(state) = states.get_mut(id){ - *state = state.union(members_dirty); - } - else{ - states.insert(*id, members_dirty); - } - } - dirty_elements.insert(*id); - } - - #( - #resolve_members; - )* - - #( - dirty_elements.extend( - <#child_types as dioxus_native_core::state::State>::update( - dirty, - &mut state_tree.map(|n| &n.#child_members, |n| &mut n.#child_members), - rdom, - ctx, - ) - ); - )* - - dirty_elements - } + const PASSES: &'static [AnyPass>] = &[ + AnyPass(#(&#member_types),*) + ]; + const MASKS: &'static [NodeMask] = &[#(#member_types::NODE_MASK),*]; } }; gen.into() @@ -246,7 +190,11 @@ struct Struct { impl Struct { fn new(name: Ident, fields: &[&Field]) -> Self { - let members = fields.iter().filter_map(|f| Member::parse(f)).collect(); + let members = fields + .iter() + .enumerate() + .filter_map(|(i, f)| Member::parse(f, 1 << i)) + .collect(); Self { name, members } } } @@ -260,7 +208,7 @@ impl<'a> StateStruct<'a> { /// Parse the state structure, and find a resolution order that will allow us to update the state for each node in after the state(s) it depends on have been resolved. fn parse(fields: &[&'a Field], strct: &'a Struct) -> Result { let mut parse_err = Ok(()); - let mut unordered_state_members: Vec<_> = strct + let mut state_members: Vec<_> = strct .members .iter() .zip(fields.iter()) @@ -273,41 +221,10 @@ impl<'a> StateStruct<'a> { }) .collect(); parse_err?; - - let mut state_members = Vec::new(); - // Keep adding members that have had all of there dependancies resolved until there are no more members left. - while !unordered_state_members.is_empty() { - let mut resolved = false; - for i in 0..unordered_state_members.len() { - let mem = &mut unordered_state_members[i]; - // if this member has all of its dependancies resolved other than itself, resolve it next. - if mem.dep_mems.iter().all(|(dep, resolved)| { - *resolved || (*dep == mem.mem && mem.dep_kind != DependencyKind::Node) - }) { - let mem = unordered_state_members.remove(i); - // mark any dependency that depends on this member as resolved - for member in unordered_state_members.iter_mut() { - for (dep, resolved) in &mut member.dep_mems { - *resolved |= *dep == mem.mem; - } - } - state_members.push(mem); - resolved = true; - break; - } - } - if !resolved { - return Err(Error::new( - strct.name.span(), - format!( - "{} has circular dependacy in {:?}", - strct.name, - unordered_state_members - .iter() - .map(|m| format!("{}", &m.mem.ident)) - .collect::>() - ), - )); + for i in 0..state_members.len() { + let deps = state_members[i].dep_mems.clone(); + for dep in deps { + state_members[dep as usize].dependant_mems.push(i as u64); } } @@ -351,175 +268,6 @@ impl<'a> StateStruct<'a> { } dependants } - - // Mark the states that depend on the current state as dirty - fn update_dependants(&self, mem: &Member) -> impl ToTokens { - let dep = self.get_depenadants(mem); - let update_child_dependants = if dep.child.is_empty() { - quote!() - } else { - let insert = dep.child.iter().map(|d|{ - if *d == mem { - quote! { - let seeking = dioxus_native_core::HeightOrdering::new(state_tree.height(parent_id).unwrap(), parent_id); - if let Err(idx) = resolution_order - .binary_search_by(|ordering| ordering.cmp(&seeking).reverse()){ - resolution_order.insert( - idx, - seeking, - ); - } - } - } else { - quote! {} - } - }); - let update: Vec<_> = dep - .child - .iter() - .map(|d| { - let ident = &d.ident; - quote! { - dirty.#ident = true; - } - }) - .collect(); - quote! { - if let Some(parent_id) = state_tree.parent(id) { - #(#insert)* - if let Some(dirty) = states.get_mut(&parent_id) { - #(#update)* - } - else { - let mut dirty = MembersDirty::new(); - #(#update)* - states.insert(parent_id, dirty); - } - } - } - }; - let node_dependants: Vec<_> = dep.node.iter().map(|d| &d.ident).collect(); - let update_node_dependants = quote! {#(members_dirty.#node_dependants = true;)*}; - let update_parent_dependants = if dep.parent.is_empty() { - quote!() - } else { - let insert = dep.parent.iter().map(|d| { - if *d == mem { - quote! { - let seeking = dioxus_native_core::HeightOrdering::new(state_tree.height(*child_id).unwrap(), *child_id); - if let Err(idx) = resolution_order - .binary_search(&seeking){ - resolution_order.insert( - idx, - seeking, - ); - } - } - } else { - quote! {} - } - }); - let update: Vec<_> = dep - .parent - .iter() - .map(|d| { - let ident = &d.ident; - quote! { - dirty.#ident = true; - } - }) - .collect(); - quote! { - for child_id in state_tree.children(id) { - #(#insert)* - if let Some(dirty) = states.get_mut(&child_id) { - #(#update)* - } - else { - let mut dirty = MembersDirty::new(); - #(#update)* - states.insert(*child_id, dirty); - } - } - } - }; - - quote! { - #update_node_dependants - #update_child_dependants - #update_parent_dependants - } - } - - // Generate code to resolve this state - fn resolve(&self, mem: &StateMember) -> impl ToTokens { - let reduce_member = mem.reduce_self(); - let update_dependant = self.update_dependants(mem.mem); - let member = &mem.mem.ident; - - match mem.dep_kind { - DependencyKind::Parent => { - quote! { - // resolve parent dependant state - let mut resolution_order = states.keys().copied().map(|id| dioxus_native_core::HeightOrdering::new(state_tree.height(id).unwrap(), id)).collect::>(); - resolution_order.sort(); - let mut i = 0; - while i < resolution_order.len(){ - let id = resolution_order[i].id; - let node = rdom.get(id).unwrap(); - let members_dirty = states.get_mut(&id).unwrap(); - let (current_state, parent) = state_tree.get_node_parent_mut(id); - let current_state = current_state.unwrap(); - if members_dirty.#member && #reduce_member { - dirty_elements.insert(id); - #update_dependant - } - i += 1; - } - } - } - DependencyKind::Child => { - quote! { - // resolve child dependant state - let mut resolution_order = states.keys().copied().map(|id| dioxus_native_core::HeightOrdering::new(state_tree.height(id).unwrap(), id)).collect::>(); - resolution_order.sort_by(|height_ordering1, height_ordering2| { - height_ordering1.cmp(&height_ordering2).reverse() - }); - let mut i = 0; - while i < resolution_order.len(){ - let id = resolution_order[i].id; - let node = rdom.get(id).unwrap(); - let members_dirty = states.get_mut(&id).unwrap(); - let (current_state, children) = state_tree.get_node_children_mut(id); - let current_state = current_state.unwrap(); - if members_dirty.#member && #reduce_member { - dirty_elements.insert(id); - #update_dependant - } - i += 1; - } - } - } - DependencyKind::Node => { - quote! { - // resolve node dependant state - let mut resolution_order = states.keys().copied().collect::>(); - let mut i = 0; - while i < resolution_order.len(){ - let id = resolution_order[i]; - let node = rdom.get(id).unwrap(); - let members_dirty = states.get_mut(&id).unwrap(); - let current_state = state_tree.get_mut(id).unwrap(); - if members_dirty.#member && #reduce_member { - dirty_elements.insert(id); - #update_dependant - } - i += 1; - } - } - } - } - } } fn try_parenthesized(input: ParseStream) -> Result { @@ -562,13 +310,15 @@ impl Parse for Dependency { /// The type of the member and the ident of the member #[derive(PartialEq, Debug)] struct Member { + id: u64, ty: Type, ident: Ident, } impl Member { - fn parse(field: &Field) -> Option { + fn parse(field: &Field, id: u64) -> Option { Some(Self { + id, ty: field.ty.clone(), ident: field.ident.as_ref()?.clone(), }) @@ -581,7 +331,9 @@ struct StateMember<'a> { // the kind of dependncies this state has dep_kind: DependencyKind, // the depenancy and if it is satified - dep_mems: Vec<(&'a Member, bool)>, + dep_mems: Vec, + // any members that depend on this member + dependant_mems: Vec, // the context this state requires ctx_ty: Option, } @@ -610,7 +362,7 @@ impl<'a> StateMember<'a> { .iter() .filter_map(|name| { if let Some(found) = parent.members.iter().find(|m| &m.ident == name) { - Some((found, false)) + Some((found.id, false)) } else { err = Err(Error::new( name.span(), @@ -624,6 +376,7 @@ impl<'a> StateMember<'a> { mem, dep_kind, dep_mems, + dependant_mems: Vec::new(), ctx_ty: dependency.ctx_ty, }) } @@ -638,8 +391,9 @@ impl<'a> StateMember<'a> { } /// generate code to call the resolve function for the state. This does not handle checking if resolving the state is necessary, or marking the states that depend on this state as dirty. - fn reduce_self(&self) -> quote::__private::TokenStream { + fn impl_pass(&self, parent_type: Type) -> quote::__private::TokenStream { let ident = &self.mem.ident; + let ty = &self.mem.ty; let get_ctx = if let Some(ctx_ty) = &self.ctx_ty { if ctx_ty == &parse_quote!(()) { quote! {&()} @@ -652,23 +406,125 @@ impl<'a> StateMember<'a> { }; let ty = &self.mem.ty; - let node_view = quote!(dioxus_native_core::node_ref::NodeView::new(node, #ty::NODE_MASK)); + let node_view = quote!(dioxus_native_core::node_ref::NodeView::new(unsafe{&*{&node.node_data as *const _}}, #ty::NODE_MASK)); let dep_idents = self.dep_mems.iter().map(|m| &m.0.ident); - match self.dep_kind { + let impl_specific = match self.dep_kind { DependencyKind::Node => { - quote!({ - current_state.#ident.reduce(#node_view, (#(¤t_state.#dep_idents,)*), #get_ctx) - }) + quote! { + impl NodePass<#parent_type> for #ty { + fn pass(&self, node: &mut T, ctx: &SendAnyMap) -> bool { + node.state.#ident.reduce(#node_view, (#(&node.state.#dep_idents,)*), #get_ctx) + } + } + } } DependencyKind::Child => { - quote!({ - current_state.#ident.reduce(#node_view, children.iter().map(|c| (#(&c.#dep_idents)*)), #get_ctx) - }) + let update = if self.dep_mems.iter().any(|id| id == self.mem.id) { + quote! { + if update { + PassReturn{ + progress: true, + mark_dirty: true, + } + } else { + PassReturn{ + progress: false, + mark_dirty: false, + } + } + } + } else { + quote! { + if update { + PassReturn{ + progress: false, + mark_dirty: true, + } + } else { + PassReturn{ + progress: false, + mark_dirty: false, + } + } + } + }; + quote!( + pub trait UpwardPass: Pass { + fn pass<'a>( + &self, + node: &mut T, + children: &mut dyn Iterator, + ctx: &SendAnyMap, + ) -> PassReturn { + let update = node.state.#ident.reduce(#node_view, children.map(|c| (#(&c.state.#dep_idents,)*)), #get_ctx); + #update + } + } + ) } DependencyKind::Parent => { - quote!({ - current_state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.#dep_idents)*)), #get_ctx) - }) + let update = if self.dep_mems.iter().any(|id| id == self.mem.id) { + quote! { + if update { + PassReturn{ + progress: true, + mark_dirty: true, + } + } else { + PassReturn{ + progress: false, + mark_dirty: false, + } + } + } + } else { + quote! { + if update { + PassReturn{ + progress: false, + mark_dirty: true, + } + } else { + PassReturn{ + progress: false, + mark_dirty: false, + } + } + } + }; + quote!( + impl DownwardPass<#parent_type> for #ty { + fn pass(&self, node: &mut T, parent: Option<&mut T>, ctx: &SendAnyMap) -> PassReturn{ + let update = current_state.state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.state.#dep_idents,)*)), #get_ctx); + #update + } + } + ) + } + }; + let pass_id = self.mem.id; + let depenancies = &self.dep_mems; + let dependants = &self.dependant_mems; + let mask = self + .dep_mems + .iter() + .map(|m| m.0.id) + .fold(self.mem.id, |a, b| a | b); + quote! { + #impl_specific + impl Pass for #ty { + fn pass_id(&self) -> PassId { + PassId(#pass_id) + } + fn dependancies(&self) -> &'static [PassId] { + &[#(PassId(#depenancies)),*] + } + fn dependants(&self) -> &'static [PassId] { + &[#(PassId(#dependants)),*] + } + fn mask(&self) -> MemberMask { + MemberMask(#mask) + } } } } diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 86bb50537..26276c6f3 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -151,8 +151,8 @@ impl BitOr for MemberMask { } pub struct PassReturn { - progress: bool, - mark_dirty: bool, + pub progress: bool, + pub mark_dirty: bool, } pub trait Pass { From 29a61b1855dd5f096bb3604afcf6ed36fcc0d152 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 3 Dec 2022 19:35:37 -0600 Subject: [PATCH 20/43] make the macro compile --- packages/native-core-macro/src/lib.rs | 64 +- .../tests/called_minimally_on_build.rs | 435 ++++---- .../native-core-macro/tests/change_nodes.rs | 15 +- .../native-core-macro/tests/initial_build.rs | 102 +- .../tests/peristant_iterator.rs | 618 ++++++------ .../native-core-macro/tests/update_state.rs | 948 +++++++++--------- packages/native-core/src/lib.rs | 4 +- packages/native-core/src/real_dom.rs | 18 +- 8 files changed, 1092 insertions(+), 1112 deletions(-) diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index d5bce0e92..b2c259efa 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -148,16 +148,12 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { let strct = Struct::new(type_name.clone(), &fields); match StateStruct::parse(&fields, &strct) { Ok(state_strct) => { - let members: Vec<_> = state_strct - .state_members - .iter() - .map(|m| &m.mem.ident) - .collect(); - let member_types = state_strct.state_members.iter().map(|m| &m.mem.ty); + let member_types1 = state_strct.state_members.iter().map(|m| &m.mem.ty); + let member_types2 = state_strct.state_members.iter().map(|m| &m.mem.ty); let impl_members = state_strct .state_members .iter() - .map(|m| state_strct.impl_pass(m)); + .map(|m| m.impl_pass(state_strct.ty)); // let child_types = state_strct.child_states.iter().map(|s| &s.ty); // let child_members = state_strct.child_states.iter().map(|s| &s.ident); @@ -165,10 +161,10 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { let gen = quote! { #(#impl_members)* impl State for #type_name { - const PASSES: &'static [AnyPass>] = &[ - AnyPass(#(&#member_types),*) + const PASSES: &'static [dioxus_native_core::AnyPass>] = &[ + #(dioxus_native_core::AnyPass(&#member_types1)),* ]; - const MASKS: &'static [NodeMask] = &[#(#member_types::NODE_MASK),*]; + const MASKS: &'static [dioxus_native_core::NodeMask] = &[#(#member_types2::NODE_MASK),*]; } }; gen.into() @@ -177,12 +173,6 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { } } -struct Depenadants<'a> { - node: Vec<&'a Member>, - child: Vec<&'a Member>, - parent: Vec<&'a Member>, -} - struct Struct { name: Ident, members: Vec, @@ -201,7 +191,9 @@ impl Struct { struct StateStruct<'a> { state_members: Vec>, + #[allow(unused)] child_states: Vec<&'a Member>, + ty: &'a Ident, } impl<'a> StateStruct<'a> { @@ -222,7 +214,7 @@ impl<'a> StateStruct<'a> { .collect(); parse_err?; for i in 0..state_members.len() { - let deps = state_members[i].dep_mems.clone(); + let deps: Vec<_> = state_members[i].dep_mems.iter().map(|m| m.id).collect(); for dep in deps { state_members[dep as usize].dependant_mems.push(i as u64); } @@ -244,30 +236,11 @@ impl<'a> StateStruct<'a> { // members need to be sorted so that members are updated after the members they depend on Ok(Self { + ty: &strct.name, state_members, child_states: child_states.collect(), }) } - - fn get_depenadants(&self, mem: &Member) -> Depenadants { - let mut dependants = Depenadants { - node: Vec::new(), - child: Vec::new(), - parent: Vec::new(), - }; - for member in &self.state_members { - for (dep, _) in &member.dep_mems { - if *dep == mem { - match member.dep_kind { - DependencyKind::Node => dependants.node.push(member.mem), - DependencyKind::Child => dependants.child.push(member.mem), - DependencyKind::Parent => dependants.parent.push(member.mem), - } - } - } - } - dependants - } } fn try_parenthesized(input: ParseStream) -> Result { @@ -331,7 +304,7 @@ struct StateMember<'a> { // the kind of dependncies this state has dep_kind: DependencyKind, // the depenancy and if it is satified - dep_mems: Vec, + dep_mems: Vec<&'a Member>, // any members that depend on this member dependant_mems: Vec, // the context this state requires @@ -362,7 +335,7 @@ impl<'a> StateMember<'a> { .iter() .filter_map(|name| { if let Some(found) = parent.members.iter().find(|m| &m.ident == name) { - Some((found.id, false)) + Some(found) } else { err = Err(Error::new( name.span(), @@ -391,9 +364,8 @@ impl<'a> StateMember<'a> { } /// generate code to call the resolve function for the state. This does not handle checking if resolving the state is necessary, or marking the states that depend on this state as dirty. - fn impl_pass(&self, parent_type: Type) -> quote::__private::TokenStream { + fn impl_pass(&self, parent_type: &Ident) -> quote::__private::TokenStream { let ident = &self.mem.ident; - let ty = &self.mem.ty; let get_ctx = if let Some(ctx_ty) = &self.ctx_ty { if ctx_ty == &parse_quote!(()) { quote! {&()} @@ -407,7 +379,7 @@ impl<'a> StateMember<'a> { let ty = &self.mem.ty; let node_view = quote!(dioxus_native_core::node_ref::NodeView::new(unsafe{&*{&node.node_data as *const _}}, #ty::NODE_MASK)); - let dep_idents = self.dep_mems.iter().map(|m| &m.0.ident); + let dep_idents = self.dep_mems.iter().map(|m| &m.ident); let impl_specific = match self.dep_kind { DependencyKind::Node => { quote! { @@ -419,7 +391,7 @@ impl<'a> StateMember<'a> { } } DependencyKind::Child => { - let update = if self.dep_mems.iter().any(|id| id == self.mem.id) { + let update = if self.dep_mems.iter().any(|m| m.id == self.mem.id) { quote! { if update { PassReturn{ @@ -463,7 +435,7 @@ impl<'a> StateMember<'a> { ) } DependencyKind::Parent => { - let update = if self.dep_mems.iter().any(|id| id == self.mem.id) { + let update = if self.dep_mems.iter().any(|m| m.id == self.mem.id) { quote! { if update { PassReturn{ @@ -503,12 +475,12 @@ impl<'a> StateMember<'a> { } }; let pass_id = self.mem.id; - let depenancies = &self.dep_mems; + let depenancies = self.dep_mems.iter().map(|m| m.id); let dependants = &self.dependant_mems; let mask = self .dep_mems .iter() - .map(|m| m.0.id) + .map(|m| m.id) .fold(self.mem.id, |a, b| a | b); quote! { #impl_specific diff --git a/packages/native-core-macro/tests/called_minimally_on_build.rs b/packages/native-core-macro/tests/called_minimally_on_build.rs index 7063574aa..7935f9693 100644 --- a/packages/native-core-macro/tests/called_minimally_on_build.rs +++ b/packages/native-core-macro/tests/called_minimally_on_build.rs @@ -1,255 +1,246 @@ -use anymap::AnyMap; -use dioxus::prelude::*; -use dioxus_native_core::node_ref::*; -use dioxus_native_core::real_dom::*; -use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; -use dioxus_native_core_macro::State; +// use anymap::AnyMap; +// use dioxus::prelude::*; +// use dioxus_native_core::node_ref::*; +// use dioxus_native_core::real_dom::*; +// use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; +// use dioxus_native_core_macro::State; -macro_rules! dep { - ( child( $name:ty, $dep:ty ) ) => { - impl ChildDepState for $name { - type Ctx = (); - type DepState = $dep; - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce<'a>( - &mut self, - _: NodeView, - _: impl Iterator, - _: &Self::Ctx, - ) -> bool - where - Self::DepState: 'a, - { - self.0 += 1; - true - } - } - }; +// macro_rules! dep { +// ( child( $name:ty, $dep:ty ) ) => { +// impl ChildDepState for $name { +// type Ctx = (); +// type DepState = $dep; +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce<'a>( +// &mut self, +// _: NodeView, +// _: impl Iterator, +// _: &Self::Ctx, +// ) -> bool +// where +// Self::DepState: 'a, +// { +// self.0 += 1; +// true +// } +// } +// }; - ( parent( $name:ty, $dep:ty ) ) => { - impl ParentDepState for $name { - type Ctx = (); - type DepState = $dep; - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce( - &mut self, - _: NodeView, - _: Option<&Self::DepState>, - _: &Self::Ctx, - ) -> bool { - self.0 += 1; - true - } - } - }; +// ( parent( $name:ty, $dep:ty ) ) => { +// impl ParentDepState for $name { +// type Ctx = (); +// type DepState = $dep; +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &Self::Ctx) -> bool { +// self.0 += 1; +// true +// } +// } +// }; - ( node( $name:ty, ($($l:lifetime),*), $dep:ty ) ) => { - impl<$($l),*> NodeDepState<$dep> for $name { - type Ctx = (); - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce( - &mut self, - _: NodeView, - _: $dep, - _: &Self::Ctx, - ) -> bool { - self.0 += 1; - true - } - } - }; -} +// ( node( $name:ty, $dep:ty ) ) => { +// impl NodeDepState for $name { +// type Ctx = (); +// type DepState<'a> = $dep; +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce(&mut self, _: NodeView, _: $dep, _: &Self::Ctx) -> bool { +// self.0 += 1; +// true +// } +// } +// }; +// } -macro_rules! test_state{ - ( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => { - #[test] - fn state_reduce_initally_called_minimally() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div { - div{ - div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - }) - } +// macro_rules! test_state{ +// ( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => { +// #[test] +// fn state_reduce_initally_called_minimally() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div { +// div{ +// div{ +// p{} +// } +// p{ +// "hello" +// } +// div{ +// h1{} +// } +// p{ +// "world" +// } +// } +// }) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx! { - div { - div{ - div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - } - }); +// let mutations = vdom.create_vnodes(rsx! { +// div { +// div{ +// div{ +// p{} +// } +// p{ +// "hello" +// } +// div{ +// h1{} +// } +// p{ +// "world" +// } +// } +// } +// }); - let mut dom: RealDom<$s> = RealDom::new(); +// let mut dom: RealDom<$s> = RealDom::new(); - let nodes_updated = dom.apply_mutations(vec![mutations]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +// let nodes_updated = dom.apply_mutations(vec![mutations]); +// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - dom.traverse_depth_first(|n| { - $( - assert_eq!(n.state.$child.0, 1); - )* - $( - assert_eq!(n.state.$node.0, 1); - )* - $( - assert_eq!(n.state.$parent.0, 1); - )* - }); - } - } -} +// dom.traverse_depth_first(|n| { +// $( +// assert_eq!(n.state.$child.0, 1); +// )* +// $( +// assert_eq!(n.state.$node.0, 1); +// )* +// $( +// assert_eq!(n.state.$parent.0, 1); +// )* +// }); +// } +// } +// } -mod node_depends_on_child_and_parent { - use super::*; - #[derive(Debug, Clone, Default, PartialEq)] - struct Node(i32); - dep!(node(Node, ('a, 'b), (&'a Child, &'b Parent))); +// mod node_depends_on_child_and_parent { +// use super::*; +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node(i32); +// dep!(node(Node, (&'a Child, &'a Parent))); - #[derive(Debug, Clone, Default, PartialEq)] - struct Child(i32); - dep!(child(Child, Child)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Child(i32); +// dep!(child(Child, Child)); - #[derive(Debug, Clone, Default, PartialEq)] - struct Parent(i32); - dep!(parent(Parent, Parent)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Parent(i32); +// dep!(parent(Parent, Parent)); - #[derive(Debug, Clone, Default, State)] - struct StateTester { - #[node_dep_state((child, parent))] - node: Node, - #[child_dep_state(child)] - child: Child, - #[parent_dep_state(parent)] - parent: Parent, - } +// #[derive(Debug, Clone, Default, State)] +// struct StateTester { +// #[node_dep_state((child, parent))] +// node: Node, +// #[child_dep_state(child)] +// child: Child, +// #[parent_dep_state(parent)] +// parent: Parent, +// } - test_state!(StateTester, child: (child), node: (node), parent: (parent)); -} +// test_state!(StateTester, child: (child), node: (node), parent: (parent)); +// } -mod child_depends_on_node_that_depends_on_parent { - use super::*; - #[derive(Debug, Clone, Default, PartialEq)] - struct Node(i32); - dep!(node(Node, ('a), (&'a Parent,))); +// mod child_depends_on_node_that_depends_on_parent { +// use super::*; +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node(i32); +// dep!(node(Node, (&'a Parent,))); - #[derive(Debug, Clone, Default, PartialEq)] - struct Child(i32); - dep!(child(Child, Node)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Child(i32); +// dep!(child(Child, Node)); - #[derive(Debug, Clone, Default, PartialEq)] - struct Parent(i32); - dep!(parent(Parent, Parent)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Parent(i32); +// dep!(parent(Parent, Parent)); - #[derive(Debug, Clone, Default, State)] - struct StateTester { - #[node_dep_state(parent)] - node: Node, - #[child_dep_state(node)] - child: Child, - #[parent_dep_state(parent)] - parent: Parent, - } +// #[derive(Debug, Clone, Default, State)] +// struct StateTester { +// #[node_dep_state(parent)] +// node: Node, +// #[child_dep_state(node)] +// child: Child, +// #[parent_dep_state(parent)] +// parent: Parent, +// } - test_state!(StateTester, child: (child), node: (node), parent: (parent)); -} +// test_state!(StateTester, child: (child), node: (node), parent: (parent)); +// } -mod parent_depends_on_node_that_depends_on_child { - use super::*; - #[derive(Debug, Clone, Default, PartialEq)] - struct Node(i32); - dep!(node(Node, ('a), (&'a Child,))); +// mod parent_depends_on_node_that_depends_on_child { +// use super::*; +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node(i32); +// dep!(node(Node, (&'a Child,))); - #[derive(Debug, Clone, Default, PartialEq)] - struct Child(i32); - dep!(child(Child, Child)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Child(i32); +// dep!(child(Child, Child)); - #[derive(Debug, Clone, Default, PartialEq)] - struct Parent(i32); - dep!(parent(Parent, Node)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Parent(i32); +// dep!(parent(Parent, Node)); - #[derive(Debug, Clone, Default, State)] - struct StateTester { - #[node_dep_state(child)] - node: Node, - #[child_dep_state(child)] - child: Child, - #[parent_dep_state(node)] - parent: Parent, - } +// #[derive(Debug, Clone, Default, State)] +// struct StateTester { +// #[node_dep_state(child)] +// node: Node, +// #[child_dep_state(child)] +// child: Child, +// #[parent_dep_state(node)] +// parent: Parent, +// } - test_state!(StateTester, child: (child), node: (node), parent: (parent)); -} +// test_state!(StateTester, child: (child), node: (node), parent: (parent)); +// } -mod node_depends_on_other_node_state { - use super::*; - #[derive(Debug, Clone, Default, PartialEq)] - struct Node1(i32); - dep!(node(Node1, ('a), (&'a Node2,))); +// mod node_depends_on_other_node_state { +// use super::*; +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node1(i32); +// dep!(node(Node1, (&'a Node2,))); - #[derive(Debug, Clone, Default, PartialEq)] - struct Node2(i32); - dep!(node(Node2, (), ())); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node2(i32); +// dep!(node(Node2, ())); - #[derive(Debug, Clone, Default, State)] - struct StateTester { - #[node_dep_state((node2))] - node1: Node1, - #[node_dep_state()] - node2: Node2, - } +// #[derive(Debug, Clone, Default, State)] +// struct StateTester { +// #[node_dep_state((node2))] +// node1: Node1, +// #[node_dep_state()] +// node2: Node2, +// } - test_state!(StateTester, child: (), node: (node1, node2), parent: ()); -} +// test_state!(StateTester, child: (), node: (node1, node2), parent: ()); +// } -mod node_child_and_parent_state_depends_on_self { - use super::*; - #[derive(Debug, Clone, Default, PartialEq)] - struct Node(i32); - dep!(node(Node, (), ())); +// mod node_child_and_parent_state_depends_on_self { +// use super::*; +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node(i32); +// dep!(node(Node, ())); - #[derive(Debug, Clone, Default, PartialEq)] - struct Child(i32); - dep!(child(Child, Child)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Child(i32); +// dep!(child(Child, Child)); - #[derive(Debug, Clone, Default, PartialEq)] - struct Parent(i32); - dep!(parent(Parent, Parent)); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Parent(i32); +// dep!(parent(Parent, Parent)); - #[derive(Debug, Clone, Default, State)] - struct StateTester { - #[node_dep_state()] - node: Node, - #[child_dep_state(child)] - child: Child, - #[parent_dep_state(parent)] - parent: Parent, - } +// #[derive(Debug, Clone, Default, State)] +// struct StateTester { +// #[node_dep_state()] +// node: Node, +// #[child_dep_state(child)] +// child: Child, +// #[parent_dep_state(parent)] +// parent: Parent, +// } - test_state!(StateTester, child: (child), node: (node), parent: (parent)); -} +// test_state!(StateTester, child: (child), node: (node), parent: (parent)); +// } diff --git a/packages/native-core-macro/tests/change_nodes.rs b/packages/native-core-macro/tests/change_nodes.rs index 2197cdbce..ccc103549 100644 --- a/packages/native-core-macro/tests/change_nodes.rs +++ b/packages/native-core-macro/tests/change_nodes.rs @@ -2,7 +2,8 @@ use dioxus::core::ElementId; use dioxus::prelude::*; use dioxus_native_core::real_dom::RealDom; use dioxus_native_core::state::State; -use dioxus_native_core::RealNodeId; +use dioxus_native_core::tree::*; +use dioxus_native_core::NodeId; use dioxus_native_core_macro::State; #[derive(State, Default, Clone)] @@ -34,13 +35,13 @@ fn remove_node() { let _to_update = dom.apply_mutations(vec![create]); - assert_eq!(dom[RealNodeId::ElementId(ElementId(0))].node_data.height, 0); - assert_eq!(dom[RealNodeId::UnaccessableId(0)].node_data.height, 1); + assert_eq!(dom.tree.height(NodeId(0)), Some(0)); + assert_eq!(dom.tree.height(NodeId(1)), Some(1)); dom.apply_mutations(vec![edit]); assert_eq!(dom.size(), 3); - assert_eq!(dom[RealNodeId::ElementId(ElementId(0))].node_data.height, 0); + assert_eq!(dom.tree.height(NodeId(0)), Some(0)); } #[test] @@ -68,11 +69,11 @@ fn add_node() { let _to_update = dom.apply_mutations(vec![create]); assert_eq!(dom.size(), 2); - assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); + assert_eq!(dom.tree.height(NodeId(2)), Some(1)); dom.apply_mutations(vec![update]); assert_eq!(dom.size(), 3); - assert_eq!(dom[RealNodeId::ElementId(ElementId(3))].node_data.height, 0); - assert_eq!(dom[RealNodeId::UnaccessableId(0)].node_data.height, 1); + assert_eq!(dom.tree.height(NodeId(3)), Some(0)); + assert_eq!(dom.tree.height(NodeId(0)), Some(1)); } diff --git a/packages/native-core-macro/tests/initial_build.rs b/packages/native-core-macro/tests/initial_build.rs index d9b35f37a..86605aca9 100644 --- a/packages/native-core-macro/tests/initial_build.rs +++ b/packages/native-core-macro/tests/initial_build.rs @@ -1,63 +1,63 @@ -use dioxus::core::ElementId; -use dioxus::prelude::*; -use dioxus_native_core::real_dom::RealDom; -use dioxus_native_core::state::State; -use dioxus_native_core::RealNodeId; -use dioxus_native_core_macro::State; +// use dioxus::core::ElementId; +// use dioxus::prelude::*; +// use dioxus_native_core::real_dom::RealDom; +// use dioxus_native_core::state::State; +// use dioxus_native_core::RealNodeId; +// use dioxus_native_core_macro::State; -#[derive(Default, Clone, State)] -struct Empty {} +// #[derive(Default, Clone, State)] +// struct Empty {} -#[test] -fn initial_build_simple() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } +// #[test] +// fn initial_build_simple() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx! { - div{} - }); +// let mutations = vdom.create_vnodes(rsx! { +// div{} +// }); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let _to_update = dom.apply_mutations(vec![mutations]); +// let _to_update = dom.apply_mutations(vec![mutations]); - assert_eq!(dom.size(), 2); - assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); -} +// assert_eq!(dom.size(), 2); +// assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); +// } -#[test] -fn initial_build_with_children() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } +// #[test] +// fn initial_build_with_children() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx! { - div{ - div{ - "hello" - p{ - "world" - } - "hello world" - } - } - }); +// let mutations = vdom.create_vnodes(rsx! { +// div{ +// div{ +// "hello" +// p{ +// "world" +// } +// "hello world" +// } +// } +// }); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let _to_update = dom.apply_mutations(vec![mutations]); - assert_eq!(dom.size(), 2); - assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); - assert_eq!(dom[RealNodeId::UnaccessableId(6)].node_data.height, 2); - assert_eq!(dom[RealNodeId::UnaccessableId(5)].node_data.height, 3); - assert_eq!(dom[RealNodeId::UnaccessableId(8)].node_data.height, 3); - assert_eq!(dom[RealNodeId::UnaccessableId(10)].node_data.height, 3); - assert_eq!(dom[RealNodeId::UnaccessableId(9)].node_data.height, 4); -} +// let _to_update = dom.apply_mutations(vec![mutations]); +// assert_eq!(dom.size(), 2); +// assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); +// assert_eq!(dom[RealNodeId::UnaccessableId(6)].node_data.height, 2); +// assert_eq!(dom[RealNodeId::UnaccessableId(5)].node_data.height, 3); +// assert_eq!(dom[RealNodeId::UnaccessableId(8)].node_data.height, 3); +// assert_eq!(dom[RealNodeId::UnaccessableId(10)].node_data.height, 3); +// assert_eq!(dom[RealNodeId::UnaccessableId(9)].node_data.height, 4); +// } diff --git a/packages/native-core-macro/tests/peristant_iterator.rs b/packages/native-core-macro/tests/peristant_iterator.rs index b999b503c..b1de9392e 100644 --- a/packages/native-core-macro/tests/peristant_iterator.rs +++ b/packages/native-core-macro/tests/peristant_iterator.rs @@ -1,333 +1,333 @@ -use dioxus::core_macro::rsx_without_templates; -use dioxus::prelude::*; -use dioxus_native_core::{ - real_dom::{NodeType, RealDom}, - state::State, - utils::PersistantElementIter, -}; -use dioxus_native_core_macro::State; +// use dioxus::core_macro::rsx_without_templates; +// use dioxus::prelude::*; +// use dioxus_native_core::{ +// real_dom::{NodeType, RealDom}, +// state::State, +// utils::PersistantElementIter, +// }; +// use dioxus_native_core_macro::State; -#[derive(State, Default, Clone)] -struct Empty {} +// #[derive(State, Default, Clone)] +// struct Empty {} -#[test] -#[allow(unused_variables)] -fn traverse() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } - let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx! { - div{ - div{ - "hello" - p{ - "world" - } - "hello world" - } - } - }); +// #[test] +// #[allow(unused_variables)] +// fn traverse() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } +// let vdom = VirtualDom::new(Base); +// let mutations = vdom.create_vnodes(rsx! { +// div{ +// div{ +// "hello" +// p{ +// "world" +// } +// "hello world" +// } +// } +// }); - let mut rdom: RealDom = RealDom::new(); +// let mut rdom: RealDom = RealDom::new(); - let _to_update = rdom.apply_mutations(vec![mutations]); +// let _to_update = rdom.apply_mutations(vec![mutations]); - let mut iter = PersistantElementIter::new(); - let div_tag = "div".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - let text1 = "hello".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Text { text: text1, .. } - )); - let p_tag = "p".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - let text2 = "world".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Text { text: text2, .. } - )); - let text3 = "hello world".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Text { text: text3, .. } - )); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); +// let mut iter = PersistantElementIter::new(); +// let div_tag = "div".to_string(); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); +// let text1 = "hello".to_string(); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text1, .. } +// )); +// let p_tag = "p".to_string(); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: p_tag, .. } +// )); +// let text2 = "world".to_string(); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text2, .. } +// )); +// let text3 = "hello world".to_string(); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text3, .. } +// )); +// assert!(matches!( +// &rdom[iter.next(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text3, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text2, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text1, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text3, .. } - )); -} +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text3, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text2, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: p_tag, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text1, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); +// assert!(matches!( +// &rdom[iter.prev(&rdom).id()].node_data.node_type, +// NodeType::Text { text: text3, .. } +// )); +// } -#[test] -#[allow(unused_variables)] -fn persist_removes() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } - let vdom = VirtualDom::new(Base); - let (build, update) = vdom.diff_lazynodes( - rsx_without_templates! { - div{ - p{ - key: "1", - "hello" - } - p{ - key: "2", - "world" - } - p{ - key: "3", - "hello world" - } - } - }, - rsx_without_templates! { - div{ - p{ - key: "1", - "hello" - } - p{ - key: "3", - "hello world" - } - } - }, - ); +// #[test] +// #[allow(unused_variables)] +// fn persist_removes() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } +// let vdom = VirtualDom::new(Base); +// let (build, update) = vdom.diff_lazynodes( +// rsx_without_templates! { +// div{ +// p{ +// key: "1", +// "hello" +// } +// p{ +// key: "2", +// "world" +// } +// p{ +// key: "3", +// "hello world" +// } +// } +// }, +// rsx_without_templates! { +// div{ +// p{ +// key: "1", +// "hello" +// } +// p{ +// key: "3", +// "hello world" +// } +// } +// }, +// ); - let mut rdom: RealDom = RealDom::new(); +// let mut rdom: RealDom = RealDom::new(); - let _to_update = rdom.apply_mutations(vec![build]); +// let _to_update = rdom.apply_mutations(vec![build]); - // this will end on the node that is removed - let mut iter1 = PersistantElementIter::new(); - // this will end on the after node that is removed - let mut iter2 = PersistantElementIter::new(); - // div - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // p - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // "hello" - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // p - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // "world" - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // p - iter2.next(&rdom).id(); - // "hello world" - iter2.next(&rdom).id(); +// // this will end on the node that is removed +// let mut iter1 = PersistantElementIter::new(); +// // this will end on the after node that is removed +// let mut iter2 = PersistantElementIter::new(); +// // div +// iter1.next(&rdom).id(); +// iter2.next(&rdom).id(); +// // p +// iter1.next(&rdom).id(); +// iter2.next(&rdom).id(); +// // "hello" +// iter1.next(&rdom).id(); +// iter2.next(&rdom).id(); +// // p +// iter1.next(&rdom).id(); +// iter2.next(&rdom).id(); +// // "world" +// iter1.next(&rdom).id(); +// iter2.next(&rdom).id(); +// // p +// iter2.next(&rdom).id(); +// // "hello world" +// iter2.next(&rdom).id(); - iter1.prune(&update, &rdom); - iter2.prune(&update, &rdom); - let _to_update = rdom.apply_mutations(vec![update]); +// iter1.prune(&update, &rdom); +// iter2.prune(&update, &rdom); +// let _to_update = rdom.apply_mutations(vec![update]); - let p_tag = "p".to_string(); - let idx = iter1.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - let text = "hello world".to_string(); - let idx = iter1.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Text { text, .. } - )); - let div_tag = "div".to_string(); - let idx = iter2.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); -} +// let p_tag = "p".to_string(); +// let idx = iter1.next(&rdom).id(); +// assert!(matches!( +// &rdom[idx].node_data.node_type, +// NodeType::Element { tag: p_tag, .. } +// )); +// let text = "hello world".to_string(); +// let idx = iter1.next(&rdom).id(); +// assert!(matches!( +// &rdom[idx].node_data.node_type, +// NodeType::Text { text, .. } +// )); +// let div_tag = "div".to_string(); +// let idx = iter2.next(&rdom).id(); +// assert!(matches!( +// &rdom[idx].node_data.node_type, +// NodeType::Element { tag: div_tag, .. } +// )); +// } -#[test] -#[allow(unused_variables)] -fn persist_instertions_before() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } - let vdom = VirtualDom::new(Base); - let (build, update) = vdom.diff_lazynodes( - rsx_without_templates! { - div{ - p{ - key: "1", - "hello" - } - p{ - key: "3", - "hello world" - } - } - }, - rsx_without_templates! { - div{ - p{ - key: "1", - "hello" - } - p{ - key: "2", - "world" - } - p{ - key: "3", - "hello world" - } - } - }, - ); +// #[test] +// #[allow(unused_variables)] +// fn persist_instertions_before() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } +// let vdom = VirtualDom::new(Base); +// let (build, update) = vdom.diff_lazynodes( +// rsx_without_templates! { +// div{ +// p{ +// key: "1", +// "hello" +// } +// p{ +// key: "3", +// "hello world" +// } +// } +// }, +// rsx_without_templates! { +// div{ +// p{ +// key: "1", +// "hello" +// } +// p{ +// key: "2", +// "world" +// } +// p{ +// key: "3", +// "hello world" +// } +// } +// }, +// ); - let mut rdom: RealDom = RealDom::new(); +// let mut rdom: RealDom = RealDom::new(); - let _to_update = rdom.apply_mutations(vec![build]); +// let _to_update = rdom.apply_mutations(vec![build]); - let mut iter = PersistantElementIter::new(); - // div - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "hello" - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "hello world" - iter.next(&rdom).id(); +// let mut iter = PersistantElementIter::new(); +// // div +// iter.next(&rdom).id(); +// // p +// iter.next(&rdom).id(); +// // "hello" +// iter.next(&rdom).id(); +// // p +// iter.next(&rdom).id(); +// // "hello world" +// iter.next(&rdom).id(); - iter.prune(&update, &rdom); - let _to_update = rdom.apply_mutations(vec![update]); +// iter.prune(&update, &rdom); +// let _to_update = rdom.apply_mutations(vec![update]); - let p_tag = "div".to_string(); - let idx = iter.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); -} +// let p_tag = "div".to_string(); +// let idx = iter.next(&rdom).id(); +// assert!(matches!( +// &rdom[idx].node_data.node_type, +// NodeType::Element { tag: p_tag, .. } +// )); +// } -#[test] -#[allow(unused_variables)] -fn persist_instertions_after() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } - let vdom = VirtualDom::new(Base); - let (build, update) = vdom.diff_lazynodes( - rsx_without_templates! { - div{ - p{ - key: "1", - "hello" - } - p{ - key: "2", - "world" - } - } - }, - rsx_without_templates! { - div{ - p{ - key: "1", - "hello" - } - p{ - key: "2", - "world" - } - p{ - key: "3", - "hello world" - } - } - }, - ); +// #[test] +// #[allow(unused_variables)] +// fn persist_instertions_after() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } +// let vdom = VirtualDom::new(Base); +// let (build, update) = vdom.diff_lazynodes( +// rsx_without_templates! { +// div{ +// p{ +// key: "1", +// "hello" +// } +// p{ +// key: "2", +// "world" +// } +// } +// }, +// rsx_without_templates! { +// div{ +// p{ +// key: "1", +// "hello" +// } +// p{ +// key: "2", +// "world" +// } +// p{ +// key: "3", +// "hello world" +// } +// } +// }, +// ); - let mut rdom: RealDom = RealDom::new(); +// let mut rdom: RealDom = RealDom::new(); - let _to_update = rdom.apply_mutations(vec![build]); +// let _to_update = rdom.apply_mutations(vec![build]); - let mut iter = PersistantElementIter::new(); - // div - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "hello" - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "world" - iter.next(&rdom).id(); +// let mut iter = PersistantElementIter::new(); +// // div +// iter.next(&rdom).id(); +// // p +// iter.next(&rdom).id(); +// // "hello" +// iter.next(&rdom).id(); +// // p +// iter.next(&rdom).id(); +// // "world" +// iter.next(&rdom).id(); - iter.prune(&update, &rdom); - let _to_update = rdom.apply_mutations(vec![update]); +// iter.prune(&update, &rdom); +// let _to_update = rdom.apply_mutations(vec![update]); - let p_tag = "p".to_string(); - let idx = iter.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - let text = "hello world".to_string(); - let idx = iter.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Text { text, .. } - )); -} +// let p_tag = "p".to_string(); +// let idx = iter.next(&rdom).id(); +// assert!(matches!( +// &rdom[idx].node_data.node_type, +// NodeType::Element { tag: p_tag, .. } +// )); +// let text = "hello world".to_string(); +// let idx = iter.next(&rdom).id(); +// assert!(matches!( +// &rdom[idx].node_data.node_type, +// NodeType::Text { text, .. } +// )); +// } diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs index 2123ee1f4..2e6a04dec 100644 --- a/packages/native-core-macro/tests/update_state.rs +++ b/packages/native-core-macro/tests/update_state.rs @@ -1,516 +1,516 @@ -use anymap::AnyMap; -use dioxus::core::ElementId; -use dioxus::core::{AttributeValue, DomEdit, Mutations}; -use dioxus::core_macro::rsx_without_templates; -use dioxus::prelude::*; -use dioxus_native_core::real_dom::*; -use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; -use dioxus_native_core::{node_ref::*, RealNodeId}; -use dioxus_native_core_macro::State; +// use anymap::AnyMap; +// use dioxus::core::ElementId; +// use dioxus::core::{AttributeValue, DomEdit, Mutations}; +// use dioxus::core_macro::rsx_without_templates; +// use dioxus::prelude::*; +// use dioxus_native_core::real_dom::*; +// use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; +// use dioxus_native_core::{node_ref::*, RealNodeId}; +// use dioxus_native_core_macro::State; -#[derive(Debug, Clone, Default, State)] -struct CallCounterStatePart1 { - #[child_dep_state(child_counter)] - child_counter: ChildDepCallCounter, -} +// #[derive(Debug, Clone, Default, State)] +// struct CallCounterStatePart1 { +// #[child_dep_state(child_counter)] +// child_counter: ChildDepCallCounter, +// } -#[derive(Debug, Clone, Default, State)] -struct CallCounterStatePart2 { - #[parent_dep_state(parent_counter)] - parent_counter: ParentDepCallCounter, -} +// #[derive(Debug, Clone, Default, State)] +// struct CallCounterStatePart2 { +// #[parent_dep_state(parent_counter)] +// parent_counter: ParentDepCallCounter, +// } -#[derive(Debug, Clone, Default, State)] -struct CallCounterStatePart3 { - #[node_dep_state()] - node_counter: NodeDepCallCounter, -} +// #[derive(Debug, Clone, Default, State)] +// struct CallCounterStatePart3 { +// #[node_dep_state()] +// node_counter: NodeDepCallCounter, +// } -#[derive(Debug, Clone, Default, State)] -struct CallCounterState { - #[child_dep_state(child_counter)] - child_counter: ChildDepCallCounter, - #[state] - part2: CallCounterStatePart2, - #[parent_dep_state(parent_counter)] - parent_counter: ParentDepCallCounter, - #[state] - part1: CallCounterStatePart1, - #[state] - part3: CallCounterStatePart3, - #[node_dep_state()] - node_counter: NodeDepCallCounter, -} +// #[derive(Debug, Clone, Default, State)] +// struct CallCounterState { +// #[child_dep_state(child_counter)] +// child_counter: ChildDepCallCounter, +// #[state] +// part2: CallCounterStatePart2, +// #[parent_dep_state(parent_counter)] +// parent_counter: ParentDepCallCounter, +// #[state] +// part1: CallCounterStatePart1, +// #[state] +// part3: CallCounterStatePart3, +// #[node_dep_state()] +// node_counter: NodeDepCallCounter, +// } -#[derive(Debug, Clone, Default)] -struct ChildDepCallCounter(u32); -impl ChildDepState for ChildDepCallCounter { - type Ctx = (); - type DepState = Self; - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce<'a>( - &mut self, - _: NodeView, - _: impl Iterator, - _: &Self::Ctx, - ) -> bool - where - Self::DepState: 'a, - { - self.0 += 1; - true - } -} +// #[derive(Debug, Clone, Default)] +// struct ChildDepCallCounter(u32); +// impl ChildDepState for ChildDepCallCounter { +// type Ctx = (); +// type DepState = Self; +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce<'a>( +// &mut self, +// _: NodeView, +// _: impl Iterator, +// _: &Self::Ctx, +// ) -> bool +// where +// Self::DepState: 'a, +// { +// self.0 += 1; +// true +// } +// } -#[derive(Debug, Clone, Default)] -struct ParentDepCallCounter(u32); -impl ParentDepState for ParentDepCallCounter { - type Ctx = (); - type DepState = Self; - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce( - &mut self, - _node: NodeView, - _parent: Option<&Self::DepState>, - _ctx: &Self::Ctx, - ) -> bool { - self.0 += 1; - true - } -} +// #[derive(Debug, Clone, Default)] +// struct ParentDepCallCounter(u32); +// impl ParentDepState for ParentDepCallCounter { +// type Ctx = (); +// type DepState = Self; +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce( +// &mut self, +// _node: NodeView, +// _parent: Option<&Self::DepState>, +// _ctx: &Self::Ctx, +// ) -> bool { +// self.0 += 1; +// true +// } +// } -#[derive(Debug, Clone, Default)] -struct NodeDepCallCounter(u32); -impl NodeDepState<()> for NodeDepCallCounter { - type Ctx = (); - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { - self.0 += 1; - true - } -} +// #[derive(Debug, Clone, Default)] +// struct NodeDepCallCounter(u32); +// impl NodeDepState<()> for NodeDepCallCounter { +// type Ctx = (); +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { +// self.0 += 1; +// true +// } +// } -#[allow(clippy::vec_box)] -#[derive(Debug, Clone, PartialEq, Default)] -struct BubbledUpStateTester(Option, Vec>); -impl ChildDepState for BubbledUpStateTester { - type Ctx = u32; - type DepState = Self; - const NODE_MASK: NodeMask = NodeMask::new().with_tag(); - fn reduce<'a>( - &mut self, - node: NodeView, - children: impl Iterator, - ctx: &Self::Ctx, - ) -> bool - where - Self::DepState: 'a, - { - assert_eq!(*ctx, 42); - *self = BubbledUpStateTester( - node.tag().map(|s| s.to_string()), - children.into_iter().map(|c| Box::new(c.clone())).collect(), - ); - true - } -} +// #[allow(clippy::vec_box)] +// #[derive(Debug, Clone, PartialEq, Default)] +// struct BubbledUpStateTester(Option, Vec>); +// impl ChildDepState for BubbledUpStateTester { +// type Ctx = u32; +// type DepState = Self; +// const NODE_MASK: NodeMask = NodeMask::new().with_tag(); +// fn reduce<'a>( +// &mut self, +// node: NodeView, +// children: impl Iterator, +// ctx: &Self::Ctx, +// ) -> bool +// where +// Self::DepState: 'a, +// { +// assert_eq!(*ctx, 42); +// *self = BubbledUpStateTester( +// node.tag().map(|s| s.to_string()), +// children.into_iter().map(|c| Box::new(c.clone())).collect(), +// ); +// true +// } +// } -#[derive(Debug, Clone, PartialEq, Default)] -struct PushedDownStateTester(Option, Option>); -impl ParentDepState for PushedDownStateTester { - type Ctx = u32; - type DepState = Self; - const NODE_MASK: NodeMask = NodeMask::new().with_tag(); - fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool { - assert_eq!(*ctx, 42); - *self = PushedDownStateTester( - node.tag().map(|s| s.to_string()), - parent.map(|c| Box::new(c.clone())), - ); - true - } -} +// #[derive(Debug, Clone, PartialEq, Default)] +// struct PushedDownStateTester(Option, Option>); +// impl ParentDepState for PushedDownStateTester { +// type Ctx = u32; +// type DepState = Self; +// const NODE_MASK: NodeMask = NodeMask::new().with_tag(); +// fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool { +// assert_eq!(*ctx, 42); +// *self = PushedDownStateTester( +// node.tag().map(|s| s.to_string()), +// parent.map(|c| Box::new(c.clone())), +// ); +// true +// } +// } -#[derive(Debug, Clone, PartialEq, Default)] -struct NodeStateTester(Option, Vec<(String, String)>); -impl NodeDepState<()> for NodeStateTester { - type Ctx = u32; - const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag(); - fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool { - assert_eq!(*ctx, 42); - *self = NodeStateTester( - node.tag().map(|s| s.to_string()), - node.attributes() - .map(|iter| { - iter.map(|a| { - ( - a.attribute.name.to_string(), - a.value.as_text().unwrap().to_string(), - ) - }) - .collect() - }) - .unwrap_or_default(), - ); - true - } -} +// #[derive(Debug, Clone, PartialEq, Default)] +// struct NodeStateTester(Option, Vec<(String, String)>); +// impl NodeDepState<()> for NodeStateTester { +// type Ctx = u32; +// const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag(); +// fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool { +// assert_eq!(*ctx, 42); +// *self = NodeStateTester( +// node.tag().map(|s| s.to_string()), +// node.attributes() +// .map(|iter| { +// iter.map(|a| { +// ( +// a.attribute.name.to_string(), +// a.value.as_text().unwrap().to_string(), +// ) +// }) +// .collect() +// }) +// .unwrap_or_default(), +// ); +// true +// } +// } -#[derive(State, Clone, Default, Debug)] -struct StateTester { - #[child_dep_state(bubbled, u32)] - bubbled: BubbledUpStateTester, - #[parent_dep_state(pushed, u32)] - pushed: PushedDownStateTester, - #[node_dep_state(NONE, u32)] - node: NodeStateTester, -} +// #[derive(State, Clone, Default, Debug)] +// struct StateTester { +// #[child_dep_state(bubbled, u32)] +// bubbled: BubbledUpStateTester, +// #[parent_dep_state(pushed, u32)] +// pushed: PushedDownStateTester, +// #[node_dep_state(NONE, u32)] +// node: NodeStateTester, +// } -#[test] -fn state_initial() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div { - p{} - h1{} - }) - } +// #[test] +// fn state_initial() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div { +// p{} +// h1{} +// }) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx! { - div { - p{ - color: "red" - } - h1{} - } - }); +// let mutations = vdom.create_vnodes(rsx! { +// div { +// p{ +// color: "red" +// } +// h1{} +// } +// }); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let nodes_updated = dom.apply_mutations(vec![mutations]); - let mut ctx = AnyMap::new(); - ctx.insert(42u32); - let _to_rerender = dom.update_state(nodes_updated, ctx); +// let nodes_updated = dom.apply_mutations(vec![mutations]); +// let mut ctx = AnyMap::new(); +// ctx.insert(42u32); +// let _to_rerender = dom.update_state(nodes_updated, ctx); - let root_div = &dom[RealNodeId::ElementId(ElementId(2))]; - assert_eq!(root_div.state.bubbled.0, Some("div".to_string())); - assert_eq!( - root_div.state.bubbled.1, - vec![ - Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())), - Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new())) - ] - ); - assert_eq!(root_div.state.pushed.0, Some("div".to_string())); - assert_eq!( - root_div.state.pushed.1, - Some(Box::new(PushedDownStateTester( - Some("Root".to_string()), - None - ))) - ); - assert_eq!(root_div.state.node.0, Some("div".to_string())); - assert_eq!(root_div.state.node.1, vec![]); +// let root_div = &dom[RealNodeId::ElementId(ElementId(2))]; +// assert_eq!(root_div.state.bubbled.0, Some("div".to_string())); +// assert_eq!( +// root_div.state.bubbled.1, +// vec![ +// Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())), +// Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new())) +// ] +// ); +// assert_eq!(root_div.state.pushed.0, Some("div".to_string())); +// assert_eq!( +// root_div.state.pushed.1, +// Some(Box::new(PushedDownStateTester( +// Some("Root".to_string()), +// None +// ))) +// ); +// assert_eq!(root_div.state.node.0, Some("div".to_string())); +// assert_eq!(root_div.state.node.1, vec![]); - let child_p = &dom[RealNodeId::UnaccessableId(3)]; - assert_eq!(child_p.state.bubbled.0, Some("p".to_string())); - assert_eq!(child_p.state.bubbled.1, Vec::new()); - assert_eq!(child_p.state.pushed.0, Some("p".to_string())); - assert_eq!( - child_p.state.pushed.1, - Some(Box::new(PushedDownStateTester( - Some("div".to_string()), - Some(Box::new(PushedDownStateTester( - Some("Root".to_string()), - None - ))) - ))) - ); - assert_eq!(child_p.state.node.0, Some("p".to_string())); - assert_eq!( - child_p.state.node.1, - vec![("color".to_string(), "red".to_string())] - ); +// let child_p = &dom[RealNodeId::UnaccessableId(3)]; +// assert_eq!(child_p.state.bubbled.0, Some("p".to_string())); +// assert_eq!(child_p.state.bubbled.1, Vec::new()); +// assert_eq!(child_p.state.pushed.0, Some("p".to_string())); +// assert_eq!( +// child_p.state.pushed.1, +// Some(Box::new(PushedDownStateTester( +// Some("div".to_string()), +// Some(Box::new(PushedDownStateTester( +// Some("Root".to_string()), +// None +// ))) +// ))) +// ); +// assert_eq!(child_p.state.node.0, Some("p".to_string())); +// assert_eq!( +// child_p.state.node.1, +// vec![("color".to_string(), "red".to_string())] +// ); - let child_h1 = &dom[RealNodeId::UnaccessableId(4)]; - assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string())); - assert_eq!(child_h1.state.bubbled.1, Vec::new()); - assert_eq!(child_h1.state.pushed.0, Some("h1".to_string())); - assert_eq!( - child_h1.state.pushed.1, - Some(Box::new(PushedDownStateTester( - Some("div".to_string()), - Some(Box::new(PushedDownStateTester( - Some("Root".to_string()), - None - ))) - ))) - ); - assert_eq!(child_h1.state.node.0, Some("h1".to_string())); - assert_eq!(child_h1.state.node.1, vec![]); -} +// let child_h1 = &dom[RealNodeId::UnaccessableId(4)]; +// assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string())); +// assert_eq!(child_h1.state.bubbled.1, Vec::new()); +// assert_eq!(child_h1.state.pushed.0, Some("h1".to_string())); +// assert_eq!( +// child_h1.state.pushed.1, +// Some(Box::new(PushedDownStateTester( +// Some("div".to_string()), +// Some(Box::new(PushedDownStateTester( +// Some("Root".to_string()), +// None +// ))) +// ))) +// ); +// assert_eq!(child_h1.state.node.0, Some("h1".to_string())); +// assert_eq!(child_h1.state.node.1, vec![]); +// } -#[test] -fn state_reduce_parent_called_minimally_on_update() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div { - width: "100%", - div{ - div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - }) - } +// #[test] +// fn state_reduce_parent_called_minimally_on_update() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div { +// width: "100%", +// div{ +// div{ +// p{} +// } +// p{ +// "hello" +// } +// div{ +// h1{} +// } +// p{ +// "world" +// } +// } +// }) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx_without_templates! { - div { - width: "100%", - div{ - div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - } - }); +// let mutations = vdom.create_vnodes(rsx_without_templates! { +// div { +// width: "100%", +// div{ +// div{ +// p{} +// } +// p{ +// "hello" +// } +// div{ +// h1{} +// } +// p{ +// "world" +// } +// } +// } +// }); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let nodes_updated = dom.apply_mutations(vec![mutations]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - let nodes_updated = dom.apply_mutations(vec![Mutations { - edits: vec![DomEdit::SetAttribute { - root: Some(1), - field: "width", - value: AttributeValue::Text("99%"), - ns: Some("style"), - }], - dirty_scopes: rustc_hash::FxHashSet::default(), - refs: Vec::new(), - }]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +// let nodes_updated = dom.apply_mutations(vec![mutations]); +// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +// let nodes_updated = dom.apply_mutations(vec![Mutations { +// edits: vec![DomEdit::SetAttribute { +// root: Some(1), +// field: "width", +// value: AttributeValue::Text("99%"), +// ns: Some("style"), +// }], +// dirty_scopes: rustc_hash::FxHashSet::default(), +// refs: Vec::new(), +// }]); +// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - dom.traverse_depth_first(|n| { - assert_eq!(n.state.part2.parent_counter.0, 2); - assert_eq!(n.state.parent_counter.0, 2); - }); -} +// dom.traverse_depth_first(|n| { +// assert_eq!(n.state.part2.parent_counter.0, 2); +// assert_eq!(n.state.parent_counter.0, 2); +// }); +// } -#[test] -fn state_reduce_child_called_minimally_on_update() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - cx.render(rsx_without_templates!(div { - div{ - div{ - p{ - width: "100%", - } - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - })) - } +// #[test] +// fn state_reduce_child_called_minimally_on_update() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// cx.render(rsx_without_templates!(div { +// div{ +// div{ +// p{ +// width: "100%", +// } +// } +// p{ +// "hello" +// } +// div{ +// h1{} +// } +// p{ +// "world" +// } +// } +// })) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx_without_templates! { - div { - div{ - div{ - p{ - width: "100%", - } - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - } - }); +// let mutations = vdom.create_vnodes(rsx_without_templates! { +// div { +// div{ +// div{ +// p{ +// width: "100%", +// } +// } +// p{ +// "hello" +// } +// div{ +// h1{} +// } +// p{ +// "world" +// } +// } +// } +// }); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let nodes_updated = dom.apply_mutations(vec![mutations]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - let nodes_updated = dom.apply_mutations(vec![Mutations { - edits: vec![DomEdit::SetAttribute { - root: Some(4), - field: "width", - value: AttributeValue::Text("99%"), - ns: Some("style"), - }], - dirty_scopes: rustc_hash::FxHashSet::default(), - refs: Vec::new(), - }]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +// let nodes_updated = dom.apply_mutations(vec![mutations]); +// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +// let nodes_updated = dom.apply_mutations(vec![Mutations { +// edits: vec![DomEdit::SetAttribute { +// root: Some(4), +// field: "width", +// value: AttributeValue::Text("99%"), +// ns: Some("style"), +// }], +// dirty_scopes: rustc_hash::FxHashSet::default(), +// refs: Vec::new(), +// }]); +// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - dom.traverse_depth_first(|n| { - assert_eq!( - n.state.part1.child_counter.0, - if let Some(RealNodeId::ElementId(ElementId(id))) = n.node_data.id { - if id > 4 { - 1 - } else { - 2 - } - } else { - panic!() - } - ); - assert_eq!( - n.state.child_counter.0, - if let Some(RealNodeId::ElementId(ElementId(id))) = n.node_data.id { - if id > 4 { - 1 - } else { - 2 - } - } else { - panic!() - } - ); - }); -} +// dom.traverse_depth_first(|n| { +// assert_eq!( +// n.state.part1.child_counter.0, +// if let Some(RealNodeId::ElementId(ElementId(id))) = n.node_data.id { +// if id > 4 { +// 1 +// } else { +// 2 +// } +// } else { +// panic!() +// } +// ); +// assert_eq!( +// n.state.child_counter.0, +// if let Some(RealNodeId::ElementId(ElementId(id))) = n.node_data.id { +// if id > 4 { +// 1 +// } else { +// 2 +// } +// } else { +// panic!() +// } +// ); +// }); +// } -#[derive(Debug, Clone, Default, State)] -struct UnorderedDependanciesState { - #[node_dep_state(c)] - b: BDepCallCounter, - #[node_dep_state()] - c: CDepCallCounter, - #[node_dep_state(b)] - a: ADepCallCounter, -} +// #[derive(Debug, Clone, Default, State)] +// struct UnorderedDependanciesState { +// #[node_dep_state(c)] +// b: BDepCallCounter, +// #[node_dep_state()] +// c: CDepCallCounter, +// #[node_dep_state(b)] +// a: ADepCallCounter, +// } -#[derive(Debug, Clone, Default, PartialEq)] -struct ADepCallCounter(usize, BDepCallCounter); -impl<'a> NodeDepState<(&'a BDepCallCounter,)> for ADepCallCounter { - type Ctx = (); - const NODE_MASK: NodeMask = NodeMask::NONE; - fn reduce( - &mut self, - _node: NodeView, - (sibling,): (&'a BDepCallCounter,), - _ctx: &Self::Ctx, - ) -> bool { - self.0 += 1; - self.1 = sibling.clone(); - true - } -} +// #[derive(Debug, Clone, Default, PartialEq)] +// struct ADepCallCounter(usize, BDepCallCounter); +// impl<'a> NodeDepState<(&'a BDepCallCounter,)> for ADepCallCounter { +// type Ctx = (); +// const NODE_MASK: NodeMask = NodeMask::NONE; +// fn reduce( +// &mut self, +// _node: NodeView, +// (sibling,): (&'a BDepCallCounter,), +// _ctx: &Self::Ctx, +// ) -> bool { +// self.0 += 1; +// self.1 = sibling.clone(); +// true +// } +// } -#[derive(Debug, Clone, Default, PartialEq)] -struct BDepCallCounter(usize, CDepCallCounter); -impl<'a> NodeDepState<(&'a CDepCallCounter,)> for BDepCallCounter { - type Ctx = (); - const NODE_MASK: NodeMask = NodeMask::NONE; - fn reduce( - &mut self, - _node: NodeView, - (sibling,): (&'a CDepCallCounter,), - _ctx: &Self::Ctx, - ) -> bool { - self.0 += 1; - self.1 = sibling.clone(); - true - } -} +// #[derive(Debug, Clone, Default, PartialEq)] +// struct BDepCallCounter(usize, CDepCallCounter); +// impl<'a> NodeDepState<(&'a CDepCallCounter,)> for BDepCallCounter { +// type Ctx = (); +// const NODE_MASK: NodeMask = NodeMask::NONE; +// fn reduce( +// &mut self, +// _node: NodeView, +// (sibling,): (&'a CDepCallCounter,), +// _ctx: &Self::Ctx, +// ) -> bool { +// self.0 += 1; +// self.1 = sibling.clone(); +// true +// } +// } -#[derive(Debug, Clone, Default, PartialEq)] -struct CDepCallCounter(usize); -impl NodeDepState<()> for CDepCallCounter { - type Ctx = (); - const NODE_MASK: NodeMask = NodeMask::ALL; - fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { - self.0 += 1; - true - } -} +// #[derive(Debug, Clone, Default, PartialEq)] +// struct CDepCallCounter(usize); +// impl NodeDepState<()> for CDepCallCounter { +// type Ctx = (); +// const NODE_MASK: NodeMask = NodeMask::ALL; +// fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { +// self.0 += 1; +// true +// } +// } -#[test] -fn dependancies_order_independant() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div { - width: "100%", - p{ - "hello" - } - }) - } +// #[test] +// fn dependancies_order_independant() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div { +// width: "100%", +// p{ +// "hello" +// } +// }) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mutations = vdom.create_vnodes(rsx! { - div { - width: "100%", - p{ - "hello" - } - } - }); +// let mutations = vdom.create_vnodes(rsx! { +// div { +// width: "100%", +// p{ +// "hello" +// } +// } +// }); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let nodes_updated = dom.apply_mutations(vec![mutations]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +// let nodes_updated = dom.apply_mutations(vec![mutations]); +// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - let c = CDepCallCounter(1); - let b = BDepCallCounter(1, c.clone()); - let a = ADepCallCounter(1, b.clone()); - dom.traverse_depth_first(|n| { - assert_eq!(&n.state.a, &a); - assert_eq!(&n.state.b, &b); - assert_eq!(&n.state.c, &c); - }); -} +// let c = CDepCallCounter(1); +// let b = BDepCallCounter(1, c.clone()); +// let a = ADepCallCounter(1, b.clone()); +// dom.traverse_depth_first(|n| { +// assert_eq!(&n.state.a, &a); +// assert_eq!(&n.state.b, &b); +// assert_eq!(&n.state.c, &c); +// }); +// } -#[derive(Clone, Default, State)] -struct DependanciesStateTest { - #[node_dep_state(c)] - b: BDepCallCounter, - #[node_dep_state()] - c: CDepCallCounter, - #[node_dep_state(b)] - a: ADepCallCounter, - #[state] - child: UnorderedDependanciesState, -} +// #[derive(Clone, Default, State)] +// struct DependanciesStateTest { +// #[node_dep_state(c)] +// b: BDepCallCounter, +// #[node_dep_state()] +// c: CDepCallCounter, +// #[node_dep_state(b)] +// a: ADepCallCounter, +// #[state] +// child: UnorderedDependanciesState, +// } diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 14089c669..13033573e 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -1,7 +1,9 @@ use std::hash::BuildHasherDefault; +pub use node_ref::NodeMask; +pub use passes::AnyPass; use rustc_hash::FxHasher; -use tree::NodeId; +pub use tree::NodeId; pub mod layout_attributes; pub mod node; diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index b9ca35c39..dadb3909a 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,6 +1,6 @@ use dioxus_core::{ElementId, Mutations, TemplateNode}; use rustc_hash::{FxHashMap, FxHashSet}; -use std::ops::{Index, IndexMut}; +use std::ops::{Deref, DerefMut, Index, IndexMut}; use crate::node::{Node, NodeType, OwnedAttributeDiscription, OwnedAttributeValue}; use crate::node_ref::{AttributeMask, NodeMask}; @@ -26,7 +26,7 @@ fn mark_dirty( /// To get started implement [crate::state::ParentDepState], [crate::state::NodeDepState], or [crate::state::ChildDepState] and call [RealDom::apply_mutations] to update the dom and [RealDom::update_state] to update the state of the nodes. #[derive(Debug)] pub struct RealDom { - tree: Tree>, + pub tree: Tree>, /// a map from element id to real node id node_id_mapping: Vec>, nodes_listening: FxHashMap>, @@ -401,6 +401,20 @@ impl RealDom { } } +impl Deref for RealDom { + type Target = Tree>; + + fn deref(&self) -> &Self::Target { + &self.tree + } +} + +impl DerefMut for RealDom { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.tree + } +} + impl Index for RealDom { type Output = Node; From 0e17199e7a8f6209354f8111081e9a697ebb9bcd Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 3 Dec 2022 22:26:37 -0600 Subject: [PATCH 21/43] more progress on macro --- packages/native-core-macro/src/lib.rs | 84 +++-- .../tests/called_minimally_on_build.rs | 291 +++++++++--------- packages/native-core/src/lib.rs | 6 +- packages/native-core/src/passes.rs | 4 +- 4 files changed, 209 insertions(+), 176 deletions(-) diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index b2c259efa..a1dc397c3 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -3,7 +3,7 @@ extern crate proc_macro; mod sorted_slice; use proc_macro::TokenStream; -use quote::{quote, ToTokens}; +use quote::{quote, ToTokens, __private::Span}; use sorted_slice::StrSlice; use syn::parenthesized; use syn::parse::ParseBuffer; @@ -148,8 +148,17 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { let strct = Struct::new(type_name.clone(), &fields); match StateStruct::parse(&fields, &strct) { Ok(state_strct) => { - let member_types1 = state_strct.state_members.iter().map(|m| &m.mem.ty); - let member_types2 = state_strct.state_members.iter().map(|m| &m.mem.ty); + let passes = state_strct.state_members.iter().map(|m| { + let unit = &m.mem.unit_type; + match m.dep_kind { + DependencyKind::Node => quote! {dioxus_native_core::AnyPass::Node(&#unit)}, + DependencyKind::Child => quote! {dioxus_native_core::AnyPass::Upward(&#unit)}, + DependencyKind::Parent => { + quote! {dioxus_native_core::AnyPass::Downward(&#unit)} + } + } + }); + let member_types = state_strct.state_members.iter().map(|m| &m.mem.ty); let impl_members = state_strct .state_members .iter() @@ -162,9 +171,9 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { #(#impl_members)* impl State for #type_name { const PASSES: &'static [dioxus_native_core::AnyPass>] = &[ - #(dioxus_native_core::AnyPass(&#member_types1)),* + #(#passes),* ]; - const MASKS: &'static [dioxus_native_core::NodeMask] = &[#(#member_types2::NODE_MASK),*]; + const MASKS: &'static [dioxus_native_core::NodeMask] = &[#(#member_types::NODE_MASK),*]; } }; gen.into() @@ -183,7 +192,7 @@ impl Struct { let members = fields .iter() .enumerate() - .filter_map(|(i, f)| Member::parse(f, 1 << i)) + .filter_map(|(i, f)| Member::parse(f, i as u64)) .collect(); Self { name, members } } @@ -285,6 +294,7 @@ impl Parse for Dependency { struct Member { id: u64, ty: Type, + unit_type: Ident, ident: Ident, } @@ -293,6 +303,11 @@ impl Member { Some(Self { id, ty: field.ty.clone(), + unit_type: Ident::new( + ("_Unit".to_string() + field.ty.to_token_stream().to_string().as_str()).as_str(), + Span::call_site(), + ) + .into(), ident: field.ident.as_ref()?.clone(), }) } @@ -378,13 +393,14 @@ impl<'a> StateMember<'a> { }; let ty = &self.mem.ty; + let unit_type = &self.mem.unit_type; let node_view = quote!(dioxus_native_core::node_ref::NodeView::new(unsafe{&*{&node.node_data as *const _}}, #ty::NODE_MASK)); let dep_idents = self.dep_mems.iter().map(|m| &m.ident); let impl_specific = match self.dep_kind { DependencyKind::Node => { quote! { - impl NodePass<#parent_type> for #ty { - fn pass(&self, node: &mut T, ctx: &SendAnyMap) -> bool { + impl dioxus_native_core::NodePass> for #unit_type { + fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type>, ctx: &dioxus_native_core::SendAnyMap) -> bool { node.state.#ident.reduce(#node_view, (#(&node.state.#dep_idents,)*), #get_ctx) } } @@ -394,12 +410,12 @@ impl<'a> StateMember<'a> { let update = if self.dep_mems.iter().any(|m| m.id == self.mem.id) { quote! { if update { - PassReturn{ + dioxus_native_core::PassReturn{ progress: true, mark_dirty: true, } } else { - PassReturn{ + dioxus_native_core::PassReturn{ progress: false, mark_dirty: false, } @@ -408,12 +424,12 @@ impl<'a> StateMember<'a> { } else { quote! { if update { - PassReturn{ + dioxus_native_core::PassReturn{ progress: false, mark_dirty: true, } } else { - PassReturn{ + dioxus_native_core::PassReturn{ progress: false, mark_dirty: false, } @@ -421,13 +437,13 @@ impl<'a> StateMember<'a> { } }; quote!( - pub trait UpwardPass: Pass { + impl dioxus_native_core::UpwardPass> for #unit_type{ fn pass<'a>( &self, - node: &mut T, - children: &mut dyn Iterator, - ctx: &SendAnyMap, - ) -> PassReturn { + node: &mut dioxus_native_core::node::Node<#parent_type>, + children: &mut dyn Iterator>, + ctx: &dioxus_native_core::SendAnyMap, + ) -> dioxus_native_core::PassReturn { let update = node.state.#ident.reduce(#node_view, children.map(|c| (#(&c.state.#dep_idents,)*)), #get_ctx); #update } @@ -438,12 +454,12 @@ impl<'a> StateMember<'a> { let update = if self.dep_mems.iter().any(|m| m.id == self.mem.id) { quote! { if update { - PassReturn{ + dioxus_native_core::PassReturn{ progress: true, mark_dirty: true, } } else { - PassReturn{ + dioxus_native_core::PassReturn{ progress: false, mark_dirty: false, } @@ -452,12 +468,12 @@ impl<'a> StateMember<'a> { } else { quote! { if update { - PassReturn{ + dioxus_native_core::PassReturn{ progress: false, mark_dirty: true, } } else { - PassReturn{ + dioxus_native_core::PassReturn{ progress: false, mark_dirty: false, } @@ -465,9 +481,9 @@ impl<'a> StateMember<'a> { } }; quote!( - impl DownwardPass<#parent_type> for #ty { - fn pass(&self, node: &mut T, parent: Option<&mut T>, ctx: &SendAnyMap) -> PassReturn{ - let update = current_state.state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.state.#dep_idents,)*)), #get_ctx); + impl dioxus_native_core::DownwardPass> for #unit_type { + fn pass(&self, node: &mut dioxus_native_core::node::Node<#parent_type>, parent: Option<&mut dioxus_native_core::node::Node<#parent_type>>, ctx: &dioxus_native_core::SendAnyMap) -> dioxus_native_core::PassReturn{ + let update = node.state.#ident.reduce(#node_view, parent.as_ref().map(|p| (#(&p.state.#dep_idents,)*)), #get_ctx); #update } } @@ -483,19 +499,21 @@ impl<'a> StateMember<'a> { .map(|m| m.id) .fold(self.mem.id, |a, b| a | b); quote! { + #[derive(Clone, Copy)] + struct #unit_type; #impl_specific - impl Pass for #ty { - fn pass_id(&self) -> PassId { - PassId(#pass_id) + impl dioxus_native_core::Pass for #unit_type { + fn pass_id(&self) -> dioxus_native_core::PassId { + dioxus_native_core::PassId(#pass_id) } - fn dependancies(&self) -> &'static [PassId] { - &[#(PassId(#depenancies)),*] + fn dependancies(&self) -> &'static [dioxus_native_core::PassId] { + &[#(dioxus_native_core::PassId(#depenancies)),*] } - fn dependants(&self) -> &'static [PassId] { - &[#(PassId(#dependants)),*] + fn dependants(&self) -> &'static [dioxus_native_core::PassId] { + &[#(dioxus_native_core::PassId(#dependants)),*] } - fn mask(&self) -> MemberMask { - MemberMask(#mask) + fn mask(&self) -> dioxus_native_core::MemberMask { + dioxus_native_core::MemberMask(1 << #mask) } } } diff --git a/packages/native-core-macro/tests/called_minimally_on_build.rs b/packages/native-core-macro/tests/called_minimally_on_build.rs index 7935f9693..24cd446d4 100644 --- a/packages/native-core-macro/tests/called_minimally_on_build.rs +++ b/packages/native-core-macro/tests/called_minimally_on_build.rs @@ -1,161 +1,174 @@ -// use anymap::AnyMap; -// use dioxus::prelude::*; -// use dioxus_native_core::node_ref::*; -// use dioxus_native_core::real_dom::*; -// use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; -// use dioxus_native_core_macro::State; +use anymap::AnyMap; +use dioxus::prelude::*; +use dioxus_native_core::node_ref::*; +use dioxus_native_core::real_dom::*; +use dioxus_native_core::state::{ + ChildDepState, ElementBorrowable, NodeDepState, ParentDepState, State, +}; +use dioxus_native_core::tree::*; +use dioxus_native_core_macro::State; -// macro_rules! dep { -// ( child( $name:ty, $dep:ty ) ) => { -// impl ChildDepState for $name { -// type Ctx = (); -// type DepState = $dep; -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce<'a>( -// &mut self, -// _: NodeView, -// _: impl Iterator, -// _: &Self::Ctx, -// ) -> bool -// where -// Self::DepState: 'a, -// { -// self.0 += 1; -// true -// } -// } -// }; +macro_rules! dep { + ( child( $name:ty, $dep:ty ) ) => { + impl ChildDepState for $name { + type Ctx = (); + type DepState = $dep; + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce<'a>( + &mut self, + _: NodeView, + _: impl Iterator::ElementBorrowed<'a>>, + _: &Self::Ctx, + ) -> bool + where + Self::DepState: 'a, + { + self.0 += 1; + true + } + } + }; -// ( parent( $name:ty, $dep:ty ) ) => { -// impl ParentDepState for $name { -// type Ctx = (); -// type DepState = $dep; -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce(&mut self, _: NodeView, _: Option<&Self::DepState>, _: &Self::Ctx) -> bool { -// self.0 += 1; -// true -// } -// } -// }; + ( parent( $name:ty, $dep:ty ) ) => { + impl ParentDepState for $name { + type Ctx = (); + type DepState = $dep; + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce( + &mut self, + _: NodeView, + _: Option<::ElementBorrowed<'_>>, + _: &Self::Ctx, + ) -> bool { + self.0 += 1; + true + } + } + }; -// ( node( $name:ty, $dep:ty ) ) => { -// impl NodeDepState for $name { -// type Ctx = (); -// type DepState<'a> = $dep; -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce(&mut self, _: NodeView, _: $dep, _: &Self::Ctx) -> bool { -// self.0 += 1; -// true -// } -// } -// }; -// } + ( node( $name:ty, $dep:ty ) ) => { + impl NodeDepState for $name { + type Ctx = (); + type DepState = $dep; + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce( + &mut self, + _: NodeView, + _: ::ElementBorrowed<'_>, + _: &Self::Ctx, + ) -> bool { + self.0 += 1; + true + } + } + }; +} -// macro_rules! test_state{ -// ( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => { -// #[test] -// fn state_reduce_initally_called_minimally() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div { -// div{ -// div{ -// p{} -// } -// p{ -// "hello" -// } -// div{ -// h1{} -// } -// p{ -// "world" -// } -// } -// }) -// } +macro_rules! test_state{ + ( $s:ty, child: ( $( $child:ident ),* ), node: ( $( $node:ident ),* ), parent: ( $( $parent:ident ),* ) ) => { + #[test] + fn state_reduce_initally_called_minimally() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + render!(div { + div{ + div{ + p{} + } + p{ + "hello" + } + div{ + h1{} + } + p{ + "world" + } + } + }) + } -// let vdom = VirtualDom::new(Base); + let vdom = VirtualDom::new(Base); -// let mutations = vdom.create_vnodes(rsx! { -// div { -// div{ -// div{ -// p{} -// } -// p{ -// "hello" -// } -// div{ -// h1{} -// } -// p{ -// "world" -// } -// } -// } -// }); + let mutations = vdom.create_vnodes(rsx! { + div { + div{ + div{ + p{} + } + p{ + "hello" + } + div{ + h1{} + } + p{ + "world" + } + } + } + }); -// let mut dom: RealDom<$s> = RealDom::new(); + let mut dom: RealDom<$s> = RealDom::new(); -// let nodes_updated = dom.apply_mutations(vec![mutations]); -// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); + let nodes_updated = dom.apply_mutations(vec![mutations]); + let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); -// dom.traverse_depth_first(|n| { -// $( -// assert_eq!(n.state.$child.0, 1); -// )* -// $( -// assert_eq!(n.state.$node.0, 1); -// )* -// $( -// assert_eq!(n.state.$parent.0, 1); -// )* -// }); -// } -// } -// } + dom.traverse_depth_first(|n| { + $( + assert_eq!(n.state.$child.0, 1); + )* + $( + assert_eq!(n.state.$node.0, 1); + )* + $( + assert_eq!(n.state.$parent.0, 1); + )* + }); + } + } +} -// mod node_depends_on_child_and_parent { -// use super::*; -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node(i32); -// dep!(node(Node, (&'a Child, &'a Parent))); +mod node_depends_on_child_and_parent { + use super::*; + #[derive(Debug, Clone, Default, PartialEq)] + struct Node(i32); + dep!(node(Node, (Child, Parent))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Child(i32); -// dep!(child(Child, Child)); + #[derive(Debug, Clone, Default, PartialEq)] + struct Child(i32); + dep!(child(Child, (Child,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Parent(i32); -// dep!(parent(Parent, Parent)); + #[derive(Debug, Clone, Default, PartialEq)] + struct Parent(i32); + dep!(parent(Parent, (Parent,))); -// #[derive(Debug, Clone, Default, State)] -// struct StateTester { -// #[node_dep_state((child, parent))] -// node: Node, -// #[child_dep_state(child)] -// child: Child, -// #[parent_dep_state(parent)] -// parent: Parent, -// } + #[derive(Debug, Clone, Default, State)] + struct StateTester { + #[node_dep_state((child, parent))] + node: Node, + #[child_dep_state(child)] + child: Child, + #[parent_dep_state(parent)] + parent: Parent, + } -// test_state!(StateTester, child: (child), node: (node), parent: (parent)); -// } + // test_state!(StateTester, child: (child), node: (node), parent: (parent)); +} // mod child_depends_on_node_that_depends_on_parent { // use super::*; // #[derive(Debug, Clone, Default, PartialEq)] // struct Node(i32); -// dep!(node(Node, (&'a Parent,))); +// dep!(node(Node, (Parent,))); // #[derive(Debug, Clone, Default, PartialEq)] // struct Child(i32); -// dep!(child(Child, Node)); +// dep!(child(Child, (Node,))); // #[derive(Debug, Clone, Default, PartialEq)] // struct Parent(i32); -// dep!(parent(Parent, Parent)); +// dep!(parent(Parent, (Parent,))); // #[derive(Debug, Clone, Default, State)] // struct StateTester { @@ -174,15 +187,15 @@ // use super::*; // #[derive(Debug, Clone, Default, PartialEq)] // struct Node(i32); -// dep!(node(Node, (&'a Child,))); +// dep!(node(Node, (Child,))); // #[derive(Debug, Clone, Default, PartialEq)] // struct Child(i32); -// dep!(child(Child, Child)); +// dep!(child(Child, (Child,))); // #[derive(Debug, Clone, Default, PartialEq)] // struct Parent(i32); -// dep!(parent(Parent, Node)); +// dep!(parent(Parent, (Node,))); // #[derive(Debug, Clone, Default, State)] // struct StateTester { @@ -201,7 +214,7 @@ // use super::*; // #[derive(Debug, Clone, Default, PartialEq)] // struct Node1(i32); -// dep!(node(Node1, (&'a Node2,))); +// dep!(node(Node1, (Node2,))); // #[derive(Debug, Clone, Default, PartialEq)] // struct Node2(i32); @@ -226,11 +239,11 @@ // #[derive(Debug, Clone, Default, PartialEq)] // struct Child(i32); -// dep!(child(Child, Child)); +// dep!(child(Child, (Child,))); // #[derive(Debug, Clone, Default, PartialEq)] // struct Parent(i32); -// dep!(parent(Parent, Parent)); +// dep!(parent(Parent, (Parent,))); // #[derive(Debug, Clone, Default, State)] // struct StateTester { diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 13033573e..5db3a3609 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -1,7 +1,9 @@ use std::hash::BuildHasherDefault; pub use node_ref::NodeMask; -pub use passes::AnyPass; +pub use passes::{ + AnyPass, DownwardPass, MemberMask, NodePass, Pass, PassId, PassReturn, UpwardPass, +}; use rustc_hash::FxHasher; pub use tree::NodeId; @@ -18,7 +20,7 @@ pub mod utils; pub type RealNodeId = NodeId; pub(crate) type FxDashMap = dashmap::DashMap>; pub(crate) type FxDashSet = dashmap::DashSet>; -pub(crate) type SendAnyMap = anymap::Map; +pub type SendAnyMap = anymap::Map; /// Used in derived state macros #[derive(Eq, PartialEq)] diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 26276c6f3..fadc8e71e 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -111,10 +111,10 @@ impl DirtyNodeStates { } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)] -pub struct PassId(u64); +pub struct PassId(pub u64); #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)] -pub struct MemberMask(u64); +pub struct MemberMask(pub u64); impl MemberMask { pub fn overlaps(&self, other: Self) -> bool { From d2047ed744ab703d958e381a5a70fb26a9184b05 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sat, 3 Dec 2022 22:40:09 -0600 Subject: [PATCH 22/43] mark cloned nodes as dirty --- packages/native-core-macro/src/lib.rs | 3 - .../tests/called_minimally_on_build.rs | 47 +++---- .../native-core-macro/tests/change_nodes.rs | 124 +++++++++--------- packages/native-core/src/real_dom.rs | 11 +- 4 files changed, 94 insertions(+), 91 deletions(-) diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index a1dc397c3..6b9c1735b 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -164,9 +164,6 @@ fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { .iter() .map(|m| m.impl_pass(state_strct.ty)); - // let child_types = state_strct.child_states.iter().map(|s| &s.ty); - // let child_members = state_strct.child_states.iter().map(|s| &s.ident); - let gen = quote! { #(#impl_members)* impl State for #type_name { diff --git a/packages/native-core-macro/tests/called_minimally_on_build.rs b/packages/native-core-macro/tests/called_minimally_on_build.rs index 24cd446d4..978ad8f5f 100644 --- a/packages/native-core-macro/tests/called_minimally_on_build.rs +++ b/packages/native-core-macro/tests/called_minimally_on_build.rs @@ -6,6 +6,7 @@ use dioxus_native_core::state::{ ChildDepState, ElementBorrowable, NodeDepState, ParentDepState, State, }; use dioxus_native_core::tree::*; +use dioxus_native_core::SendAnyMap; use dioxus_native_core_macro::State; macro_rules! dep { @@ -112,7 +113,7 @@ macro_rules! test_state{ let mut dom: RealDom<$s> = RealDom::new(); let nodes_updated = dom.apply_mutations(vec![mutations]); - let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); + let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); dom.traverse_depth_first(|n| { $( @@ -129,32 +130,32 @@ macro_rules! test_state{ } } -mod node_depends_on_child_and_parent { - use super::*; - #[derive(Debug, Clone, Default, PartialEq)] - struct Node(i32); - dep!(node(Node, (Child, Parent))); +// mod node_depends_on_child_and_parent { +// use super::*; +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Node(i32); +// dep!(node(Node, (Child, Parent))); - #[derive(Debug, Clone, Default, PartialEq)] - struct Child(i32); - dep!(child(Child, (Child,))); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Child(i32); +// dep!(child(Child, (Child,))); - #[derive(Debug, Clone, Default, PartialEq)] - struct Parent(i32); - dep!(parent(Parent, (Parent,))); +// #[derive(Debug, Clone, Default, PartialEq)] +// struct Parent(i32); +// dep!(parent(Parent, (Parent,))); - #[derive(Debug, Clone, Default, State)] - struct StateTester { - #[node_dep_state((child, parent))] - node: Node, - #[child_dep_state(child)] - child: Child, - #[parent_dep_state(parent)] - parent: Parent, - } +// #[derive(Debug, Clone, Default, State)] +// struct StateTester { +// #[node_dep_state((child, parent))] +// node: Node, +// #[child_dep_state(child)] +// child: Child, +// #[parent_dep_state(parent)] +// parent: Parent, +// } - // test_state!(StateTester, child: (child), node: (node), parent: (parent)); -} +// test_state!(StateTester, child: (child), node: (node), parent: (parent)); +// } // mod child_depends_on_node_that_depends_on_parent { // use super::*; diff --git a/packages/native-core-macro/tests/change_nodes.rs b/packages/native-core-macro/tests/change_nodes.rs index ccc103549..af3cbefb8 100644 --- a/packages/native-core-macro/tests/change_nodes.rs +++ b/packages/native-core-macro/tests/change_nodes.rs @@ -1,79 +1,79 @@ -use dioxus::core::ElementId; -use dioxus::prelude::*; -use dioxus_native_core::real_dom::RealDom; -use dioxus_native_core::state::State; -use dioxus_native_core::tree::*; -use dioxus_native_core::NodeId; -use dioxus_native_core_macro::State; +// use dioxus::core::ElementId; +// use dioxus::prelude::*; +// use dioxus_native_core::real_dom::RealDom; +// use dioxus_native_core::state::State; +// use dioxus_native_core::tree::*; +// use dioxus_native_core::NodeId; +// use dioxus_native_core_macro::State; -#[derive(State, Default, Clone)] -struct Empty {} +// #[derive(State, Default, Clone)] +// struct Empty {} -#[test] -fn remove_node() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } +// #[test] +// fn remove_node() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let mut dom: RealDom = RealDom::new(); - let (create, edit) = vdom.diff_lazynodes( - rsx! { - div{ - div{} - } - }, - rsx! { - div{} - }, - ); +// let mut dom: RealDom = RealDom::new(); +// let (create, edit) = vdom.diff_lazynodes( +// rsx! { +// div{ +// div{} +// } +// }, +// rsx! { +// div{} +// }, +// ); - println!("create: {:#?}", create); - println!("edit: {:#?}", edit); +// println!("create: {:#?}", create); +// println!("edit: {:#?}", edit); - let _to_update = dom.apply_mutations(vec![create]); +// let _to_update = dom.apply_mutations(vec![create]); - assert_eq!(dom.tree.height(NodeId(0)), Some(0)); - assert_eq!(dom.tree.height(NodeId(1)), Some(1)); +// assert_eq!(dom.tree.height(NodeId(0)), Some(0)); +// assert_eq!(dom.tree.height(NodeId(1)), Some(1)); - dom.apply_mutations(vec![edit]); +// dom.apply_mutations(vec![edit]); - assert_eq!(dom.size(), 3); - assert_eq!(dom.tree.height(NodeId(0)), Some(0)); -} +// assert_eq!(dom.size(), 3); +// assert_eq!(dom.tree.height(NodeId(0)), Some(0)); +// } -#[test] -fn add_node() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div {}) - } +// #[test] +// fn add_node() { +// #[allow(non_snake_case)] +// fn Base(cx: Scope) -> Element { +// render!(div {}) +// } - let vdom = VirtualDom::new(Base); +// let vdom = VirtualDom::new(Base); - let (create, update) = vdom.diff_lazynodes( - rsx! { - div{} - }, - rsx! { - div{ - p{} - } - }, - ); +// let (create, update) = vdom.diff_lazynodes( +// rsx! { +// div{} +// }, +// rsx! { +// div{ +// p{} +// } +// }, +// ); - let mut dom: RealDom = RealDom::new(); +// let mut dom: RealDom = RealDom::new(); - let _to_update = dom.apply_mutations(vec![create]); +// let _to_update = dom.apply_mutations(vec![create]); - assert_eq!(dom.size(), 2); - assert_eq!(dom.tree.height(NodeId(2)), Some(1)); +// assert_eq!(dom.size(), 2); +// assert_eq!(dom.tree.height(NodeId(2)), Some(1)); - dom.apply_mutations(vec![update]); +// dom.apply_mutations(vec![update]); - assert_eq!(dom.size(), 3); - assert_eq!(dom.tree.height(NodeId(3)), Some(0)); - assert_eq!(dom.tree.height(NodeId(0)), Some(1)); -} +// assert_eq!(dom.size(), 3); +// assert_eq!(dom.tree.height(NodeId(3)), Some(0)); +// assert_eq!(dom.tree.height(NodeId(0)), Some(1)); +// } diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index dadb3909a..fdc036993 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -206,7 +206,7 @@ impl RealDom { } LoadTemplate { name, index, id } => { let template_id = self.templates[name][index]; - let clone_id = self.clone_node(template_id); + let clone_id = self.clone_node(template_id, &mut nodes_updated); self.set_element_id(clone_id, id); } ReplaceWith { id, m } => { @@ -384,16 +384,21 @@ impl RealDom { self.tree.root() } - fn clone_node(&mut self, node_id: NodeId) -> RealNodeId { + fn clone_node( + &mut self, + node_id: NodeId, + nodes_updated: &mut FxHashMap, + ) -> RealNodeId { let node = self.tree.get(node_id).unwrap(); let new_node = node.clone(); let new_id = self.create_node(new_node); + mark_dirty(new_id, NodeMask::ALL, nodes_updated); let self_ptr = self as *mut Self; for child in self.tree.children_ids(node_id).unwrap() { unsafe { // this is safe because no node has itself as a child let self_mut = &mut *self_ptr; - let child_id = self_mut.clone_node(*child); + let child_id = self_mut.clone_node(*child, nodes_updated); self_mut.add_child(new_id, child_id); } } From 72184e65ef9d07564b5f8b420cdeeb8af85407ff Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 4 Dec 2022 19:43:05 -0600 Subject: [PATCH 23/43] update persistant_iterator utility --- ...ant_iterator.rs => persistent_iterator.rs} | 0 packages/native-core/src/real_dom.rs | 2 +- packages/native-core/src/utils.rs | 424 +++++++++--------- 3 files changed, 213 insertions(+), 213 deletions(-) rename packages/native-core-macro/tests/{peristant_iterator.rs => persistent_iterator.rs} (100%) diff --git a/packages/native-core-macro/tests/peristant_iterator.rs b/packages/native-core-macro/tests/persistent_iterator.rs similarity index 100% rename from packages/native-core-macro/tests/peristant_iterator.rs rename to packages/native-core-macro/tests/persistent_iterator.rs diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index fdc036993..5ef100082 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -62,7 +62,7 @@ impl RealDom { } } - fn element_to_node_id(&self, element_id: ElementId) -> RealNodeId { + pub fn element_to_node_id(&self, element_id: ElementId) -> RealNodeId { self.node_id_mapping.get(element_id.0).unwrap().unwrap() } diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index 0a5e7ebf1..70d016d92 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -1,225 +1,225 @@ -// use crate::{real_dom::RealDom, state::State, RealNodeId}; -// use dioxus_core::{ElementId, Mutations}; +use crate::{node::NodeType, real_dom::RealDom, state::State, tree::TreeView, NodeId, RealNodeId}; +use dioxus_core::{Mutation, Mutations}; -// pub enum ElementProduced { -// /// The iterator produced an element by progressing to the next node in a depth first order. -// Progressed(RealNodeId), -// /// The iterator reached the end of the tree and looped back to the root -// Looped(RealNodeId), -// } -// impl ElementProduced { -// pub fn id(&self) -> RealNodeId { -// match self { -// ElementProduced::Progressed(id) => *id, -// ElementProduced::Looped(id) => *id, -// } -// } -// } +pub enum ElementProduced { + /// The iterator produced an element by progressing to the next node in a depth first order. + Progressed(RealNodeId), + /// The iterator reached the end of the tree and looped back to the root + Looped(RealNodeId), +} +impl ElementProduced { + pub fn id(&self) -> RealNodeId { + match self { + ElementProduced::Progressed(id) => *id, + ElementProduced::Looped(id) => *id, + } + } +} -// #[derive(Debug)] -// enum NodePosition { -// AtNode, -// InChild(usize), -// } +#[derive(Debug)] +enum NodePosition { + AtNode, + InChild(usize), +} -// impl NodePosition { -// fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self { -// match self { -// Self::AtNode => Self::AtNode, -// Self::InChild(i) => Self::InChild(f(*i)), -// } -// } +impl NodePosition { + fn map(&self, mut f: impl FnMut(usize) -> usize) -> Self { + match self { + Self::AtNode => Self::AtNode, + Self::InChild(i) => Self::InChild(f(*i)), + } + } -// fn get_or_insert(&mut self, child_idx: usize) -> usize { -// match self { -// Self::AtNode => { -// *self = Self::InChild(child_idx); -// child_idx -// } -// Self::InChild(i) => *i, -// } -// } -// } + fn get_or_insert(&mut self, child_idx: usize) -> usize { + match self { + Self::AtNode => { + *self = Self::InChild(child_idx); + child_idx + } + Self::InChild(i) => *i, + } + } +} -// /// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom]. -// /// This iterator traverses the tree depth first. -// /// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]). -// /// The iterator loops around when it reaches the end or the beginning. -// pub struct PersistantElementIter { -// // stack of elements and fragments -// stack: smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, -// } +/// Focus systems need a iterator that can persist through changes in the [dioxus_core::VirtualDom]. +/// This iterator traverses the tree depth first. +/// Iterate through it with [PersistantElementIter::next] [PersistantElementIter::prev], and update it with [PersistantElementIter::prune] (with data from [`dioxus_core::prelude::VirtualDom::work_with_deadline`]). +/// The iterator loops around when it reaches the end or the beginning. +pub struct PersistantElementIter { + // stack of elements and fragments + stack: smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, +} -// impl Default for PersistantElementIter { -// fn default() -> Self { -// PersistantElementIter { -// stack: smallvec::smallvec![(RealNodeId::ElementId(ElementId(0)), NodePosition::AtNode)], -// } -// } -// } +impl Default for PersistantElementIter { + fn default() -> Self { + PersistantElementIter { + stack: smallvec::smallvec![(NodeId(0), NodePosition::AtNode)], + } + } +} -// impl PersistantElementIter { -// pub fn new() -> Self { -// Self::default() -// } +impl PersistantElementIter { + pub fn new() -> Self { + Self::default() + } -// /// remove stale element refreneces -// /// returns true if the focused element is removed -// pub fn prune(&mut self, mutations: &Mutations, rdom: &RealDom) -> bool { -// let mut changed = false; -// let ids_removed: Vec<_> = mutations -// .edits -// .iter() -// .filter_map(|e| { -// // nodes within templates will never be removed -// if let DomEdit::Remove { root } = e { -// let id = rdom.resolve_maybe_id(*root); -// Some(id) -// } else { -// None -// } -// }) -// .collect(); -// // if any element is removed in the chain, remove it and its children from the stack -// if let Some(r) = self -// .stack -// .iter() -// .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id)) -// { -// self.stack.truncate(r); -// changed = true; -// } -// // if a child is removed or inserted before or at the current element, update the child index -// for (el_id, child_idx) in self.stack.iter_mut() { -// if let NodePosition::InChild(child_idx) = child_idx { -// if let NodeType::Element { children, .. } = &rdom[*el_id].node_data.node_type { -// for m in &mutations.edits { -// match m { -// DomEdit::Remove { root } => { -// let id = rdom.resolve_maybe_id(*root); -// if children.iter().take(*child_idx + 1).any(|c| *c == id) { -// *child_idx -= 1; -// } -// } -// DomEdit::InsertBefore { root, nodes } => { -// let id = rdom.resolve_maybe_id(*root); -// let n = nodes.len(); -// if children.iter().take(*child_idx + 1).any(|c| *c == id) { -// *child_idx += n as usize; -// } -// } -// DomEdit::InsertAfter { root, nodes } => { -// let id = rdom.resolve_maybe_id(*root); -// let n = nodes.len(); -// if children.iter().take(*child_idx).any(|c| *c == id) { -// *child_idx += n as usize; -// } -// } -// _ => (), -// } -// } -// } -// } -// } -// changed -// } + /// remove stale element refreneces + /// returns true if the focused element is removed + pub fn prune(&mut self, mutations: &Mutations, rdom: &RealDom) -> bool { + let mut changed = false; + let ids_removed: Vec<_> = mutations + .edits + .iter() + .filter_map(|m| { + // nodes within templates will never be removedns + if let Mutation::Remove { id } = m { + Some(rdom.element_to_node_id(*id)) + } else { + None + } + }) + .collect(); + // if any element is removed in the chain, remove it and its children from the stack + if let Some(r) = self + .stack + .iter() + .position(|(el_id, _)| ids_removed.iter().any(|id| el_id == id)) + { + self.stack.truncate(r); + changed = true; + } + // if a child is removed or inserted before or at the current element, update the child index + for (el_id, child_idx) in self.stack.iter_mut() { + if let NodePosition::InChild(child_idx) = child_idx { + if let Some(children) = &rdom.tree.children_ids(*el_id) { + for m in &mutations.edits { + match m { + Mutation::Remove { id } => { + let id = rdom.element_to_node_id(*id); + if children.iter().take(*child_idx + 1).any(|c| *c == id) { + *child_idx -= 1; + } + } + Mutation::InsertBefore { id, m } => { + let id = rdom.element_to_node_id(*id); + if children.iter().take(*child_idx + 1).any(|c| *c == id) { + *child_idx += *m; + } + } + Mutation::InsertAfter { id, m } => { + let id = rdom.element_to_node_id(*id); + if children.iter().take(*child_idx).any(|c| *c == id) { + *child_idx += *m; + } + } + _ => (), + } + } + } + } + } + changed + } -// /// get the next element -// pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { -// if self.stack.is_empty() { -// let id = RealNodeId::ElementId(ElementId(0)); -// let new = (id, NodePosition::AtNode); -// self.stack.push(new); -// ElementProduced::Looped(id) -// } else { -// let (last, o_child_idx) = self.stack.last_mut().unwrap(); -// let node = &rdom[*last]; -// match &node.node_data.node_type { -// NodeType::Element { children, .. } => { -// *o_child_idx = o_child_idx.map(|i| i + 1); -// // if we have children, go to the next child -// let child_idx = o_child_idx.get_or_insert(0); -// if child_idx >= children.len() { -// self.pop(); -// self.next(rdom) -// } else { -// let id = children[child_idx]; -// if let NodeType::Element { .. } = &rdom[id].node_data.node_type { -// self.stack.push((id, NodePosition::AtNode)); -// } -// ElementProduced::Progressed(id) -// } -// } + /// get the next element + pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { + if self.stack.is_empty() { + let id = NodeId(0); + let new = (id, NodePosition::AtNode); + self.stack.push(new); + ElementProduced::Looped(id) + } else { + let (last, o_child_idx) = self.stack.last_mut().unwrap(); + let node = &rdom[*last]; + match &node.node_data.node_type { + NodeType::Element { .. } => { + let children = rdom.tree.children_ids(*last).unwrap(); + *o_child_idx = o_child_idx.map(|i| i + 1); + // if we have children, go to the next child + let child_idx = o_child_idx.get_or_insert(0); + if child_idx >= children.len() { + self.pop(); + self.next(rdom) + } else { + let id = children[child_idx]; + if let NodeType::Element { .. } = &rdom[id].node_data.node_type { + self.stack.push((id, NodePosition::AtNode)); + } + ElementProduced::Progressed(id) + } + } -// NodeType::Text { .. } | NodeType::Placeholder { .. } => { -// // we are at a leaf, so we are done -// ElementProduced::Progressed(self.pop()) -// } -// } -// } -// } + NodeType::Text { .. } | NodeType::Placeholder { .. } => { + // we are at a leaf, so we are done + ElementProduced::Progressed(self.pop()) + } + } + } + } -// /// get the previous element -// pub fn prev(&mut self, rdom: &RealDom) -> ElementProduced { -// // recursively add the last child element to the stack -// fn push_back( -// stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, -// new_node: RealNodeId, -// rdom: &RealDom, -// ) -> RealNodeId { -// match &rdom[new_node].node_data.node_type { -// NodeType::Element { children, .. } => { -// if children.is_empty() { -// new_node -// } else { -// stack.push((new_node, NodePosition::InChild(children.len() - 1))); -// push_back(stack, *children.last().unwrap(), rdom) -// } -// } -// _ => new_node, -// } -// } -// if self.stack.is_empty() { -// let new_node = RealNodeId::ElementId(ElementId(0)); -// ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom)) -// } else { -// let (last, o_child_idx) = self.stack.last_mut().unwrap(); -// let node = &rdom[*last]; -// match &node.node_data.node_type { -// NodeType::Element { children, .. } => { -// // if we have children, go to the next child -// if let NodePosition::InChild(0) = o_child_idx { -// ElementProduced::Progressed(self.pop()) -// } else { -// *o_child_idx = o_child_idx.map(|i| i - 1); -// if let NodePosition::InChild(child_idx) = o_child_idx { -// if *child_idx >= children.len() || children.is_empty() { -// self.pop(); -// self.prev(rdom) -// } else { -// let new_node = children[*child_idx]; -// ElementProduced::Progressed(push_back( -// &mut self.stack, -// new_node, -// rdom, -// )) -// } -// } else { -// self.pop(); -// self.prev(rdom) -// } -// } -// } + /// get the previous element + pub fn prev(&mut self, rdom: &RealDom) -> ElementProduced { + // recursively add the last child element to the stack + fn push_back( + stack: &mut smallvec::SmallVec<[(RealNodeId, NodePosition); 5]>, + new_node: RealNodeId, + rdom: &RealDom, + ) -> RealNodeId { + match &rdom[new_node].node_data.node_type { + NodeType::Element { .. } => { + let children = rdom.tree.children_ids(new_node).unwrap(); + if children.is_empty() { + new_node + } else { + stack.push((new_node, NodePosition::InChild(children.len() - 1))); + push_back(stack, *children.last().unwrap(), rdom) + } + } + _ => new_node, + } + } + if self.stack.is_empty() { + let new_node = NodeId(0); + ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom)) + } else { + let (last, o_child_idx) = self.stack.last_mut().unwrap(); + let node = &rdom[*last]; + match &node.node_data.node_type { + NodeType::Element { .. } => { + let children = rdom.tree.children_ids(*last).unwrap(); + // if we have children, go to the next child + if let NodePosition::InChild(0) = o_child_idx { + ElementProduced::Progressed(self.pop()) + } else { + *o_child_idx = o_child_idx.map(|i| i - 1); + if let NodePosition::InChild(child_idx) = o_child_idx { + if *child_idx >= children.len() || children.is_empty() { + self.pop(); + self.prev(rdom) + } else { + let new_node = children[*child_idx]; + ElementProduced::Progressed(push_back( + &mut self.stack, + new_node, + rdom, + )) + } + } else { + self.pop(); + self.prev(rdom) + } + } + } -// NodeType::Text { .. } | NodeType::Placeholder { .. } => { -// // we are at a leaf, so we are done -// ElementProduced::Progressed(self.pop()) -// } -// } -// } -// } + NodeType::Text { .. } | NodeType::Placeholder { .. } => { + // we are at a leaf, so we are done + ElementProduced::Progressed(self.pop()) + } + } + } + } -// fn pop(&mut self) -> RealNodeId { -// self.stack.pop().unwrap().0 -// } -// } + fn pop(&mut self) -> RealNodeId { + self.stack.pop().unwrap().0 + } +} From f21d099ae4f49f8a99cebbd707f60fdd7c4d9f9c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 4 Dec 2022 19:48:16 -0600 Subject: [PATCH 24/43] fix mask generation --- packages/native-core-macro/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 6b9c1735b..6174e756a 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -493,8 +493,8 @@ impl<'a> StateMember<'a> { let mask = self .dep_mems .iter() - .map(|m| m.id) - .fold(self.mem.id, |a, b| a | b); + .map(|m| 1 << m.id) + .fold(1 << self.mem.id, |a, b| a | b); quote! { #[derive(Clone, Copy)] struct #unit_type; @@ -510,7 +510,7 @@ impl<'a> StateMember<'a> { &[#(dioxus_native_core::PassId(#dependants)),*] } fn mask(&self) -> dioxus_native_core::MemberMask { - dioxus_native_core::MemberMask(1 << #mask) + dioxus_native_core::MemberMask(#mask) } } } From 8c07b4059f10ac53b1ac426d1bf6879e67a2268c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 4 Dec 2022 20:39:54 -0600 Subject: [PATCH 25/43] update tui with new mutations --- packages/native-core-macro/src/lib.rs | 2 +- packages/native-core/Cargo.toml | 2 +- packages/native-core/src/lib.rs | 4 +- packages/native-core/src/real_dom.rs | 322 +++++++++++++------------- packages/tui/Cargo.toml | 3 +- packages/tui/src/focus.rs | 29 +-- packages/tui/src/hooks.rs | 169 +++++++------- packages/tui/src/layout.rs | 22 +- packages/tui/src/lib.rs | 61 +++-- packages/tui/src/node.rs | 9 +- packages/tui/src/query.rs | 19 +- packages/tui/src/render.rs | 15 +- packages/tui/src/style_attributes.rs | 8 +- 13 files changed, 335 insertions(+), 330 deletions(-) diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 6174e756a..6c0cdb2d0 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -493,7 +493,7 @@ impl<'a> StateMember<'a> { let mask = self .dep_mems .iter() - .map(|m| 1 << m.id) + .map(|m| 1u64 << m.id) .fold(1 << self.mem.id, |a, b| a | b); quote! { #[derive(Clone, Copy)] diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index 0dc7e0d6e..f15a743f3 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -15,7 +15,7 @@ dioxus-core = { path = "../core", version = "^0.2.1" } dioxus-html = { path = "../html", version = "^0.2.1" } dioxus-core-macro = { path = "../core-macro", version = "^0.2.1" } -taffy = "0.2.0" +taffy = "0.2.1" smallvec = "1.6" rustc-hash = "1.1.0" anymap = "0.12.1" diff --git a/packages/native-core/src/lib.rs b/packages/native-core/src/lib.rs index 5db3a3609..ae739af86 100644 --- a/packages/native-core/src/lib.rs +++ b/packages/native-core/src/lib.rs @@ -18,8 +18,8 @@ pub mod utils; /// A id for a node that lives in the real dom. pub type RealNodeId = NodeId; -pub(crate) type FxDashMap = dashmap::DashMap>; -pub(crate) type FxDashSet = dashmap::DashSet>; +pub type FxDashMap = dashmap::DashMap>; +pub type FxDashSet = dashmap::DashSet>; pub type SendAnyMap = anymap::Map; /// Used in derived state macros diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 5ef100082..f6b58a933 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -151,184 +151,180 @@ impl RealDom { } /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. - pub fn apply_mutations(&mut self, mutations_vec: Vec) -> DirtyNodeStates { + pub fn apply_mutations(&mut self, mutations: Mutations) -> DirtyNodeStates { let mut nodes_updated: FxHashMap = FxHashMap::default(); - for mutations in mutations_vec { - for template in mutations.templates { - let mut template_root_ids = Vec::new(); - for root in template.roots { - if let Some(id) = self.create_template_node(root, &mut nodes_updated) { - template_root_ids.push(id); + for template in mutations.templates { + let mut template_root_ids = Vec::new(); + for root in template.roots { + if let Some(id) = self.create_template_node(root, &mut nodes_updated) { + template_root_ids.push(id); + } + } + self.templates + .insert(template.name.to_string(), template_root_ids); + } + for e in mutations.edits { + use dioxus_core::Mutation::*; + match e { + AppendChildren { id, m } => { + let children = self.stack.split_off(m); + let parent = self.element_to_node_id(id); + for child in children { + self.add_child(parent, child); + mark_dirty(parent, NodeMask::ALL, &mut nodes_updated); } } - self.templates - .insert(template.name.to_string(), template_root_ids); - } - for e in mutations.edits { - use dioxus_core::Mutation::*; - match e { - AppendChildren { id, m } => { - let children = self.stack.split_off(m); - let parent = self.element_to_node_id(id); - for child in children { - self.add_child(parent, child); - mark_dirty(parent, NodeMask::ALL, &mut nodes_updated); - } + AssignId { path, id } => { + self.set_element_id(self.load_child(path), id); + } + CreatePlaceholder { id } => { + let node = Node::new(NodeType::Placeholder); + let node_id = self.create_node(node); + self.set_element_id(node_id, id); + self.stack.push(node_id); + mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated); + } + CreateTextNode { value, id } => { + let node = Node::new(NodeType::Text { + text: value.to_string(), + }); + let node_id = self.create_node(node); + let node = self.tree.get_mut(node_id).unwrap(); + node.node_data.element_id = Some(id); + self.stack.push(node_id); + mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); + } + HydrateText { path, value, id } => { + let node_id = self.load_child(path); + self.set_element_id(node_id, id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Text { text } = &mut node.node_data.node_type { + *text = value.to_string(); } - AssignId { path, id } => { - self.set_element_id(self.load_child(path), id); + mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); + } + LoadTemplate { name, index, id } => { + let template_id = self.templates[name][index]; + let clone_id = self.clone_node(template_id, &mut nodes_updated); + self.set_element_id(clone_id, id); + } + ReplaceWith { id, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.element_to_node_id(id); + for new in new_nodes { + self.tree.insert_after(old_node_id, new); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } - CreatePlaceholder { id } => { - let node = Node::new(NodeType::Placeholder); - let node_id = self.create_node(node); - self.set_element_id(node_id, id); - self.stack.push(node_id); - mark_dirty(node_id, NodeMask::ALL, &mut nodes_updated); + self.tree.remove(old_node_id); + } + ReplacePlaceholder { path, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.load_child(path); + for new in new_nodes { + self.tree.insert_after(old_node_id, new); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } - CreateTextNode { value, id } => { - let node = Node::new(NodeType::Text { - text: value.to_string(), - }); - let node_id = self.create_node(node); - let node = self.tree.get_mut(node_id).unwrap(); - node.node_data.element_id = Some(id); - self.stack.push(node_id); - mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); + self.tree.remove(old_node_id); + } + InsertAfter { id, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.element_to_node_id(id); + for new in new_nodes { + self.tree.insert_after(old_node_id, new); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } - HydrateText { path, value, id } => { - let node_id = self.load_child(path); - self.set_element_id(node_id, id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Text { text } = &mut node.node_data.node_type { - *text = value.to_string(); - } - mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); + } + InsertBefore { id, m } => { + let new_nodes = self.stack.split_off(m); + let old_node_id = self.element_to_node_id(id); + for new in new_nodes { + self.tree.insert_before(old_node_id, new); + mark_dirty(new, NodeMask::ALL, &mut nodes_updated); } - LoadTemplate { name, index, id } => { - let template_id = self.templates[name][index]; - let clone_id = self.clone_node(template_id, &mut nodes_updated); - self.set_element_id(clone_id, id); + } + SetAttribute { + name, + value, + id, + ns, + } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type { + attributes.insert( + OwnedAttributeDiscription { + name: name.to_string(), + namespace: ns.map(|s| s.to_string()), + volatile: false, + }, + crate::node::OwnedAttributeValue::Text(value.to_string()), + ); + mark_dirty( + node_id, + NodeMask::new_with_attrs(AttributeMask::single(name)), + &mut nodes_updated, + ); } - ReplaceWith { id, m } => { - let new_nodes = self.stack.split_off(m); - let old_node_id = self.element_to_node_id(id); - for new in new_nodes { - self.tree.insert_after(old_node_id, new); - mark_dirty(new, NodeMask::ALL, &mut nodes_updated); - } - self.tree.remove(old_node_id); + } + SetBoolAttribute { name, value, id } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type { + attributes.insert( + OwnedAttributeDiscription { + name: name.to_string(), + namespace: None, + volatile: false, + }, + crate::node::OwnedAttributeValue::Bool(value), + ); + mark_dirty( + node_id, + NodeMask::new_with_attrs(AttributeMask::single(name)), + &mut nodes_updated, + ); } - ReplacePlaceholder { path, m } => { - let new_nodes = self.stack.split_off(m); - let old_node_id = self.load_child(path); - for new in new_nodes { - self.tree.insert_after(old_node_id, new); - mark_dirty(new, NodeMask::ALL, &mut nodes_updated); - } - self.tree.remove(old_node_id); + } + SetText { value, id } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Text { text } = &mut node.node_data.node_type { + *text = value.to_string(); } - InsertAfter { id, m } => { - let new_nodes = self.stack.split_off(m); - let old_node_id = self.element_to_node_id(id); - for new in new_nodes { - self.tree.insert_after(old_node_id, new); - mark_dirty(new, NodeMask::ALL, &mut nodes_updated); - } - } - InsertBefore { id, m } => { - let new_nodes = self.stack.split_off(m); - let old_node_id = self.element_to_node_id(id); - for new in new_nodes { - self.tree.insert_before(old_node_id, new); - mark_dirty(new, NodeMask::ALL, &mut nodes_updated); - } - } - SetAttribute { - name, - value, - id, - ns, - } => { - let node_id = self.element_to_node_id(id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type - { - attributes.insert( - OwnedAttributeDiscription { - name: name.to_string(), - namespace: ns.map(|s| s.to_string()), - volatile: false, - }, - crate::node::OwnedAttributeValue::Text(value.to_string()), - ); - mark_dirty( - node_id, - NodeMask::new_with_attrs(AttributeMask::single(name)), - &mut nodes_updated, - ); - } - } - SetBoolAttribute { name, value, id } => { - let node_id = self.element_to_node_id(id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Element { attributes, .. } = &mut node.node_data.node_type - { - attributes.insert( - OwnedAttributeDiscription { - name: name.to_string(), - namespace: None, - volatile: false, - }, - crate::node::OwnedAttributeValue::Bool(value), - ); - mark_dirty( - node_id, - NodeMask::new_with_attrs(AttributeMask::single(name)), - &mut nodes_updated, - ); - } - } - SetText { value, id } => { - let node_id = self.element_to_node_id(id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Text { text } = &mut node.node_data.node_type { - *text = value.to_string(); - } - mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); - } - NewEventListener { name, scope: _, id } => { - let node_id = self.element_to_node_id(id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { - match self.nodes_listening.get_mut(name) { - Some(hs) => { - hs.insert(node_id); - } - None => { - let mut hs = FxHashSet::default(); - hs.insert(node_id); - self.nodes_listening.insert(name.to_string(), hs); - } + mark_dirty(node_id, NodeMask::new().with_text(), &mut nodes_updated); + } + NewEventListener { name, scope: _, id } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { + match self.nodes_listening.get_mut(name) { + Some(hs) => { + hs.insert(node_id); + } + None => { + let mut hs = FxHashSet::default(); + hs.insert(node_id); + self.nodes_listening.insert(name.to_string(), hs); } - listeners.insert(name.to_string()); } + listeners.insert(name.to_string()); } - RemoveEventListener { id, name } => { - let node_id = self.element_to_node_id(id); - let node = self.tree.get_mut(node_id).unwrap(); - if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { - listeners.remove(name); - } - self.nodes_listening.get_mut(name).unwrap().remove(&node_id); - } - Remove { id } => { - let node_id = self.element_to_node_id(id); - self.tree.remove(node_id); - } - PushRoot { id } => { - let node_id = self.element_to_node_id(id); - self.stack.push(node_id); + } + RemoveEventListener { id, name } => { + let node_id = self.element_to_node_id(id); + let node = self.tree.get_mut(node_id).unwrap(); + if let NodeType::Element { listeners, .. } = &mut node.node_data.node_type { + listeners.remove(name); } + self.nodes_listening.get_mut(name).unwrap().remove(&node_id); + } + Remove { id } => { + let node_id = self.element_to_node_id(id); + self.tree.remove(node_id); + } + PushRoot { id } => { + let node_id = self.element_to_node_id(id); + self.stack.push(node_id); } } } diff --git a/packages/tui/Cargo.toml b/packages/tui/Cargo.toml index 10f005bbc..116784c7b 100644 --- a/packages/tui/Cargo.toml +++ b/packages/tui/Cargo.toml @@ -23,10 +23,11 @@ crossterm = "0.23.0" anyhow = "1.0.42" tokio = { version = "1.15.0", features = ["full"] } futures = "0.3.19" -taffy = "0.1.0" +taffy = "0.2.1" smallvec = "1.6" rustc-hash = "1.1.0" anymap = "0.12.1" +futures-channel = "0.3.25" [dev-dependencies] dioxus = { path = "../dioxus" } diff --git a/packages/tui/src/focus.rs b/packages/tui/src/focus.rs index 9c070bc00..90d9e9da0 100644 --- a/packages/tui/src/focus.rs +++ b/packages/tui/src/focus.rs @@ -1,6 +1,7 @@ -use crate::{node::PreventDefault, Dom}; +use crate::{node::PreventDefault, TuiDom}; use dioxus_native_core::{ + tree::TreeView, utils::{ElementProduced, PersistantElementIter}, RealNodeId, }; @@ -10,7 +11,6 @@ use std::{cmp::Ordering, num::NonZeroU16}; use dioxus_native_core::{ node_ref::{AttributeMask, NodeMask, NodeView}, - real_dom::NodeType, state::NodeDepState, }; @@ -64,7 +64,8 @@ pub(crate) struct Focus { pub level: FocusLevel, } -impl NodeDepState<()> for Focus { +impl NodeDepState for Focus { + type DepState = (); type Ctx = (); const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(FOCUS_ATTRIBUTES)).with_listeners(); @@ -77,7 +78,7 @@ impl NodeDepState<()> for Focus { { if let Some(index) = a .value - .as_int32() + .as_int() .or_else(|| a.value.as_text().and_then(|v| v.parse::().ok())) { match index.cmp(&0) { @@ -126,7 +127,7 @@ pub(crate) struct FocusState { impl FocusState { /// Returns true if the focus has changed. - pub fn progress(&mut self, rdom: &mut Dom, forward: bool) -> bool { + pub fn progress(&mut self, rdom: &mut TuiDom, forward: bool) -> bool { if let Some(last) = self.last_focused_id { if rdom[last].state.prevent_default == PreventDefault::KeyDown { return false; @@ -234,10 +235,10 @@ impl FocusState { false } - pub(crate) fn prune(&mut self, mutations: &dioxus_core::Mutations, rdom: &Dom) { + pub(crate) fn prune(&mut self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) { fn remove_children( to_prune: &mut [&mut Option], - rdom: &Dom, + rdom: &TuiDom, removed: RealNodeId, ) { for opt in to_prune.iter_mut() { @@ -247,8 +248,8 @@ impl FocusState { } } } - if let NodeType::Element { children, .. } = &rdom[removed].node_data.node_type { - for child in children { + if let Some(children) = &rdom.children_ids(removed) { + for child in *children { remove_children(to_prune, rdom, *child); } } @@ -258,22 +259,22 @@ impl FocusState { } for m in &mutations.edits { match m { - dioxus_core::DomEdit::ReplaceWith { root, .. } => remove_children( + dioxus_core::Mutation::ReplaceWith { id, .. } => remove_children( &mut [&mut self.last_focused_id], rdom, - rdom.resolve_maybe_id(*root), + rdom.element_to_node_id(*id), ), - dioxus_core::DomEdit::Remove { root } => remove_children( + dioxus_core::Mutation::Remove { id } => remove_children( &mut [&mut self.last_focused_id], rdom, - rdom.resolve_maybe_id(*root), + rdom.element_to_node_id(*id), ), _ => (), } } } - pub(crate) fn set_focus(&mut self, rdom: &mut Dom, id: RealNodeId) { + pub(crate) fn set_focus(&mut self, rdom: &mut TuiDom, id: RealNodeId) { if let Some(old) = self.last_focused_id.replace(id) { rdom[old].state.focused = false; } diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 44ac276cc..197c7ea79 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -2,7 +2,8 @@ use crossterm::event::{ Event as TermEvent, KeyCode as TermKeyCode, KeyModifiers, MouseButton, MouseEventKind, }; use dioxus_core::*; -use dioxus_native_core::RealNodeId; +use dioxus_native_core::tree::TreeView; +use dioxus_native_core::NodeId; use rustc_hash::{FxHashMap, FxHashSet}; use dioxus_html::geometry::euclid::{Point2D, Rect, Size2D}; @@ -12,19 +13,25 @@ use dioxus_html::geometry::{ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers}; use dioxus_html::input_data::MouseButtonSet as DioxusMouseButtons; use dioxus_html::input_data::{MouseButton as DioxusMouseButton, MouseButtonSet}; -use dioxus_html::{event_bubbles, on::*}; +use dioxus_html::{event_bubbles, FocusData, KeyboardData, MouseData, WheelData}; use std::{ any::Any, cell::{RefCell, RefMut}, rc::Rc, - sync::Arc, time::{Duration, Instant}, }; use taffy::geometry::{Point, Size}; use taffy::{prelude::Layout, Taffy}; use crate::FocusState; -use crate::{Dom, Node}; +use crate::{TuiDom, TuiNode}; + +pub(crate) struct Event { + pub id: ElementId, + pub name: &'static str, + pub data: Rc, + pub bubbles: bool, +} // a wrapper around the input state for easier access // todo: fix loop @@ -71,12 +78,12 @@ enum EventData { Keyboard(KeyboardData), } impl EventData { - fn into_any(self) -> Arc { + fn into_any(self) -> Rc { match self { - Self::Mouse(m) => Arc::new(m), - Self::Wheel(w) => Arc::new(w), - Self::Screen(s) => Arc::new(s), - Self::Keyboard(k) => Arc::new(k), + Self::Mouse(m) => Rc::new(m), + Self::Wheel(w) => Rc::new(w), + Self::Screen(s) => Rc::new(s), + Self::Keyboard(k) => Rc::new(k), } } } @@ -164,9 +171,9 @@ impl InnerInputState { fn update( &mut self, evts: &mut Vec, - resolved_events: &mut Vec, + resolved_events: &mut Vec, layout: &Taffy, - dom: &mut Dom, + dom: &mut TuiDom, ) { let previous_mouse = self.mouse.clone(); @@ -192,33 +199,33 @@ impl InnerInputState { if old_focus != self.focus_state.last_focused_id { // elements with listeners will always have a element id - if let Some(RealNodeId::ElementId(id)) = self.focus_state.last_focused_id { - resolved_events.push(UserEvent { - scope_id: None, - priority: EventPriority::Medium, - name: "focus", - element: Some(id), - data: Arc::new(FocusData {}), - bubbles: event_bubbles("focus"), - }); - resolved_events.push(UserEvent { - scope_id: None, - priority: EventPriority::Medium, - name: "focusin", - element: Some(id), - data: Arc::new(FocusData {}), - bubbles: event_bubbles("focusin"), - }); + if let Some(id) = self.focus_state.last_focused_id { + let element = dom.tree.get(id).unwrap(); + if let Some(id) = element.node_data.element_id { + resolved_events.push(Event { + name: "focus", + id, + data: Rc::new(FocusData {}), + bubbles: event_bubbles("focus"), + }); + resolved_events.push(Event { + name: "focusin", + id, + data: Rc::new(FocusData {}), + bubbles: event_bubbles("focusin"), + }); + } } - if let Some(RealNodeId::ElementId(id)) = old_focus { - resolved_events.push(UserEvent { - scope_id: None, - priority: EventPriority::Medium, - name: "focusout", - element: Some(id), - data: Arc::new(FocusData {}), - bubbles: event_bubbles("focusout"), - }); + if let Some(id) = old_focus { + let element = dom.tree.get(id).unwrap(); + if let Some(id) = element.node_data.element_id { + resolved_events.push(Event { + name: "focusout", + id, + data: Rc::new(FocusData {}), + bubbles: event_bubbles("focusout"), + }); + } } } @@ -230,9 +237,9 @@ impl InnerInputState { fn resolve_mouse_events( &mut self, previous_mouse: Option, - resolved_events: &mut Vec, + resolved_events: &mut Vec, layout: &Taffy, - dom: &mut Dom, + dom: &mut TuiDom, ) { fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool { let Point { x, y } = layout.location; @@ -244,28 +251,29 @@ impl InnerInputState { fn try_create_event( name: &'static str, - data: Arc, - will_bubble: &mut FxHashSet, - resolved_events: &mut Vec, - node: &Node, - dom: &Dom, + data: Rc, + will_bubble: &mut FxHashSet, + resolved_events: &mut Vec, + node: &TuiNode, + dom: &TuiDom, ) { // only trigger event if the event was not triggered already by a child - let id = node.mounted_id(); + let id = node.node_data.node_id; if will_bubble.insert(id) { - let mut parent = node.node_data.parent; - while let Some(parent_id) = parent { + let mut parent = dom.parent(id); + while let Some(current_parent) = parent { + let parent_id = current_parent.node_data.node_id; will_bubble.insert(parent_id); - parent = dom[parent_id].node_data.parent; + parent = dom.parent(parent_id); + } + if let Some(id) = node.mounted_id() { + resolved_events.push(Event { + name, + id, + data, + bubbles: event_bubbles(name), + }) } - resolved_events.push(UserEvent { - scope_id: None, - priority: EventPriority::Medium, - name, - element: Some(id.as_element_id()), - data, - bubbles: event_bubbles(name), - }) } } @@ -326,7 +334,7 @@ impl InnerInputState { if currently_contains && previously_contained { try_create_event( "mousemove", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -350,7 +358,7 @@ impl InnerInputState { if currently_contains && !previously_contained { try_create_event( "mouseenter", - Arc::new(mouse_data.clone()), + Rc::new(mouse_data.clone()), &mut will_bubble, resolved_events, node, @@ -373,7 +381,7 @@ impl InnerInputState { if currently_contains && !previously_contained { try_create_event( "mouseover", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -393,7 +401,7 @@ impl InnerInputState { if currently_contains { try_create_event( "mousedown", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -414,7 +422,7 @@ impl InnerInputState { if currently_contains { try_create_event( "mouseup", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -436,7 +444,7 @@ impl InnerInputState { if currently_contains { try_create_event( "click", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -459,7 +467,7 @@ impl InnerInputState { if currently_contains { try_create_event( "contextmenu", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -483,7 +491,7 @@ impl InnerInputState { if currently_contains { try_create_event( "wheel", - Arc::new(w.clone()), + Rc::new(w.clone()), &mut will_bubble, resolved_events, node, @@ -508,7 +516,7 @@ impl InnerInputState { if !currently_contains && previously_contained { try_create_event( "mouseleave", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -531,7 +539,7 @@ impl InnerInputState { if !currently_contains && previously_contained { try_create_event( "mouseout", - Arc::new(prepare_mouse_data(mouse_data, &node_layout)), + Rc::new(prepare_mouse_data(mouse_data, &node_layout)), &mut will_bubble, resolved_events, node, @@ -549,7 +557,7 @@ impl InnerInputState { let currently_contains = layout_contains_point(node_layout, new_pos); if currently_contains && node.state.focus.level.focusable() { - focus_id = Some(node.mounted_id()); + focus_id = Some(node.node_data.node_id); } }); if let Some(id) = focus_id { @@ -564,12 +572,11 @@ impl InnerInputState { // } } -fn get_abs_layout(node: &Node, dom: &Dom, taffy: &Taffy) -> Layout { +fn get_abs_layout(node: &TuiNode, dom: &TuiDom, taffy: &Taffy) -> Layout { let mut node_layout = *taffy.layout(node.state.layout.node.unwrap()).unwrap(); let mut current = node; - while let Some(parent_id) = current.node_data.parent { - let parent = &dom[parent_id]; + while let Some(parent) = dom.parent(current.node_data.node_id) { current = parent; let parent_layout = taffy.layout(parent.state.layout.node.unwrap()).unwrap(); node_layout.location.x += parent_layout.location.x; @@ -614,11 +621,11 @@ impl RinkInputHandler { ) } - pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &Dom) { + pub(crate) fn prune(&self, mutations: &dioxus_core::Mutations, rdom: &TuiDom) { self.state.borrow_mut().focus_state.prune(mutations, rdom); } - pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut Dom) -> Vec { + pub(crate) fn get_events(&self, layout: &Taffy, dom: &mut TuiDom) -> Vec { let mut resolved_events = Vec::new(); (*self.state).borrow_mut().update( @@ -651,7 +658,7 @@ impl RinkInputHandler { }) .map(|evt| (evt.0, evt.1.into_any())); - let mut hm: FxHashMap<&'static str, Vec>> = FxHashMap::default(); + let mut hm: FxHashMap<&'static str, Vec>> = FxHashMap::default(); for (event, data) in events { if let Some(v) = hm.get_mut(event) { v.push(data); @@ -663,14 +670,14 @@ impl RinkInputHandler { for node in dom.get_listening_sorted(event) { for data in &datas { if node.state.focused { - resolved_events.push(UserEvent { - scope_id: None, - priority: EventPriority::Medium, - name: event, - element: Some(node.mounted_id().as_element_id()), - data: data.clone(), - bubbles: event_bubbles(event), - }); + if let Some(id) = node.mounted_id() { + resolved_events.push(Event { + name: event, + id, + data: data.clone(), + bubbles: event_bubbles(event), + }); + } } } } diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index e65e5b2df..2abebe856 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -1,12 +1,10 @@ -use std::cell::RefCell; -use std::rc::Rc; +use std::sync::{Arc, Mutex}; use dioxus_core::*; use dioxus_native_core::layout_attributes::apply_layout_attributes; +use dioxus_native_core::node::OwnedAttributeView; use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView}; -use dioxus_native_core::real_dom::OwnedAttributeView; use dioxus_native_core::state::ChildDepState; -use dioxus_native_core::RealNodeId; use dioxus_native_core_macro::sorted_str_slice; use taffy::prelude::*; @@ -42,8 +40,8 @@ pub(crate) struct TaffyLayout { } impl ChildDepState for TaffyLayout { - type Ctx = Rc>; - type DepState = Self; + type Ctx = Arc>; + type DepState = (Self,); // use tag to force this to be called when a node is built const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(SORTED_LAYOUT_ATTRS)) @@ -54,14 +52,14 @@ impl ChildDepState for TaffyLayout { fn reduce<'a>( &mut self, node: NodeView, - children: impl Iterator, + children: impl Iterator, ctx: &Self::Ctx, ) -> bool where Self::DepState: 'a, { let mut changed = false; - let mut taffy = ctx.borrow_mut(); + let mut taffy = ctx.lock().expect("poisoned taffy"); let mut style = Style::default(); if let Some(text) = node.text() { let char_len = text.chars().count(); @@ -81,7 +79,7 @@ impl ChildDepState for TaffyLayout { taffy.set_style(n, style).unwrap(); } } else { - self.node = PossiblyUninitalized::Initialized(taffy.new_node(style, &[]).unwrap()); + self.node = PossiblyUninitalized::Initialized(taffy.new_leaf(style).unwrap()); changed = true; } } else { @@ -101,14 +99,14 @@ impl ChildDepState for TaffyLayout { } // the root node fills the entire area - if node.id() == RealNodeId::ElementId(ElementId(0)) { + if node.id() == Some(ElementId(0)) { apply_layout_attributes("width", "100%", &mut style); apply_layout_attributes("height", "100%", &mut style); } // Set all direct nodes as our children let mut child_layout = vec![]; - for l in children { + for (l,) in children { child_layout.push(l.node.unwrap()); } @@ -121,7 +119,7 @@ impl ChildDepState for TaffyLayout { } } else { self.node = PossiblyUninitalized::Initialized( - taffy.new_node(style, &child_layout).unwrap(), + taffy.new_with_children(style, &child_layout).unwrap(), ); changed = true; } diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 70d90ef0a..6b514492f 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -1,24 +1,23 @@ use anyhow::Result; -use anymap::AnyMap; use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use dioxus_core::exports::futures_channel::mpsc::unbounded; +use futures_channel::mpsc::unbounded; use dioxus_core::*; -use dioxus_native_core::{real_dom::RealDom, RealNodeId}; +use dioxus_native_core::{real_dom::RealDom, NodeId, SendAnyMap, FxDashSet}; use focus::FocusState; use futures::{ channel::mpsc::{UnboundedReceiver, UnboundedSender}, pin_mut, StreamExt, }; use query::Query; -use std::cell::RefCell; +use std::{cell::RefCell, sync::{Mutex, Arc}}; use std::rc::Rc; use std::{io, time::Duration}; use taffy::Taffy; -pub use taffy::{geometry::Point, prelude::Size}; +pub use taffy::{geometry::Point, prelude::*}; use tui::{backend::CrosstermBackend, layout::Rect, Terminal}; mod config; @@ -80,10 +79,10 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) { let cx = dom.base_scope(); let rdom = Rc::new(RefCell::new(RealDom::new())); - let taffy = Rc::new(RefCell::new(Taffy::new())); - cx.provide_root_context(state); - cx.provide_root_context(TuiContext { tx: event_tx_clone }); - cx.provide_root_context(Query { + let taffy = Arc::new(Mutex::new(Taffy::new())); + cx.provide_context(state); + cx.provide_context(TuiContext { tx: event_tx_clone }); + cx.provide_context(Query { rdom: rdom.clone(), stretch: taffy.clone(), }); @@ -91,8 +90,8 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) { { let mut rdom = rdom.borrow_mut(); let mutations = dom.rebuild(); - let to_update = rdom.apply_mutations(vec![mutations]); - let mut any_map = AnyMap::new(); + let to_update = rdom.apply_mutations(mutations); + let mut any_map = SendAnyMap::new(); any_map.insert(taffy.clone()); let _to_rerender = rdom.update_state(to_update, any_map); } @@ -114,8 +113,8 @@ fn render_vdom( mut event_reciever: UnboundedReceiver, handler: RinkInputHandler, cfg: Config, - rdom: Rc>, - taffy: Rc>, + rdom: Rc>, + taffy: Arc>, mut register_event: impl FnMut(crossterm::event::Event), ) -> Result<()> { tokio::runtime::Builder::new_current_thread() @@ -133,10 +132,8 @@ fn render_vdom( terminal.clear().unwrap(); } - let mut to_rerender: rustc_hash::FxHashSet = - vec![RealNodeId::ElementId(ElementId(0))] - .into_iter() - .collect(); + let mut to_rerender =FxDashSet::default(); + to_rerender.insert(NodeId(0)); let mut updated = true; loop { @@ -153,17 +150,17 @@ fn render_vdom( if !to_rerender.is_empty() || updated { updated = false; - fn resize(dims: Rect, taffy: &mut Taffy, rdom: &Dom) { + fn resize(dims: Rect, taffy: &mut Taffy, rdom: &TuiDom) { let width = dims.width; let height = dims.height; - let root_node = rdom[ElementId(0)].state.layout.node.unwrap(); + let root_node = rdom[NodeId(0)].state.layout.node.unwrap(); taffy .compute_layout( root_node, Size { - width: taffy::prelude::Number::Defined((width - 1) as f32), - height: taffy::prelude::Number::Defined((height - 1) as f32), + width: AvailableSpace::Definite((width - 1) as f32), + height: AvailableSpace::Definite((height - 1) as f32), }, ) .unwrap(); @@ -172,15 +169,15 @@ fn render_vdom( terminal.draw(|frame| { let rdom = rdom.borrow(); // size is guaranteed to not change when rendering - resize(frame.size(), &mut taffy.borrow_mut(), &rdom); - let root = &rdom[ElementId(0)]; + resize(frame.size(), &mut taffy.lock().expect("taffy lock poisoned"), &rdom); + let root = &rdom[NodeId(0)]; render::render_vnode( frame, - &taffy.borrow(), + &taffy.lock().expect("taffy lock poisoned"), &rdom, root, cfg, - Point::zero(), + Point::ZERO, ); })?; } else { @@ -192,7 +189,7 @@ fn render_vdom( width: 100, height: 100, }, - &mut taffy.borrow_mut(), + &mut taffy.lock().expect("taffy lock poisoned"), &rdom, ); } @@ -234,23 +231,21 @@ fn render_vdom( { let evts = { let mut rdom = rdom.borrow_mut(); - handler.get_events(&taffy.borrow(), &mut rdom) + handler.get_events(&taffy.lock().expect("taffy lock poisoned"), &mut rdom) }; { updated |= handler.state().focus_state.clean(); } for e in evts { - vdom.handle_message(SchedulerMsg::Event(e)); + vdom.handle_event(e.name, e.data, e.id, e.bubbles) } let mut rdom = rdom.borrow_mut(); - let mutations = vdom.work_with_deadline(|| false); - for m in &mutations { - handler.prune(m, &rdom); - } + let mutations = vdom.render_immediate(); + handler.prune(&mutations, &rdom); // updates the dom's nodes let to_update = rdom.apply_mutations(mutations); // update the style and layout - let mut any_map = AnyMap::new(); + let mut any_map = SendAnyMap::new(); any_map.insert(taffy.clone()); to_rerender = rdom.update_state(to_update, any_map); } diff --git a/packages/tui/src/node.rs b/packages/tui/src/node.rs index daa85500e..33f74a011 100644 --- a/packages/tui/src/node.rs +++ b/packages/tui/src/node.rs @@ -4,12 +4,12 @@ use crate::style_attributes::StyleModifier; use dioxus_native_core::{real_dom::RealDom, state::*}; use dioxus_native_core_macro::{sorted_str_slice, State}; -pub(crate) type Dom = RealDom; -pub(crate) type Node = dioxus_native_core::real_dom::Node; +pub(crate) type TuiDom = RealDom; +pub(crate) type TuiNode = dioxus_native_core::node::Node; #[derive(Debug, Clone, State, Default)] pub(crate) struct NodeState { - #[child_dep_state(layout, RefCell)] + #[child_dep_state(layout, Mutex)] pub layout: TaffyLayout, #[parent_dep_state(style)] pub style: StyleModifier, @@ -45,7 +45,8 @@ impl Default for PreventDefault { } } -impl NodeDepState<()> for PreventDefault { +impl NodeDepState for PreventDefault { + type DepState = (); type Ctx = (); const NODE_MASK: dioxus_native_core::node_ref::NodeMask = diff --git a/packages/tui/src/query.rs b/packages/tui/src/query.rs index 0a8a886cc..15b7fd7f2 100644 --- a/packages/tui/src/query.rs +++ b/packages/tui/src/query.rs @@ -1,6 +1,7 @@ use std::{ cell::{Ref, RefCell}, rc::Rc, + sync::{Arc, Mutex, MutexGuard}, }; use dioxus_core::ElementId; @@ -10,7 +11,7 @@ use taffy::{ Taffy, }; -use crate::Dom; +use crate::TuiDom; /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered. /// Provided as a root context for all tui applictions. @@ -45,24 +46,28 @@ use crate::Dom; /// ``` #[derive(Clone)] pub struct Query { - pub(crate) rdom: Rc>, - pub(crate) stretch: Rc>, + pub(crate) rdom: Rc>, + pub(crate) stretch: Arc>, } impl Query { pub fn get(&self, id: ElementId) -> ElementRef { - ElementRef::new(self.rdom.borrow(), self.stretch.borrow(), id) + ElementRef::new( + self.rdom.borrow(), + self.stretch.lock().expect("taffy lock poisoned"), + id, + ) } } pub struct ElementRef<'a> { - inner: Ref<'a, Dom>, - stretch: Ref<'a, Taffy>, + inner: Ref<'a, TuiDom>, + stretch: MutexGuard<'a, Taffy>, id: ElementId, } impl<'a> ElementRef<'a> { - fn new(inner: Ref<'a, Dom>, stretch: Ref<'a, Taffy>, id: ElementId) -> Self { + fn new(inner: Ref<'a, TuiDom>, stretch: MutexGuard<'a, Taffy>, id: ElementId) -> Self { Self { inner, stretch, id } } diff --git a/packages/tui/src/render.rs b/packages/tui/src/render.rs index 9cea0ced0..7beb543e2 100644 --- a/packages/tui/src/render.rs +++ b/packages/tui/src/render.rs @@ -1,3 +1,4 @@ +use dioxus_native_core::tree::TreeView; use std::io::Stdout; use taffy::{ geometry::Point, @@ -10,7 +11,7 @@ use crate::{ style::{RinkColor, RinkStyle}, style_attributes::{BorderEdge, BorderStyle}, widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext}, - Config, Dom, Node, + Config, TuiDom, TuiNode, }; const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5]; @@ -18,12 +19,12 @@ const RADIUS_MULTIPLIER: [f32; 2] = [1.0, 0.5]; pub(crate) fn render_vnode( frame: &mut tui::Frame>, layout: &Taffy, - rdom: &Dom, - node: &Node, + rdom: &TuiDom, + node: &TuiNode, cfg: Config, parent_location: Point, ) { - use dioxus_native_core::real_dom::NodeType; + use dioxus_native_core::node::NodeType; if let NodeType::Placeholder = &node.node_data.node_type { return; @@ -68,7 +69,7 @@ pub(crate) fn render_vnode( frame.render_widget(WidgetWithContext::new(label, cfg), area); } } - NodeType::Element { children, .. } => { + NodeType::Element { .. } => { let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16); // the renderer will panic if a node is rendered out of range even if the size is zero @@ -76,7 +77,7 @@ pub(crate) fn render_vnode( frame.render_widget(WidgetWithContext::new(node, cfg), area); } - for c in children { + for c in rdom.children_ids(node.node_data.node_id).unwrap() { render_vnode(frame, layout, rdom, &rdom[*c], cfg, location); } } @@ -84,7 +85,7 @@ pub(crate) fn render_vnode( } } -impl RinkWidget for &Node { +impl RinkWidget for &TuiNode { fn render(self, area: Rect, mut buf: RinkBuffer<'_>) { use tui::symbols::line::*; diff --git a/packages/tui/src/style_attributes.rs b/packages/tui/src/style_attributes.rs index 0aa674dd8..9d1a61d12 100644 --- a/packages/tui/src/style_attributes.rs +++ b/packages/tui/src/style_attributes.rs @@ -31,8 +31,8 @@ use dioxus_native_core::{ layout_attributes::parse_value, + node::OwnedAttributeView, node_ref::{AttributeMask, NodeMask, NodeView}, - real_dom::OwnedAttributeView, state::ParentDepState, }; use dioxus_native_core_macro::sorted_str_slice; @@ -48,12 +48,12 @@ pub struct StyleModifier { impl ParentDepState for StyleModifier { type Ctx = (); - type DepState = Self; + type DepState = (Self,); // todo: seperate each attribute into it's own class const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::Static(SORTED_STYLE_ATTRS)).with_element(); - fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, _: &Self::Ctx) -> bool { + fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, _: &Self::Ctx) -> bool { let mut new = StyleModifier::default(); if parent.is_some() { new.core.fg = None; @@ -91,7 +91,7 @@ impl ParentDepState for StyleModifier { } // keep the text styling from the parent element - if let Some(parent) = parent { + if let Some((parent,)) = parent { let mut new_style = new.core.merge(parent.core); new_style.bg = new.core.bg; new.core = new_style; From 11b37ee141b856dd50abdc2b7e17dfff1ded9060 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 09:44:43 -0600 Subject: [PATCH 26/43] more progress updating tui --- packages/core/src/arena.rs | 3 -- packages/core/src/create.rs | 5 ---- packages/core/src/diff.rs | 15 ---------- packages/core/src/scheduler/wait.rs | 16 ---------- packages/core/src/virtual_dom.rs | 7 ----- packages/native-core/src/node_ref.rs | 6 ++++ packages/native-core/src/passes.rs | 2 +- packages/native-core/src/real_dom.rs | 44 ++++++++++++++++++++-------- packages/native-core/src/utils.rs | 1 + packages/tui/src/layout.rs | 2 +- packages/tui/src/lib.rs | 7 +++-- 11 files changed, 44 insertions(+), 64 deletions(-) diff --git a/packages/core/src/arena.rs b/packages/core/src/arena.rs index d6af9b051..024ad5375 100644 --- a/packages/core/src/arena.rs +++ b/packages/core/src/arena.rs @@ -69,7 +69,6 @@ impl VirtualDom { ) } - println!("reclaiming {:?}", el); self.elements.try_remove(el.0) } @@ -95,9 +94,7 @@ impl VirtualDom { // Drop all the hooks once the children are dropped // this means we'll drop hooks bottom-up for hook in scope.hook_list.get_mut().drain(..) { - println!("dropping hook !"); drop(unsafe { BumpBox::from_raw(hook) }); - println!("hook dropped !"); } } diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index feaa276e4..b0a463c9e 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -388,11 +388,6 @@ impl<'b> VirtualDom { // Set the placeholder of the scope self.scopes[scope.0].placeholder.set(Some(new_id)); - println!( - "assigning id {:?} to path {:?}, template: {:?}", - new_id, &template.template.node_paths, template.template - ); - // Since the placeholder is already in the DOM, we don't create any new nodes self.mutations.push(AssignId { id: new_id, diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index c0b14d0bd..5f2bccbb1 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -61,17 +61,10 @@ impl<'b> VirtualDom { } fn diff_node(&mut self, left_template: &'b VNode<'b>, right_template: &'b VNode<'b>) { - println!( - "diffing node {:#?},\n\n{:#?}", - left_template.template.name, right_template.template.name - ); - if left_template.template.name != right_template.template.name { return self.light_diff_templates(left_template, right_template); } - println!("diffing attributs..."); - for (left_attr, right_attr) in left_template .dynamic_attrs .iter() @@ -106,8 +99,6 @@ impl<'b> VirtualDom { } } - println!("diffing dyn nodes..."); - for (idx, (left_node, right_node)) in left_template .dynamic_nodes .iter() @@ -133,8 +124,6 @@ impl<'b> VirtualDom { }; } - println!("applying roots..."); - // Make sure the roots get transferred over for (left, right) in left_template .root_ids @@ -396,8 +385,6 @@ impl<'b> VirtualDom { "all siblings must be keyed or all siblings must be non-keyed" ); - println!("Diffing fragment {:?}, {:?}", old.len(), new.len()); - if new_is_keyed && old_is_keyed { self.diff_keyed_children(old, new); } else { @@ -420,8 +407,6 @@ impl<'b> VirtualDom { debug_assert!(!new.is_empty()); debug_assert!(!old.is_empty()); - println!("Diffing non keyed children"); - match dbg!(old.len().cmp(&new.len())) { Ordering::Greater => self.remove_nodes(&old[new.len()..]), Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()), diff --git a/packages/core/src/scheduler/wait.rs b/packages/core/src/scheduler/wait.rs index e13b0fd45..f8fdfb595 100644 --- a/packages/core/src/scheduler/wait.rs +++ b/packages/core/src/scheduler/wait.rs @@ -40,8 +40,6 @@ impl VirtualDom { } pub(crate) fn handle_suspense_wakeup(&mut self, id: SuspenseId) { - println!("suspense notified"); - let leaf = self .scheduler .leaves @@ -67,13 +65,6 @@ impl VirtualDom { // safety: we're not going to modify the suspense context but we don't want to make a clone of it let fiber = self.acquire_suspense_boundary(leaf.scope_id); - println!("ready pool"); - - println!( - "Existing mutations {:?}, scope {:?}", - fiber.mutations, fiber.id - ); - let scope = &mut self.scopes[scope_id.0]; let arena = scope.current_frame(); @@ -106,16 +97,9 @@ impl VirtualDom { std::mem::swap(&mut self.mutations, mutations); if fiber.waiting_on.borrow().is_empty() { - println!("fiber is finished!"); self.finished_fibers.push(fiber.id); - } else { - println!("fiber is not finished {:?}", fiber.waiting_on); } - } else { - println!("nodes arent right"); } - } else { - println!("not ready"); } } } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index a7046636b..dbcba7548 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -362,8 +362,6 @@ impl VirtualDom { let target_path = el_ref.path; for (idx, attr) in template.dynamic_attrs.iter().enumerate() { - println!("{:?} \n {:?} \n {:?}", attr, name, element); - let this_path = template.template.attr_paths[idx]; // listeners are required to be prefixed with "on", but they come back to the virtualdom with that missing @@ -385,8 +383,6 @@ impl VirtualDom { } } - println!("calling listeners: {:?}", listeners); - // Now that we've accumulated all the parent attributes for the target element, call them in reverse order // We check the bubble state between each call to see if the event has been stopped from bubbling for listener in listeners.drain(..).rev() { @@ -403,8 +399,6 @@ impl VirtualDom { parent_path = template.parent.and_then(|id| self.elements.get(id.0)); } - - println!("all listeners exhausted"); } /// Wait for the scheduler to have any work. @@ -527,7 +521,6 @@ impl VirtualDom { /// /// If no suspense trees are present pub async fn render_with_deadline(&mut self, deadline: impl Future) -> Mutations { - println!("render with deadline"); pin_mut!(deadline); loop { diff --git a/packages/native-core/src/node_ref.rs b/packages/native-core/src/node_ref.rs index 759cc5ffb..d178c2d18 100644 --- a/packages/native-core/src/node_ref.rs +++ b/packages/native-core/src/node_ref.rs @@ -3,6 +3,7 @@ use dioxus_core::ElementId; use crate::{ node::{NodeData, NodeType, OwnedAttributeView}, state::union_ordered_iter, + RealNodeId, }; /// A view into a [VNode] with limited access. @@ -26,6 +27,11 @@ impl<'a> NodeView<'a> { self.inner.element_id } + /// Get the node id of the node + pub fn node_id(&self) -> RealNodeId { + self.inner.node_id + } + /// Get the tag of the node if the tag is enabled in the mask pub fn tag(&self) -> Option<&'a str> { self.mask diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index fadc8e71e..fe774f7ba 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -334,7 +334,7 @@ pub fn resolve_passes>( if pass .dependancies() .iter() - .all(|d| resolved_passes.contains(d)) + .all(|d| resolved_passes.contains(d) || *d == pass_id) && !pass_mask.overlaps(currently_borrowed) { let pass = passes.remove(i); diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index f6b58a933..5c4771f0f 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -1,5 +1,6 @@ use dioxus_core::{ElementId, Mutations, TemplateNode}; use rustc_hash::{FxHashMap, FxHashSet}; +use std::fmt::Debug; use std::ops::{Deref, DerefMut, Index, IndexMut}; use crate::node::{Node, NodeType, OwnedAttributeDiscription, OwnedAttributeValue}; @@ -32,6 +33,7 @@ pub struct RealDom { nodes_listening: FxHashMap>, stack: Vec, templates: FxHashMap>, + root_initialized: bool, } impl Default for RealDom { @@ -55,10 +57,11 @@ impl RealDom { RealDom { tree, - node_id_mapping: Vec::new(), + node_id_mapping: vec![Some(root_id)], nodes_listening: FxHashMap::default(), stack: vec![root_id], templates: FxHashMap::default(), + root_initialized: false, } } @@ -99,7 +102,7 @@ impl RealDom { &mut self, node: &TemplateNode, mutations_vec: &mut FxHashMap, - ) -> Option { + ) -> RealNodeId { match node { TemplateNode::Element { tag, @@ -132,21 +135,31 @@ impl RealDom { }); let node_id = self.create_node(node); for child in *children { - if let Some(child_id) = self.create_template_node(child, mutations_vec) { - self.add_child(node_id, child_id); - } + let child_id = self.create_template_node(child, mutations_vec); + self.add_child(node_id, child_id); } self.stack.push(node_id); - Some(node_id) + node_id } TemplateNode::Text(txt) => { let node_id = self.create_node(Node::new(NodeType::Text { text: txt.to_string(), })); self.stack.push(node_id); - Some(node_id) + node_id + } + TemplateNode::Dynamic(_) => { + let node_id = self.create_node(Node::new(NodeType::Placeholder)); + self.stack.push(node_id); + node_id + } + TemplateNode::DynamicText(_) => { + let node_id = self.create_node(Node::new(NodeType::Text { + text: String::new(), + })); + self.stack.push(node_id); + node_id } - _ => None, } } @@ -156,13 +169,17 @@ impl RealDom { for template in mutations.templates { let mut template_root_ids = Vec::new(); for root in template.roots { - if let Some(id) = self.create_template_node(root, &mut nodes_updated) { - template_root_ids.push(id); - } + let id = self.create_template_node(root, &mut nodes_updated); + template_root_ids.push(id); } self.templates .insert(template.name.to_string(), template_root_ids); } + if !self.root_initialized { + self.root_initialized = true; + let root_id = self.tree.root(); + nodes_updated.insert(root_id, NodeMask::ALL); + } for e in mutations.edits { use dioxus_core::Mutation::*; match e { @@ -171,7 +188,7 @@ impl RealDom { let parent = self.element_to_node_id(id); for child in children { self.add_child(parent, child); - mark_dirty(parent, NodeMask::ALL, &mut nodes_updated); + mark_dirty(child, NodeMask::ALL, &mut nodes_updated); } } AssignId { path, id } => { @@ -207,6 +224,7 @@ impl RealDom { let template_id = self.templates[name][index]; let clone_id = self.clone_node(template_id, &mut nodes_updated); self.set_element_id(clone_id, id); + self.stack.push(clone_id); } ReplaceWith { id, m } => { let new_nodes = self.stack.split_off(m); @@ -218,7 +236,7 @@ impl RealDom { self.tree.remove(old_node_id); } ReplacePlaceholder { path, m } => { - let new_nodes = self.stack.split_off(m); + let new_nodes = self.stack.split_off(self.stack.len() - m); let old_node_id = self.load_child(path); for new in new_nodes { self.tree.insert_after(old_node_id, new); diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index 70d016d92..bec658624 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -1,5 +1,6 @@ use crate::{node::NodeType, real_dom::RealDom, state::State, tree::TreeView, NodeId, RealNodeId}; use dioxus_core::{Mutation, Mutations}; +use std::fmt::Debug; pub enum ElementProduced { /// The iterator produced an element by progressing to the next node in a depth first order. diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index 2abebe856..54513268f 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -17,7 +17,7 @@ impl PossiblyUninitalized { pub fn unwrap(self) -> T { match self { Self::Initialized(i) => i, - _ => panic!(), + _ => panic!("uninitalized"), } } pub fn ok(self) -> Option { diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 6b514492f..56a34a047 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -16,7 +16,7 @@ use query::Query; use std::{cell::RefCell, sync::{Mutex, Arc}}; use std::rc::Rc; use std::{io, time::Duration}; -use taffy::Taffy; +use taffy::{Taffy}; pub use taffy::{geometry::Point, prelude::*}; use tui::{backend::CrosstermBackend, layout::Rect, Terminal}; @@ -168,12 +168,13 @@ fn render_vdom( if let Some(terminal) = &mut terminal { terminal.draw(|frame| { let rdom = rdom.borrow(); + let mut taffy =taffy.lock().expect("taffy lock poisoned"); // size is guaranteed to not change when rendering - resize(frame.size(), &mut taffy.lock().expect("taffy lock poisoned"), &rdom); + resize(frame.size(), &mut *taffy, &rdom); let root = &rdom[NodeId(0)]; render::render_vnode( frame, - &taffy.lock().expect("taffy lock poisoned"), + &*taffy, &rdom, root, cfg, From 1587056a4be3b5310be1b72a2a7ea99cd3e69f81 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 13:30:13 -0600 Subject: [PATCH 27/43] some basic tui examples working --- packages/native-core/src/passes.rs | 4 +++- packages/native-core/src/real_dom.rs | 9 ++++++--- packages/native-core/src/state.rs | 4 +--- packages/tui/src/lib.rs | 15 +++++++++------ packages/tui/src/node.rs | 3 ++- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index fe774f7ba..bdc7c7e5b 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -215,8 +215,10 @@ fn resolve_downward_pass + ?Sized>( while let Some(id) = dirty.pop_front() { let (node, parent) = tree.node_parent_mut(id).unwrap(); let result = pass.pass(node, parent, ctx); - if result.mark_dirty || result.progress { + if result.mark_dirty { nodes_updated.insert(id); + } + if result.mark_dirty || result.progress { for id in tree.children_ids(id).unwrap() { if result.mark_dirty { for dependant in pass.dependants() { diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 5c4771f0f..7ee8926d4 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -164,7 +164,10 @@ impl RealDom { } /// Updates the dom with some mutations and return a set of nodes that were updated. Pass the dirty nodes to update_state. - pub fn apply_mutations(&mut self, mutations: Mutations) -> DirtyNodeStates { + pub fn apply_mutations( + &mut self, + mutations: Mutations, + ) -> (DirtyNodeStates, FxHashMap) { let mut nodes_updated: FxHashMap = FxHashMap::default(); for template in mutations.templates { let mut template_root_ids = Vec::new(); @@ -348,7 +351,7 @@ impl RealDom { } let dirty_nodes = DirtyNodeStates::default(); - for (n, mask) in nodes_updated { + for (&n, mask) in &nodes_updated { // remove any nodes that were created and then removed in the same mutations from the dirty nodes list if self.tree.contains(n) { for (m, p) in S::MASKS.iter().zip(S::PASSES.iter()) { @@ -359,7 +362,7 @@ impl RealDom { } } - dirty_nodes + (dirty_nodes, nodes_updated) } /// Update the state of the dom, after appling some mutations. This will keep the nodes in the dom up to date with their VNode counterparts. diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index 09ae4333b..365aa151c 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -221,10 +221,8 @@ pub trait State: Default + Clone + 'static { tree: &mut T, ctx: SendAnyMap, ) -> FxDashSet { - let set = FxDashSet::default(); let passes = Self::PASSES.iter().collect(); - resolve_passes(tree, dirty, passes, ctx); - set + resolve_passes(tree, dirty, passes, ctx) } } diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 56a34a047..8b35ac54c 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -6,7 +6,7 @@ use crossterm::{ }; use futures_channel::mpsc::unbounded; use dioxus_core::*; -use dioxus_native_core::{real_dom::RealDom, NodeId, SendAnyMap, FxDashSet}; +use dioxus_native_core::{real_dom::RealDom, NodeId, SendAnyMap, FxDashSet, NodeMask}; use focus::FocusState; use futures::{ channel::mpsc::{UnboundedReceiver, UnboundedSender}, @@ -90,7 +90,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) { { let mut rdom = rdom.borrow_mut(); let mutations = dom.rebuild(); - let to_update = rdom.apply_mutations(mutations); + let (to_update,_) = rdom.apply_mutations(mutations); let mut any_map = SendAnyMap::new(); any_map.insert(taffy.clone()); let _to_rerender = rdom.update_state(to_update, any_map); @@ -135,16 +135,14 @@ fn render_vdom( let mut to_rerender =FxDashSet::default(); to_rerender.insert(NodeId(0)); let mut updated = true; - + loop { /* -> render the nodes in the right place with tui/crossterm -> wait for changes -> resolve events -> lazily update the layout and style based on nodes changed - use simd to compare lines for diffing? - todo: lazy re-rendering */ @@ -244,11 +242,16 @@ fn render_vdom( let mutations = vdom.render_immediate(); handler.prune(&mutations, &rdom); // updates the dom's nodes - let to_update = rdom.apply_mutations(mutations); + let (to_update, dirty) = rdom.apply_mutations(mutations); // update the style and layout let mut any_map = SendAnyMap::new(); any_map.insert(taffy.clone()); to_rerender = rdom.update_state(to_update, any_map); + for (id, mask) in dirty { + if mask.overlaps(&NodeMask::new().with_text()) { + to_rerender.insert(id); + } + } } } diff --git a/packages/tui/src/node.rs b/packages/tui/src/node.rs index 33f74a011..26ea4412e 100644 --- a/packages/tui/src/node.rs +++ b/packages/tui/src/node.rs @@ -54,7 +54,8 @@ impl NodeDepState for PreventDefault { dioxus_native_core::node_ref::AttributeMask::Static(&sorted_str_slice!([ "dioxus-prevent-default" ])), - ); + ) + .with_listeners(); fn reduce( &mut self, From 24cb503c09c4b2a670deb86cddad3d51b4cbd68b Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 13:49:55 -0600 Subject: [PATCH 28/43] don't push template nodes onto the stack --- packages/native-core/src/real_dom.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index 7ee8926d4..f5fb17603 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -138,26 +138,22 @@ impl RealDom { let child_id = self.create_template_node(child, mutations_vec); self.add_child(node_id, child_id); } - self.stack.push(node_id); node_id } TemplateNode::Text(txt) => { let node_id = self.create_node(Node::new(NodeType::Text { text: txt.to_string(), })); - self.stack.push(node_id); node_id } TemplateNode::Dynamic(_) => { let node_id = self.create_node(Node::new(NodeType::Placeholder)); - self.stack.push(node_id); node_id } TemplateNode::DynamicText(_) => { let node_id = self.create_node(Node::new(NodeType::Text { text: String::new(), })); - self.stack.push(node_id); node_id } } From 9a37da639354334f0c78b313fb3c78bb29e884fc Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 13:53:43 -0600 Subject: [PATCH 29/43] update hover example --- packages/tui/examples/tui_hover.rs | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/tui/examples/tui_hover.rs b/packages/tui/examples/tui_hover.rs index 4362876d2..2037adae4 100644 --- a/packages/tui/examples/tui_hover.rs +++ b/packages/tui/examples/tui_hover.rs @@ -1,7 +1,7 @@ -use std::{convert::TryInto, sync::Arc}; - use dioxus::{events::MouseData, prelude::*}; -use dioxus_core::UiEvent; +use dioxus_core::Event; +use std::rc::Rc; +use std::{convert::TryInto, sync::Arc}; fn main() { dioxus_tui::launch(app); @@ -12,7 +12,7 @@ fn app(cx: Scope) -> Element { "#".to_string() + &c.iter().map(|c| format!("{c:02X?}")).collect::() } - fn get_brightness(m: Arc) -> i32 { + fn get_brightness(m: &Rc) -> i32 { let b: i32 = m.held_buttons().len().try_into().unwrap(); 127 * b } @@ -32,8 +32,8 @@ fn app(cx: Scope) -> Element { let buttons = use_state(&cx, || "".to_string()); let modifiers = use_state(&cx, || "".to_string()); - let update_data = move |event: UiEvent| { - let mouse_data = event.data; + let update_data = move |event: Event| { + let mouse_data = event.inner(); page_coordinates.set(format!("{:?}", mouse_data.page_coordinates())); element_coordinates.set(format!("{:?}", mouse_data.element_coordinates())); @@ -62,9 +62,9 @@ fn app(cx: Scope) -> Element { justify_content: "center", align_items: "center", background_color: "{q1_color_str}", - onmouseenter: move |m| q1_color.set([get_brightness(m.data), 0, 0]), - onmousedown: move |m| q1_color.set([get_brightness(m.data), 0, 0]), - onmouseup: move |m| q1_color.set([get_brightness(m.data), 0, 0]), + onmouseenter: move |m| q1_color.set([get_brightness(m.inner()), 0, 0]), + onmousedown: move |m| q1_color.set([get_brightness(m.inner()), 0, 0]), + onmouseup: move |m| q1_color.set([get_brightness(m.inner()), 0, 0]), onwheel: move |w| q1_color.set([q1_color[0] + (10.0*w.delta().strip_units().y) as i32, 0, 0]), onmouseleave: move |_| q1_color.set([200; 3]), onmousemove: update_data, @@ -76,9 +76,9 @@ fn app(cx: Scope) -> Element { justify_content: "center", align_items: "center", background_color: "{q2_color_str}", - onmouseenter: move |m| q2_color.set([get_brightness(m.data); 3]), - onmousedown: move |m| q2_color.set([get_brightness(m.data); 3]), - onmouseup: move |m| q2_color.set([get_brightness(m.data); 3]), + onmouseenter: move |m| q2_color.set([get_brightness(m.inner()); 3]), + onmousedown: move |m| q2_color.set([get_brightness(m.inner()); 3]), + onmouseup: move |m| q2_color.set([get_brightness(m.inner()); 3]), onwheel: move |w| q2_color.set([q2_color[0] + (10.0*w.delta().strip_units().y) as i32;3]), onmouseleave: move |_| q2_color.set([200; 3]), onmousemove: update_data, @@ -96,9 +96,9 @@ fn app(cx: Scope) -> Element { justify_content: "center", align_items: "center", background_color: "{q3_color_str}", - onmouseenter: move |m| q3_color.set([0, get_brightness(m.data), 0]), - onmousedown: move |m| q3_color.set([0, get_brightness(m.data), 0]), - onmouseup: move |m| q3_color.set([0, get_brightness(m.data), 0]), + onmouseenter: move |m| q3_color.set([0, get_brightness(m.inner()), 0]), + onmousedown: move |m| q3_color.set([0, get_brightness(m.inner()), 0]), + onmouseup: move |m| q3_color.set([0, get_brightness(m.inner()), 0]), onwheel: move |w| q3_color.set([0, q3_color[1] + (10.0*w.delta().strip_units().y) as i32, 0]), onmouseleave: move |_| q3_color.set([200; 3]), onmousemove: update_data, @@ -110,9 +110,9 @@ fn app(cx: Scope) -> Element { justify_content: "center", align_items: "center", background_color: "{q4_color_str}", - onmouseenter: move |m| q4_color.set([0, 0, get_brightness(m.data)]), - onmousedown: move |m| q4_color.set([0, 0, get_brightness(m.data)]), - onmouseup: move |m| q4_color.set([0, 0, get_brightness(m.data)]), + onmouseenter: move |m| q4_color.set([0, 0, get_brightness(m.inner())]), + onmousedown: move |m| q4_color.set([0, 0, get_brightness(m.inner())]), + onmouseup: move |m| q4_color.set([0, 0, get_brightness(m.inner())]), onwheel: move |w| q4_color.set([0, 0, q4_color[2] + (10.0*w.delta().strip_units().y) as i32]), onmouseleave: move |_| q4_color.set([200; 3]), onmousemove: update_data, From 094012b07d3d45a9fbc61cff582bafecf1f02dab Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 14:04:13 -0600 Subject: [PATCH 30/43] update benchmark --- packages/tui/benches/update.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/tui/benches/update.rs b/packages/tui/benches/update.rs index 8db50a6a2..f122ce8f2 100644 --- a/packages/tui/benches/update.rs +++ b/packages/tui/benches/update.rs @@ -73,7 +73,7 @@ fn Grid(cx: Scope) -> Element { let count = use_state(&cx, || 0); let counts = use_ref(&cx, || vec![0; size * size]); - let ctx: TuiContext = cx.consume_context().unwrap(); + let ctx: &TuiContext = cx.consume_context().unwrap(); if *count.get() + 1 >= (size * size) { ctx.quit(); } else { @@ -88,14 +88,14 @@ fn Grid(cx: Scope) -> Element { }); } - cx.render(rsx! { + render! { div{ width: "100%", height: "100%", flex_direction: "column", (0..size).map(|x| { - cx.render(rsx! { + rsx! { div{ width: "100%", height: "100%", @@ -104,7 +104,7 @@ fn Grid(cx: Scope) -> Element { { let alpha = y as f32*100.0/size as f32 + counts.read()[x*size + y] as f32; let key = format!("{}-{}", x, y); - cx.render(rsx! { + rsx! { Box{ x: x, y: y, @@ -112,15 +112,15 @@ fn Grid(cx: Scope) -> Element { hue: alpha, key: "{key}", } - }) + } } ) } - }) + } } ) } - }) + } } fn app3(cx: Scope) -> Element { From e6a11f50dd21c926648e4eb7255065dd3c37efb5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 14:20:45 -0600 Subject: [PATCH 31/43] update more examples --- packages/core/src/diff.rs | 3 +-- packages/tui/examples/tui_buttons.rs | 22 +++++++++++----------- packages/tui/examples/tui_color_test.rs | 8 ++++---- packages/tui/examples/tui_margin.rs | 12 ++++++------ 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 5f2bccbb1..28ffb2404 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -167,7 +167,6 @@ impl<'b> VirtualDom { ) { // Replace components that have different render fns if left.render_fn != right.render_fn { - dbg!(&left, &right); let created = self.create_component_node(right_template, right, idx); let head = unsafe { self.scopes[left.scope.get().unwrap().0] @@ -407,7 +406,7 @@ impl<'b> VirtualDom { debug_assert!(!new.is_empty()); debug_assert!(!old.is_empty()); - match dbg!(old.len().cmp(&new.len())) { + match old.len().cmp(&new.len()) { Ordering::Greater => self.remove_nodes(&old[new.len()..]), Ordering::Less => self.create_and_insert_after(&new[old.len()..], old.last().unwrap()), Ordering::Equal => {} diff --git a/packages/tui/examples/tui_buttons.rs b/packages/tui/examples/tui_buttons.rs index 5b9199baa..bd9f3da13 100644 --- a/packages/tui/examples/tui_buttons.rs +++ b/packages/tui/examples/tui_buttons.rs @@ -23,12 +23,12 @@ fn Button(cx: Scope) -> Element { cx.render(rsx! { div{ - width: "100%", - height: "100%", + width: "20px", + height: "5px", background_color: "{color}", tabindex: "{cx.props.layer}", onkeydown: |e| { - if let Code::Space = e.data.code() { + if let Code::Space = e.inner().code() { toggle.modify(|f| !f); } }, @@ -60,7 +60,7 @@ fn app(cx: Scope) -> Element { height: "100%", (1..8).map(|y| - cx.render(rsx!{ + rsx!{ div{ display: "flex", flex_direction: "row", @@ -68,26 +68,26 @@ fn app(cx: Scope) -> Element { height: "100%", (1..8).map(|x|{ if (x + y) % 2 == 0{ - cx.render(rsx!{ + rsx!{ div{ - width: "100%", - height: "100%", + width: "20px", + height: "5px", background_color: "rgb(100, 100, 100)", } - }) + } } else{ let layer = (x + y) % 3; - cx.render(rsx!{ + rsx!{ Button{ color_offset: x * y, layer: layer as u16, } - }) + } } }) } - }) + } ) } }) diff --git a/packages/tui/examples/tui_color_test.rs b/packages/tui/examples/tui_color_test.rs index f3972331e..872fec53e 100644 --- a/packages/tui/examples/tui_color_test.rs +++ b/packages/tui/examples/tui_color_test.rs @@ -17,7 +17,7 @@ fn app(cx: Scope) -> Element { (0..=steps).map(|x| { let hue = x as f32*360.0/steps as f32; - cx.render(rsx! { + rsx! { div{ width: "100%", height: "100%", @@ -25,7 +25,7 @@ fn app(cx: Scope) -> Element { (0..=steps).map(|y| { let alpha = y as f32*100.0/steps as f32; - cx.render(rsx! { + rsx! { div { left: "{x}px", top: "{y}px", @@ -33,11 +33,11 @@ fn app(cx: Scope) -> Element { height: "100%", background_color: "hsl({hue}, 100%, 50%, {alpha}%)", } - }) + } } ) } - }) + } } ) } diff --git a/packages/tui/examples/tui_margin.rs b/packages/tui/examples/tui_margin.rs index d86bc116e..6e5634726 100644 --- a/packages/tui/examples/tui_margin.rs +++ b/packages/tui/examples/tui_margin.rs @@ -11,13 +11,13 @@ fn app(cx: Scope) -> Element { height: "100%", flex_direction: "column", background_color: "black", - // margin_right: "10px", + margin_right: "10px", div { width: "70%", height: "70%", background_color: "green", - // margin_left: "4px", + margin_left: "4px", div { width: "100%", @@ -34,10 +34,10 @@ fn app(cx: Scope) -> Element { align_items: "center", flex_direction: "column", - // padding_top: "2px", - // padding_bottom: "2px", - // padding_left: "4px", - // padding_right: "4px", + padding_top: "2px", + padding_bottom: "2px", + padding_left: "4px", + padding_right: "4px", "[A]" "[A]" From a823b45af94eb4362648e6cbc2ec538821d0c82d Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 20:18:10 -0600 Subject: [PATCH 32/43] fix root node layout --- packages/tui/examples/tui_buttons.rs | 8 ++++---- packages/tui/src/layout.rs | 7 ------- packages/tui/src/lib.rs | 22 ++++++++++++++++------ 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/tui/examples/tui_buttons.rs b/packages/tui/examples/tui_buttons.rs index bd9f3da13..852dd1d94 100644 --- a/packages/tui/examples/tui_buttons.rs +++ b/packages/tui/examples/tui_buttons.rs @@ -23,8 +23,8 @@ fn Button(cx: Scope) -> Element { cx.render(rsx! { div{ - width: "20px", - height: "5px", + width: "100%", + height: "100%", background_color: "{color}", tabindex: "{cx.props.layer}", onkeydown: |e| { @@ -70,8 +70,8 @@ fn app(cx: Scope) -> Element { if (x + y) % 2 == 0{ rsx!{ div{ - width: "20px", - height: "5px", + width: "100%", + height: "100%", background_color: "rgb(100, 100, 100)", } } diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index 54513268f..1e14f159c 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, Mutex}; -use dioxus_core::*; use dioxus_native_core::layout_attributes::apply_layout_attributes; use dioxus_native_core::node::OwnedAttributeView; use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView}; @@ -98,12 +97,6 @@ impl ChildDepState for TaffyLayout { } } - // the root node fills the entire area - if node.id() == Some(ElementId(0)) { - apply_layout_attributes("width", "100%", &mut style); - apply_layout_attributes("height", "100%", &mut style); - } - // Set all direct nodes as our children let mut child_layout = vec![]; for (l,) in children { diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 8b35ac54c..e3ac36ba6 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -152,14 +152,24 @@ fn render_vdom( let width = dims.width; let height = dims.height; let root_node = rdom[NodeId(0)].state.layout.node.unwrap(); - - taffy + + // the root node fills the entire area + + let mut style=*taffy.style(root_node).unwrap(); + style.size=Size { + width: Dimension::Points(width as f32), + height: Dimension::Points(height as f32), + }; + taffy.set_style(root_node, style).unwrap(); + + let size =Size { + width: AvailableSpace::Definite(width as f32), + height: AvailableSpace::Definite(height as f32), + }; + taffy .compute_layout( root_node, - Size { - width: AvailableSpace::Definite((width - 1) as f32), - height: AvailableSpace::Definite((height - 1) as f32), - }, + size ) .unwrap(); } From 57ef56511cd9a15e3aa865922cafb17001bb3deb Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 20:30:05 -0600 Subject: [PATCH 33/43] ignore out of bounds renders --- packages/tui/src/lib.rs | 4 ++-- packages/tui/src/render.rs | 15 +++++++++++---- packages/tui/src/widget.rs | 5 +++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index e3ac36ba6..cb000f00e 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -158,12 +158,12 @@ fn render_vdom( let mut style=*taffy.style(root_node).unwrap(); style.size=Size { width: Dimension::Points(width as f32), - height: Dimension::Points(height as f32), + height: Dimension::Points(height as f32), }; taffy.set_style(root_node, style).unwrap(); let size =Size { - width: AvailableSpace::Definite(width as f32), + width: AvailableSpace::Definite(width as f32), height: AvailableSpace::Definite(height as f32), }; taffy diff --git a/packages/tui/src/render.rs b/packages/tui/src/render.rs index 7beb543e2..d779418fa 100644 --- a/packages/tui/src/render.rs +++ b/packages/tui/src/render.rs @@ -36,8 +36,15 @@ pub(crate) fn render_vnode( location.x += parent_location.x; location.y += parent_location.y; - let Point { x, y } = location; - let Size { width, height } = size; + let Point { mut x, mut y } = location; + x = x.floor(); + y = y.floor(); + let Size { + mut width, + mut height, + } = size; + width = width.ceil(); + height = height.ceil(); match &node.node_data.node_type { NodeType::Text { text } => { @@ -62,7 +69,7 @@ pub(crate) fn render_vnode( text, style: node.state.style.core, }; - let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16); + let area = Rect::new(x as u16, y as u16, width as u16, height as u16); // the renderer will panic if a node is rendered out of range even if the size is zero if area.width > 0 && area.height > 0 { @@ -70,7 +77,7 @@ pub(crate) fn render_vnode( } } NodeType::Element { .. } => { - let area = Rect::new(x as u16, y as u16, *width as u16, *height as u16); + let area = Rect::new(x as u16, y as u16, width as u16, height as u16); // the renderer will panic if a node is rendered out of range even if the size is zero if area.width > 0 && area.height > 0 { diff --git a/packages/tui/src/widget.rs b/packages/tui/src/widget.rs index 416538462..7c6645692 100644 --- a/packages/tui/src/widget.rs +++ b/packages/tui/src/widget.rs @@ -22,8 +22,9 @@ impl<'a> RinkBuffer<'a> { pub fn set(&mut self, x: u16, y: u16, new: RinkCell) { let area = self.buf.area(); - if x < area.x || x > area.width || y < area.y || y > area.height { - panic!("({x}, {y}) is not in {area:?}"); + if x < area.x || x >= area.width + area.x || y < area.y || y >= area.height + area.y { + // panic!("({x}, {y}) is not in {area:?}"); + return; } let mut cell = self.buf.get_mut(x, y); cell.bg = convert(self.cfg.rendering_mode, new.bg.blend(cell.bg)); From f10d47b53d3360bf86f62e91ccae93ccd7532455 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 20:37:55 -0600 Subject: [PATCH 34/43] update color picker example --- packages/tui/examples/tui_colorpicker.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/tui/examples/tui_colorpicker.rs b/packages/tui/examples/tui_colorpicker.rs index 553a37aa4..3b1184930 100644 --- a/packages/tui/examples/tui_colorpicker.rs +++ b/packages/tui/examples/tui_colorpicker.rs @@ -1,5 +1,5 @@ -use dioxus::core_macro::rsx_without_templates; use dioxus::prelude::*; +use dioxus::core::RenderReturn; use dioxus_tui::query::Query; use dioxus_tui::Size; @@ -10,18 +10,20 @@ fn main() { fn app(cx: Scope) -> Element { let hue = use_state(&cx, || 0.0); let brightness = use_state(&cx, || 0.0); - let tui_query: Query = cx.consume_context().unwrap(); + let tui_query: &Query = cx.consume_context().unwrap(); // disable templates so that every node has an id and can be queried - cx.render(rsx_without_templates! { + cx.render(rsx! { div{ width: "100%", background_color: "hsl({hue}, 70%, {brightness}%)", onmousemove: move |evt| { - let node = tui_query.get(cx.root_node().mounted_id()); - let Size{width, height} = node.size().unwrap(); - let pos = evt.data.element_coordinates(); - hue.set((pos.x as f32/width as f32)*255.0); - brightness.set((pos.y as f32/height as f32)*100.0); + if let RenderReturn::Sync(Ok(node))=cx.root_node(){ + let node = tui_query.get(node.root_ids[0].get()); + let Size{width, height} = node.size().unwrap(); + let pos = evt.inner().element_coordinates(); + hue.set((pos.x as f32/width as f32)*255.0); + brightness.set((pos.y as f32/height as f32)*100.0); + } }, "hsl({hue}, 70%, {brightness}%)", } From b1db7a6400e44b365a1b373714f453bf102c88ca Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 20:43:11 -0600 Subject: [PATCH 35/43] update all events example --- packages/tui/examples/tui_all_events.rs | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/tui/examples/tui_all_events.rs b/packages/tui/examples/tui_all_events.rs index ffa03e039..6995cf39d 100644 --- a/packages/tui/examples/tui_all_events.rs +++ b/packages/tui/examples/tui_all_events.rs @@ -1,6 +1,6 @@ use dioxus::prelude::*; -use dioxus_html::on::{FocusData, KeyboardData, MouseData, WheelData}; -use std::sync::Arc; +use dioxus_html::{FocusData, KeyboardData, MouseData, WheelData}; +use std::rc::Rc; fn main() { dioxus_tui::launch(app); @@ -8,20 +8,20 @@ fn main() { #[derive(Debug)] enum Event { - MouseMove(Arc), - MouseClick(Arc), - MouseDoubleClick(Arc), - MouseDown(Arc), - MouseUp(Arc), + MouseMove(Rc), + MouseClick(Rc), + MouseDoubleClick(Rc), + MouseDown(Rc), + MouseUp(Rc), - Wheel(Arc), + Wheel(Rc), - KeyDown(Arc), - KeyUp(Arc), - KeyPress(Arc), + KeyDown(Rc), + KeyUp(Rc), + KeyPress(Rc), - FocusIn(Arc), - FocusOut(Arc), + FocusIn(Rc), + FocusOut(Rc), } const MAX_EVENTS: usize = 8; @@ -37,7 +37,7 @@ fn app(cx: Scope) -> Element { // todo: remove let mut trimmed = format!("{event:?}"); trimmed.truncate(200); - cx.render(rsx!(p { "{trimmed}" })) + rsx!(p { "{trimmed}" }) }); let log_event = move |event: Event| { @@ -57,20 +57,20 @@ fn app(cx: Scope) -> Element { align_items: "center", background_color: "hsl(248, 53%, 58%)", - onmousemove: move |event| log_event(Event::MouseMove(event.data)), - onclick: move |event| log_event(Event::MouseClick(event.data)), - ondblclick: move |event| log_event(Event::MouseDoubleClick(event.data)), - onmousedown: move |event| log_event(Event::MouseDown(event.data)), - onmouseup: move |event| log_event(Event::MouseUp(event.data)), + onmousemove: move |event| log_event(Event::MouseMove(event.inner().clone())), + onclick: move |event| log_event(Event::MouseClick(event.inner().clone())), + ondblclick: move |event| log_event(Event::MouseDoubleClick(event.inner().clone())), + onmousedown: move |event| log_event(Event::MouseDown(event.inner().clone())), + onmouseup: move |event| log_event(Event::MouseUp(event.inner().clone())), - onwheel: move |event| log_event(Event::Wheel(event.data)), + onwheel: move |event| log_event(Event::Wheel(event.inner().clone())), - onkeydown: move |event| log_event(Event::KeyDown(event.data)), - onkeyup: move |event| log_event(Event::KeyUp(event.data)), - onkeypress: move |event| log_event(Event::KeyPress(event.data)), + onkeydown: move |event| log_event(Event::KeyDown(event.inner().clone())), + onkeyup: move |event| log_event(Event::KeyUp(event.inner().clone())), + onkeypress: move |event| log_event(Event::KeyPress(event.inner().clone())), - onfocusin: move |event| log_event(Event::FocusIn(event.data)), - onfocusout: move |event| log_event(Event::FocusOut(event.data)), + onfocusin: move |event| log_event(Event::FocusIn(event.inner().clone())), + onfocusout: move |event| log_event(Event::FocusOut(event.inner().clone())), "Hover, click, type or scroll to see the info down below" }, From 317aed734365796b53577b4930a9bd76425ad2cf Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 20:45:08 -0600 Subject: [PATCH 36/43] update remaining examples --- packages/tui/examples/tui_text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tui/examples/tui_text.rs b/packages/tui/examples/tui_text.rs index 5268c59f6..dfbf6e4d2 100644 --- a/packages/tui/examples/tui_text.rs +++ b/packages/tui/examples/tui_text.rs @@ -12,7 +12,7 @@ fn app(cx: Scope) -> Element { width: "100%", height: "100%", flex_direction: "column", - onwheel: move |evt| alpha.set((**alpha + evt.data.delta().strip_units().y as i64).min(100).max(0)), + onwheel: move |evt| alpha.set((**alpha + evt.inner().delta().strip_units().y as i64).min(100).max(0)), p { background_color: "black", From ad33f2ce96f191babc16a45fc84eb6de84749084 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 5 Dec 2022 21:36:24 -0600 Subject: [PATCH 37/43] update tests --- packages/native-core-macro/src/lib.rs | 9 +- .../tests/called_minimally_on_build.rs | 265 +++--- .../native-core-macro/tests/change_nodes.rs | 79 -- .../native-core-macro/tests/initial_build.rs | 63 -- .../tests/persistent_iterator.rs | 585 ++++++------ .../native-core-macro/tests/update_state.rs | 892 ++++++++---------- packages/native-core/src/utils.rs | 21 +- 7 files changed, 833 insertions(+), 1081 deletions(-) delete mode 100644 packages/native-core-macro/tests/change_nodes.rs delete mode 100644 packages/native-core-macro/tests/initial_build.rs diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs index 6c0cdb2d0..dda96b512 100644 --- a/packages/native-core-macro/src/lib.rs +++ b/packages/native-core-macro/src/lib.rs @@ -189,7 +189,7 @@ impl Struct { let members = fields .iter() .enumerate() - .filter_map(|(i, f)| Member::parse(f, i as u64)) + .filter_map(|(i, f)| Member::parse(&name, f, i as u64)) .collect(); Self { name, members } } @@ -296,12 +296,15 @@ struct Member { } impl Member { - fn parse(field: &Field, id: u64) -> Option { + fn parse(parent: &Ident, field: &Field, id: u64) -> Option { Some(Self { id, ty: field.ty.clone(), unit_type: Ident::new( - ("_Unit".to_string() + field.ty.to_token_stream().to_string().as_str()).as_str(), + ("_Unit".to_string() + + parent.to_token_stream().to_string().as_str() + + field.ty.to_token_stream().to_string().as_str()) + .as_str(), Span::call_site(), ) .into(), diff --git a/packages/native-core-macro/tests/called_minimally_on_build.rs b/packages/native-core-macro/tests/called_minimally_on_build.rs index 978ad8f5f..84e4fb0e6 100644 --- a/packages/native-core-macro/tests/called_minimally_on_build.rs +++ b/packages/native-core-macro/tests/called_minimally_on_build.rs @@ -71,48 +71,33 @@ macro_rules! test_state{ fn state_reduce_initally_called_minimally() { #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { - render!(div { - div{ + render!{ + div { div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - }) - } - - let vdom = VirtualDom::new(Base); - - let mutations = vdom.create_vnodes(rsx! { - div { - div{ - div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" + div{ + p{} + } + p{ + "hello" + } + div{ + h1{} + } + p{ + "world" + } } } } - }); + } + + let mut vdom = VirtualDom::new(Base); + + let mutations = vdom.rebuild(); let mut dom: RealDom<$s> = RealDom::new(); - let nodes_updated = dom.apply_mutations(vec![mutations]); + let (nodes_updated, _) = dom.apply_mutations(mutations); let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); dom.traverse_depth_first(|n| { @@ -130,131 +115,131 @@ macro_rules! test_state{ } } -// mod node_depends_on_child_and_parent { -// use super::*; -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node(i32); -// dep!(node(Node, (Child, Parent))); +mod node_depends_on_child_and_parent { + use super::*; + #[derive(Debug, Clone, Default, PartialEq)] + struct Node(i32); + dep!(node(Node, (Child, Parent))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Child(i32); -// dep!(child(Child, (Child,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Child(i32); + dep!(child(Child, (Child,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Parent(i32); -// dep!(parent(Parent, (Parent,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Parent(i32); + dep!(parent(Parent, (Parent,))); -// #[derive(Debug, Clone, Default, State)] -// struct StateTester { -// #[node_dep_state((child, parent))] -// node: Node, -// #[child_dep_state(child)] -// child: Child, -// #[parent_dep_state(parent)] -// parent: Parent, -// } + #[derive(Debug, Clone, Default, State)] + struct StateTester { + #[node_dep_state((child, parent))] + node: Node, + #[child_dep_state(child)] + child: Child, + #[parent_dep_state(parent)] + parent: Parent, + } -// test_state!(StateTester, child: (child), node: (node), parent: (parent)); -// } + test_state!(StateTester, child: (child), node: (node), parent: (parent)); +} -// mod child_depends_on_node_that_depends_on_parent { -// use super::*; -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node(i32); -// dep!(node(Node, (Parent,))); +mod child_depends_on_node_that_depends_on_parent { + use super::*; + #[derive(Debug, Clone, Default, PartialEq)] + struct Node(i32); + dep!(node(Node, (Parent,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Child(i32); -// dep!(child(Child, (Node,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Child(i32); + dep!(child(Child, (Node,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Parent(i32); -// dep!(parent(Parent, (Parent,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Parent(i32); + dep!(parent(Parent, (Parent,))); -// #[derive(Debug, Clone, Default, State)] -// struct StateTester { -// #[node_dep_state(parent)] -// node: Node, -// #[child_dep_state(node)] -// child: Child, -// #[parent_dep_state(parent)] -// parent: Parent, -// } + #[derive(Debug, Clone, Default, State)] + struct StateTester { + #[node_dep_state(parent)] + node: Node, + #[child_dep_state(node)] + child: Child, + #[parent_dep_state(parent)] + parent: Parent, + } -// test_state!(StateTester, child: (child), node: (node), parent: (parent)); -// } + test_state!(StateTester, child: (child), node: (node), parent: (parent)); +} -// mod parent_depends_on_node_that_depends_on_child { -// use super::*; -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node(i32); -// dep!(node(Node, (Child,))); +mod parent_depends_on_node_that_depends_on_child { + use super::*; + #[derive(Debug, Clone, Default, PartialEq)] + struct Node(i32); + dep!(node(Node, (Child,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Child(i32); -// dep!(child(Child, (Child,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Child(i32); + dep!(child(Child, (Child,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Parent(i32); -// dep!(parent(Parent, (Node,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Parent(i32); + dep!(parent(Parent, (Node,))); -// #[derive(Debug, Clone, Default, State)] -// struct StateTester { -// #[node_dep_state(child)] -// node: Node, -// #[child_dep_state(child)] -// child: Child, -// #[parent_dep_state(node)] -// parent: Parent, -// } + #[derive(Debug, Clone, Default, State)] + struct StateTester { + #[node_dep_state(child)] + node: Node, + #[child_dep_state(child)] + child: Child, + #[parent_dep_state(node)] + parent: Parent, + } -// test_state!(StateTester, child: (child), node: (node), parent: (parent)); -// } + test_state!(StateTester, child: (child), node: (node), parent: (parent)); +} -// mod node_depends_on_other_node_state { -// use super::*; -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node1(i32); -// dep!(node(Node1, (Node2,))); +mod node_depends_on_other_node_state { + use super::*; + #[derive(Debug, Clone, Default, PartialEq)] + struct Node1(i32); + dep!(node(Node1, (Node2,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node2(i32); -// dep!(node(Node2, ())); + #[derive(Debug, Clone, Default, PartialEq)] + struct Node2(i32); + dep!(node(Node2, ())); -// #[derive(Debug, Clone, Default, State)] -// struct StateTester { -// #[node_dep_state((node2))] -// node1: Node1, -// #[node_dep_state()] -// node2: Node2, -// } + #[derive(Debug, Clone, Default, State)] + struct StateTester { + #[node_dep_state((node2))] + node1: Node1, + #[node_dep_state()] + node2: Node2, + } -// test_state!(StateTester, child: (), node: (node1, node2), parent: ()); -// } + test_state!(StateTester, child: (), node: (node1, node2), parent: ()); +} -// mod node_child_and_parent_state_depends_on_self { -// use super::*; -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Node(i32); -// dep!(node(Node, ())); +mod node_child_and_parent_state_depends_on_self { + use super::*; + #[derive(Debug, Clone, Default, PartialEq)] + struct Node(i32); + dep!(node(Node, ())); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Child(i32); -// dep!(child(Child, (Child,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Child(i32); + dep!(child(Child, (Child,))); -// #[derive(Debug, Clone, Default, PartialEq)] -// struct Parent(i32); -// dep!(parent(Parent, (Parent,))); + #[derive(Debug, Clone, Default, PartialEq)] + struct Parent(i32); + dep!(parent(Parent, (Parent,))); -// #[derive(Debug, Clone, Default, State)] -// struct StateTester { -// #[node_dep_state()] -// node: Node, -// #[child_dep_state(child)] -// child: Child, -// #[parent_dep_state(parent)] -// parent: Parent, -// } + #[derive(Debug, Clone, Default, State)] + struct StateTester { + #[node_dep_state()] + node: Node, + #[child_dep_state(child)] + child: Child, + #[parent_dep_state(parent)] + parent: Parent, + } -// test_state!(StateTester, child: (child), node: (node), parent: (parent)); -// } + test_state!(StateTester, child: (child), node: (node), parent: (parent)); +} diff --git a/packages/native-core-macro/tests/change_nodes.rs b/packages/native-core-macro/tests/change_nodes.rs deleted file mode 100644 index af3cbefb8..000000000 --- a/packages/native-core-macro/tests/change_nodes.rs +++ /dev/null @@ -1,79 +0,0 @@ -// use dioxus::core::ElementId; -// use dioxus::prelude::*; -// use dioxus_native_core::real_dom::RealDom; -// use dioxus_native_core::state::State; -// use dioxus_native_core::tree::*; -// use dioxus_native_core::NodeId; -// use dioxus_native_core_macro::State; - -// #[derive(State, Default, Clone)] -// struct Empty {} - -// #[test] -// fn remove_node() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } - -// let vdom = VirtualDom::new(Base); - -// let mut dom: RealDom = RealDom::new(); -// let (create, edit) = vdom.diff_lazynodes( -// rsx! { -// div{ -// div{} -// } -// }, -// rsx! { -// div{} -// }, -// ); - -// println!("create: {:#?}", create); -// println!("edit: {:#?}", edit); - -// let _to_update = dom.apply_mutations(vec![create]); - -// assert_eq!(dom.tree.height(NodeId(0)), Some(0)); -// assert_eq!(dom.tree.height(NodeId(1)), Some(1)); - -// dom.apply_mutations(vec![edit]); - -// assert_eq!(dom.size(), 3); -// assert_eq!(dom.tree.height(NodeId(0)), Some(0)); -// } - -// #[test] -// fn add_node() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } - -// let vdom = VirtualDom::new(Base); - -// let (create, update) = vdom.diff_lazynodes( -// rsx! { -// div{} -// }, -// rsx! { -// div{ -// p{} -// } -// }, -// ); - -// let mut dom: RealDom = RealDom::new(); - -// let _to_update = dom.apply_mutations(vec![create]); - -// assert_eq!(dom.size(), 2); -// assert_eq!(dom.tree.height(NodeId(2)), Some(1)); - -// dom.apply_mutations(vec![update]); - -// assert_eq!(dom.size(), 3); -// assert_eq!(dom.tree.height(NodeId(3)), Some(0)); -// assert_eq!(dom.tree.height(NodeId(0)), Some(1)); -// } diff --git a/packages/native-core-macro/tests/initial_build.rs b/packages/native-core-macro/tests/initial_build.rs deleted file mode 100644 index 86605aca9..000000000 --- a/packages/native-core-macro/tests/initial_build.rs +++ /dev/null @@ -1,63 +0,0 @@ -// use dioxus::core::ElementId; -// use dioxus::prelude::*; -// use dioxus_native_core::real_dom::RealDom; -// use dioxus_native_core::state::State; -// use dioxus_native_core::RealNodeId; -// use dioxus_native_core_macro::State; - -// #[derive(Default, Clone, State)] -// struct Empty {} - -// #[test] -// fn initial_build_simple() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } - -// let vdom = VirtualDom::new(Base); - -// let mutations = vdom.create_vnodes(rsx! { -// div{} -// }); - -// let mut dom: RealDom = RealDom::new(); - -// let _to_update = dom.apply_mutations(vec![mutations]); - -// assert_eq!(dom.size(), 2); -// assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); -// } - -// #[test] -// fn initial_build_with_children() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } - -// let vdom = VirtualDom::new(Base); - -// let mutations = vdom.create_vnodes(rsx! { -// div{ -// div{ -// "hello" -// p{ -// "world" -// } -// "hello world" -// } -// } -// }); - -// let mut dom: RealDom = RealDom::new(); - -// let _to_update = dom.apply_mutations(vec![mutations]); -// assert_eq!(dom.size(), 2); -// assert_eq!(dom[RealNodeId::ElementId(ElementId(2))].node_data.height, 1); -// assert_eq!(dom[RealNodeId::UnaccessableId(6)].node_data.height, 2); -// assert_eq!(dom[RealNodeId::UnaccessableId(5)].node_data.height, 3); -// assert_eq!(dom[RealNodeId::UnaccessableId(8)].node_data.height, 3); -// assert_eq!(dom[RealNodeId::UnaccessableId(10)].node_data.height, 3); -// assert_eq!(dom[RealNodeId::UnaccessableId(9)].node_data.height, 4); -// } diff --git a/packages/native-core-macro/tests/persistent_iterator.rs b/packages/native-core-macro/tests/persistent_iterator.rs index b1de9392e..c35c6ab0a 100644 --- a/packages/native-core-macro/tests/persistent_iterator.rs +++ b/packages/native-core-macro/tests/persistent_iterator.rs @@ -1,333 +1,298 @@ -// use dioxus::core_macro::rsx_without_templates; -// use dioxus::prelude::*; -// use dioxus_native_core::{ -// real_dom::{NodeType, RealDom}, -// state::State, -// utils::PersistantElementIter, -// }; -// use dioxus_native_core_macro::State; +use dioxus::prelude::*; +use dioxus_native_core::{ + real_dom::{ RealDom}, + node::NodeType, + state::State, + utils::PersistantElementIter, +}; +use dioxus_native_core_macro::State; -// #[derive(State, Default, Clone)] -// struct Empty {} +#[derive(State, Default, Clone, Debug)] +struct Empty {} -// #[test] -// #[allow(unused_variables)] -// fn traverse() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } -// let vdom = VirtualDom::new(Base); -// let mutations = vdom.create_vnodes(rsx! { -// div{ -// div{ -// "hello" -// p{ -// "world" -// } -// "hello world" -// } -// } -// }); +#[test] +#[allow(unused_variables)] +fn traverse() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + render!( + div{ + div{ + "hello" + p{ + "world" + } + "hello world" + } + } + ) + } -// let mut rdom: RealDom = RealDom::new(); + let mut vdom = VirtualDom::new(Base); + let mutations = vdom.rebuild(); -// let _to_update = rdom.apply_mutations(vec![mutations]); + let mut rdom: RealDom = RealDom::new(); -// let mut iter = PersistantElementIter::new(); -// let div_tag = "div".to_string(); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); -// let text1 = "hello".to_string(); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text1, .. } -// )); -// let p_tag = "p".to_string(); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: p_tag, .. } -// )); -// let text2 = "world".to_string(); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text2, .. } -// )); -// let text3 = "hello world".to_string(); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text3, .. } -// )); -// assert!(matches!( -// &rdom[iter.next(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); + let _to_update = rdom.apply_mutations(mutations); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text3, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text2, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: p_tag, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text1, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); -// assert!(matches!( -// &rdom[iter.prev(&rdom).id()].node_data.node_type, -// NodeType::Text { text: text3, .. } -// )); -// } + let mut iter = PersistantElementIter::new(); + let div_tag = "div".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + let text1 = "hello".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Text { text: text1, .. } + )); + let p_tag = "p".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + let text2 = "world".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Text { text: text2, .. } + )); + let text3 = "hello world".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Text { text: text3, .. } + )); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); -// #[test] -// #[allow(unused_variables)] -// fn persist_removes() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } -// let vdom = VirtualDom::new(Base); -// let (build, update) = vdom.diff_lazynodes( -// rsx_without_templates! { -// div{ -// p{ -// key: "1", -// "hello" -// } -// p{ -// key: "2", -// "world" -// } -// p{ -// key: "3", -// "hello world" -// } -// } -// }, -// rsx_without_templates! { -// div{ -// p{ -// key: "1", -// "hello" -// } -// p{ -// key: "3", -// "hello world" -// } -// } -// }, -// ); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text3, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text2, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text1, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text3, .. } + )); +} -// let mut rdom: RealDom = RealDom::new(); +#[test] +#[allow(unused_variables)] +fn persist_removes() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let children = match cx.generation()%2{ + 0=>3, + 1=>2, + _ => unreachable!() + }; + render!( + div{ + (0..children).map(|i|{ + rsx!{ + p{ + key: "{i}", + "{i}" + } + } + }) + } + ) + } + let mut vdom = VirtualDom::new(Base); -// let _to_update = rdom.apply_mutations(vec![build]); + let mut rdom: RealDom = RealDom::new(); -// // this will end on the node that is removed -// let mut iter1 = PersistantElementIter::new(); -// // this will end on the after node that is removed -// let mut iter2 = PersistantElementIter::new(); -// // div -// iter1.next(&rdom).id(); -// iter2.next(&rdom).id(); -// // p -// iter1.next(&rdom).id(); -// iter2.next(&rdom).id(); -// // "hello" -// iter1.next(&rdom).id(); -// iter2.next(&rdom).id(); -// // p -// iter1.next(&rdom).id(); -// iter2.next(&rdom).id(); -// // "world" -// iter1.next(&rdom).id(); -// iter2.next(&rdom).id(); -// // p -// iter2.next(&rdom).id(); -// // "hello world" -// iter2.next(&rdom).id(); + let build = vdom.rebuild(); + let _to_update = rdom.apply_mutations(build); -// iter1.prune(&update, &rdom); -// iter2.prune(&update, &rdom); -// let _to_update = rdom.apply_mutations(vec![update]); + // this will end on the node that is removed + let mut iter1 = PersistantElementIter::new(); + // this will end on the after node that is removed + let mut iter2 = PersistantElementIter::new(); + // div + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // p + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // "1" + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // p + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // "2" + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // p + iter2.next(&rdom).id(); + // "3" + iter2.next(&rdom).id(); -// let p_tag = "p".to_string(); -// let idx = iter1.next(&rdom).id(); -// assert!(matches!( -// &rdom[idx].node_data.node_type, -// NodeType::Element { tag: p_tag, .. } -// )); -// let text = "hello world".to_string(); -// let idx = iter1.next(&rdom).id(); -// assert!(matches!( -// &rdom[idx].node_data.node_type, -// NodeType::Text { text, .. } -// )); -// let div_tag = "div".to_string(); -// let idx = iter2.next(&rdom).id(); -// assert!(matches!( -// &rdom[idx].node_data.node_type, -// NodeType::Element { tag: div_tag, .. } -// )); -// } + let update = vdom.rebuild(); + iter1.prune(&update, &rdom); + iter2.prune(&update, &rdom); + let _to_update = rdom.apply_mutations(update); -// #[test] -// #[allow(unused_variables)] -// fn persist_instertions_before() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } -// let vdom = VirtualDom::new(Base); -// let (build, update) = vdom.diff_lazynodes( -// rsx_without_templates! { -// div{ -// p{ -// key: "1", -// "hello" -// } -// p{ -// key: "3", -// "hello world" -// } -// } -// }, -// rsx_without_templates! { -// div{ -// p{ -// key: "1", -// "hello" -// } -// p{ -// key: "2", -// "world" -// } -// p{ -// key: "3", -// "hello world" -// } -// } -// }, -// ); + let p_tag = "1".to_string(); + let idx = iter1.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + let text = "2".to_string(); + let idx = iter1.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Text { text, .. } + )); + let div_tag = "div".to_string(); + let idx = iter2.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); +} -// let mut rdom: RealDom = RealDom::new(); +#[test] +#[allow(unused_variables)] +fn persist_instertions_before() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let children = match cx.generation()%2{ + 0=>3, + 1=>2, + _ => unreachable!() + }; + render!( + div{ + (0..children).map(|i|{ + rsx!{ + p{ + key: "{i}", + "{i}" + } + } + }) + } + ) + } + let mut vdom = VirtualDom::new(Base); -// let _to_update = rdom.apply_mutations(vec![build]); + let mut rdom: RealDom = RealDom::new(); -// let mut iter = PersistantElementIter::new(); -// // div -// iter.next(&rdom).id(); -// // p -// iter.next(&rdom).id(); -// // "hello" -// iter.next(&rdom).id(); -// // p -// iter.next(&rdom).id(); -// // "hello world" -// iter.next(&rdom).id(); + let build = vdom.rebuild(); + let _to_update = rdom.apply_mutations(build); + + let mut iter = PersistantElementIter::new(); + // div + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "1" + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "2" + iter.next(&rdom).id(); + + let update = vdom.rebuild(); + iter.prune(&update, &rdom); + let _to_update = rdom.apply_mutations(update); -// iter.prune(&update, &rdom); -// let _to_update = rdom.apply_mutations(vec![update]); + let p_tag = "div".to_string(); + let idx = iter.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); +} -// let p_tag = "div".to_string(); -// let idx = iter.next(&rdom).id(); -// assert!(matches!( -// &rdom[idx].node_data.node_type, -// NodeType::Element { tag: p_tag, .. } -// )); -// } +#[test] +#[allow(unused_variables)] +fn persist_instertions_after() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let children = match cx.generation()%2{ + 0=>3, + 1=>2, + _ => unreachable!() + }; + render!( + div{ + (0..children).map(|i|{ + rsx!{ + p{ + key: "{i}", + "{i}" + } + } + }) + } + ) + } + let mut vdom = VirtualDom::new(Base); -// #[test] -// #[allow(unused_variables)] -// fn persist_instertions_after() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div {}) -// } -// let vdom = VirtualDom::new(Base); -// let (build, update) = vdom.diff_lazynodes( -// rsx_without_templates! { -// div{ -// p{ -// key: "1", -// "hello" -// } -// p{ -// key: "2", -// "world" -// } -// } -// }, -// rsx_without_templates! { -// div{ -// p{ -// key: "1", -// "hello" -// } -// p{ -// key: "2", -// "world" -// } -// p{ -// key: "3", -// "hello world" -// } -// } -// }, -// ); + let mut rdom: RealDom = RealDom::new(); -// let mut rdom: RealDom = RealDom::new(); + let build = vdom.rebuild(); + let _to_update = rdom.apply_mutations(build); -// let _to_update = rdom.apply_mutations(vec![build]); + let mut iter = PersistantElementIter::new(); + // div + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "hello" + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "world" + iter.next(&rdom).id(); -// let mut iter = PersistantElementIter::new(); -// // div -// iter.next(&rdom).id(); -// // p -// iter.next(&rdom).id(); -// // "hello" -// iter.next(&rdom).id(); -// // p -// iter.next(&rdom).id(); -// // "world" -// iter.next(&rdom).id(); + let update = vdom.rebuild(); + iter.prune(&update, &rdom); + let _to_update = rdom.apply_mutations(update); -// iter.prune(&update, &rdom); -// let _to_update = rdom.apply_mutations(vec![update]); - -// let p_tag = "p".to_string(); -// let idx = iter.next(&rdom).id(); -// assert!(matches!( -// &rdom[idx].node_data.node_type, -// NodeType::Element { tag: p_tag, .. } -// )); -// let text = "hello world".to_string(); -// let idx = iter.next(&rdom).id(); -// assert!(matches!( -// &rdom[idx].node_data.node_type, -// NodeType::Text { text, .. } -// )); -// } + let p_tag = "p".to_string(); + let idx = iter.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + let text = "hello world".to_string(); + let idx = iter.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Text { text, .. } + )); +} diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs index 2e6a04dec..456ea5f5b 100644 --- a/packages/native-core-macro/tests/update_state.rs +++ b/packages/native-core-macro/tests/update_state.rs @@ -1,516 +1,454 @@ -// use anymap::AnyMap; -// use dioxus::core::ElementId; -// use dioxus::core::{AttributeValue, DomEdit, Mutations}; -// use dioxus::core_macro::rsx_without_templates; -// use dioxus::prelude::*; -// use dioxus_native_core::real_dom::*; -// use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; -// use dioxus_native_core::{node_ref::*, RealNodeId}; -// use dioxus_native_core_macro::State; +use dioxus::core::ElementId; +use dioxus::core::{AttributeValue, Mutations}; +use dioxus::prelude::*; +use dioxus_native_core::real_dom::*; +use dioxus_native_core::state::{ChildDepState, NodeDepState, ParentDepState, State}; +use dioxus_native_core::tree::TreeView; +use dioxus_native_core::{node_ref::*, NodeId, RealNodeId, SendAnyMap}; +use dioxus_native_core_macro::State; -// #[derive(Debug, Clone, Default, State)] -// struct CallCounterStatePart1 { -// #[child_dep_state(child_counter)] -// child_counter: ChildDepCallCounter, -// } +#[derive(Debug, Clone, Default, State)] +struct CallCounterStatePart1 { + #[child_dep_state(child_counter)] + child_counter: ChildDepCallCounter, +} -// #[derive(Debug, Clone, Default, State)] -// struct CallCounterStatePart2 { -// #[parent_dep_state(parent_counter)] -// parent_counter: ParentDepCallCounter, -// } +#[derive(Debug, Clone, Default, State)] +struct CallCounterStatePart2 { + #[parent_dep_state(parent_counter)] + parent_counter: ParentDepCallCounter, +} -// #[derive(Debug, Clone, Default, State)] -// struct CallCounterStatePart3 { -// #[node_dep_state()] -// node_counter: NodeDepCallCounter, -// } +#[derive(Debug, Clone, Default, State)] +struct CallCounterStatePart3 { + #[node_dep_state()] + node_counter: NodeDepCallCounter, +} -// #[derive(Debug, Clone, Default, State)] -// struct CallCounterState { -// #[child_dep_state(child_counter)] -// child_counter: ChildDepCallCounter, -// #[state] -// part2: CallCounterStatePart2, -// #[parent_dep_state(parent_counter)] -// parent_counter: ParentDepCallCounter, -// #[state] -// part1: CallCounterStatePart1, -// #[state] -// part3: CallCounterStatePart3, -// #[node_dep_state()] -// node_counter: NodeDepCallCounter, -// } +#[derive(Debug, Clone, Default, State)] +struct CallCounterState { + #[child_dep_state(child_counter)] + child_counter: ChildDepCallCounter, + #[state] + part2: CallCounterStatePart2, + #[parent_dep_state(parent_counter)] + parent_counter: ParentDepCallCounter, + #[state] + part1: CallCounterStatePart1, + #[state] + part3: CallCounterStatePart3, + #[node_dep_state()] + node_counter: NodeDepCallCounter, +} -// #[derive(Debug, Clone, Default)] -// struct ChildDepCallCounter(u32); -// impl ChildDepState for ChildDepCallCounter { -// type Ctx = (); -// type DepState = Self; -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce<'a>( -// &mut self, -// _: NodeView, -// _: impl Iterator, -// _: &Self::Ctx, -// ) -> bool -// where -// Self::DepState: 'a, -// { -// self.0 += 1; -// true -// } -// } +#[derive(Debug, Clone, Default)] +struct ChildDepCallCounter(u32); +impl ChildDepState for ChildDepCallCounter { + type Ctx = (); + type DepState = (Self,); + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce<'a>( + &mut self, + _: NodeView, + _: impl Iterator, + _: &Self::Ctx, + ) -> bool + where + Self::DepState: 'a, + { + self.0 += 1; + true + } +} -// #[derive(Debug, Clone, Default)] -// struct ParentDepCallCounter(u32); -// impl ParentDepState for ParentDepCallCounter { -// type Ctx = (); -// type DepState = Self; -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce( -// &mut self, -// _node: NodeView, -// _parent: Option<&Self::DepState>, -// _ctx: &Self::Ctx, -// ) -> bool { -// self.0 += 1; -// true -// } -// } +#[derive(Debug, Clone, Default)] +struct ParentDepCallCounter(u32); +impl ParentDepState for ParentDepCallCounter { + type Ctx = (); + type DepState = (Self,); + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool { + self.0 += 1; + true + } +} -// #[derive(Debug, Clone, Default)] -// struct NodeDepCallCounter(u32); -// impl NodeDepState<()> for NodeDepCallCounter { -// type Ctx = (); -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { -// self.0 += 1; -// true -// } -// } +#[derive(Debug, Clone, Default)] +struct NodeDepCallCounter(u32); +impl NodeDepState for NodeDepCallCounter { + type Ctx = (); + type DepState = (); + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { + self.0 += 1; + true + } +} -// #[allow(clippy::vec_box)] -// #[derive(Debug, Clone, PartialEq, Default)] -// struct BubbledUpStateTester(Option, Vec>); -// impl ChildDepState for BubbledUpStateTester { -// type Ctx = u32; -// type DepState = Self; -// const NODE_MASK: NodeMask = NodeMask::new().with_tag(); -// fn reduce<'a>( -// &mut self, -// node: NodeView, -// children: impl Iterator, -// ctx: &Self::Ctx, -// ) -> bool -// where -// Self::DepState: 'a, -// { -// assert_eq!(*ctx, 42); -// *self = BubbledUpStateTester( -// node.tag().map(|s| s.to_string()), -// children.into_iter().map(|c| Box::new(c.clone())).collect(), -// ); -// true -// } -// } +#[allow(clippy::vec_box)] +#[derive(Debug, Clone, PartialEq, Default)] +struct BubbledUpStateTester(Option, Vec>); +impl ChildDepState for BubbledUpStateTester { + type Ctx = u32; + type DepState = (Self,); + const NODE_MASK: NodeMask = NodeMask::new().with_tag(); + fn reduce<'a>( + &mut self, + node: NodeView, + children: impl Iterator, + ctx: &Self::Ctx, + ) -> bool + where + Self::DepState: 'a, + { + assert_eq!(*ctx, 42); + *self = BubbledUpStateTester( + node.tag().map(|s| s.to_string()), + children + .into_iter() + .map(|(c,)| Box::new(c.clone())) + .collect(), + ); + true + } +} -// #[derive(Debug, Clone, PartialEq, Default)] -// struct PushedDownStateTester(Option, Option>); -// impl ParentDepState for PushedDownStateTester { -// type Ctx = u32; -// type DepState = Self; -// const NODE_MASK: NodeMask = NodeMask::new().with_tag(); -// fn reduce(&mut self, node: NodeView, parent: Option<&Self::DepState>, ctx: &Self::Ctx) -> bool { -// assert_eq!(*ctx, 42); -// *self = PushedDownStateTester( -// node.tag().map(|s| s.to_string()), -// parent.map(|c| Box::new(c.clone())), -// ); -// true -// } -// } +#[derive(Debug, Clone, PartialEq, Default)] +struct PushedDownStateTester(Option, Option>); +impl ParentDepState for PushedDownStateTester { + type Ctx = u32; + type DepState = (Self,); + const NODE_MASK: NodeMask = NodeMask::new().with_tag(); + fn reduce(&mut self, node: NodeView, parent: Option<(&Self,)>, ctx: &Self::Ctx) -> bool { + assert_eq!(*ctx, 42); + *self = PushedDownStateTester( + node.tag().map(|s| s.to_string()), + parent.map(|(c,)| Box::new(c.clone())), + ); + true + } +} -// #[derive(Debug, Clone, PartialEq, Default)] -// struct NodeStateTester(Option, Vec<(String, String)>); -// impl NodeDepState<()> for NodeStateTester { -// type Ctx = u32; -// const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag(); -// fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool { -// assert_eq!(*ctx, 42); -// *self = NodeStateTester( -// node.tag().map(|s| s.to_string()), -// node.attributes() -// .map(|iter| { -// iter.map(|a| { -// ( -// a.attribute.name.to_string(), -// a.value.as_text().unwrap().to_string(), -// ) -// }) -// .collect() -// }) -// .unwrap_or_default(), -// ); -// true -// } -// } +#[derive(Debug, Clone, PartialEq, Default)] +struct NodeStateTester(Option, Vec<(String, String)>); +impl NodeDepState for NodeStateTester { + type Ctx = u32; + type DepState = (); + const NODE_MASK: NodeMask = NodeMask::new_with_attrs(AttributeMask::All).with_tag(); + fn reduce(&mut self, node: NodeView, _sibling: (), ctx: &Self::Ctx) -> bool { + assert_eq!(*ctx, 42); + *self = NodeStateTester( + node.tag().map(|s| s.to_string()), + node.attributes() + .map(|iter| { + iter.map(|a| { + ( + a.attribute.name.to_string(), + a.value.as_text().unwrap().to_string(), + ) + }) + .collect() + }) + .unwrap_or_default(), + ); + true + } +} -// #[derive(State, Clone, Default, Debug)] -// struct StateTester { -// #[child_dep_state(bubbled, u32)] -// bubbled: BubbledUpStateTester, -// #[parent_dep_state(pushed, u32)] -// pushed: PushedDownStateTester, -// #[node_dep_state(NONE, u32)] -// node: NodeStateTester, -// } +#[derive(State, Clone, Default, Debug)] +struct StateTester { + #[child_dep_state(bubbled, u32)] + bubbled: BubbledUpStateTester, + #[parent_dep_state(pushed, u32)] + pushed: PushedDownStateTester, + #[node_dep_state(NONE, u32)] + node: NodeStateTester, +} -// #[test] -// fn state_initial() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div { -// p{} -// h1{} -// }) -// } +#[test] +fn state_initial() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + render! { + div{ + p{} + h1{} + div { + p{ + color: "red" + } + h1{} + } + } + } + } -// let vdom = VirtualDom::new(Base); + let mut vdom = VirtualDom::new(Base); -// let mutations = vdom.create_vnodes(rsx! { -// div { -// p{ -// color: "red" -// } -// h1{} -// } -// }); + let mutations = vdom.rebuild(); -// let mut dom: RealDom = RealDom::new(); + let mut dom: RealDom = RealDom::new(); -// let nodes_updated = dom.apply_mutations(vec![mutations]); -// let mut ctx = AnyMap::new(); -// ctx.insert(42u32); -// let _to_rerender = dom.update_state(nodes_updated, ctx); + let (nodes_updated, _) = dom.apply_mutations(mutations); + let mut ctx = SendAnyMap::new(); + ctx.insert(42u32); + let _to_rerender = dom.update_state(nodes_updated, ctx); -// let root_div = &dom[RealNodeId::ElementId(ElementId(2))]; -// assert_eq!(root_div.state.bubbled.0, Some("div".to_string())); -// assert_eq!( -// root_div.state.bubbled.1, -// vec![ -// Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())), -// Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new())) -// ] -// ); -// assert_eq!(root_div.state.pushed.0, Some("div".to_string())); -// assert_eq!( -// root_div.state.pushed.1, -// Some(Box::new(PushedDownStateTester( -// Some("Root".to_string()), -// None -// ))) -// ); -// assert_eq!(root_div.state.node.0, Some("div".to_string())); -// assert_eq!(root_div.state.node.1, vec![]); + let root_div_id = dom.children_ids(NodeId(0)).unwrap()[0]; + let root_div = &dom.get(root_div_id).unwrap(); + assert_eq!(root_div.state.bubbled.0, Some("div".to_string())); + assert_eq!( + root_div.state.bubbled.1, + vec![ + Box::new(BubbledUpStateTester(Some("p".to_string()), Vec::new())), + Box::new(BubbledUpStateTester(Some("h1".to_string()), Vec::new())) + ] + ); + assert_eq!(root_div.state.pushed.0, Some("div".to_string())); + assert_eq!( + root_div.state.pushed.1, + Some(Box::new(PushedDownStateTester( + Some("Root".to_string()), + None + ))) + ); + assert_eq!(root_div.state.node.0, Some("div".to_string())); + assert_eq!(root_div.state.node.1, vec![]); -// let child_p = &dom[RealNodeId::UnaccessableId(3)]; -// assert_eq!(child_p.state.bubbled.0, Some("p".to_string())); -// assert_eq!(child_p.state.bubbled.1, Vec::new()); -// assert_eq!(child_p.state.pushed.0, Some("p".to_string())); -// assert_eq!( -// child_p.state.pushed.1, -// Some(Box::new(PushedDownStateTester( -// Some("div".to_string()), -// Some(Box::new(PushedDownStateTester( -// Some("Root".to_string()), -// None -// ))) -// ))) -// ); -// assert_eq!(child_p.state.node.0, Some("p".to_string())); -// assert_eq!( -// child_p.state.node.1, -// vec![("color".to_string(), "red".to_string())] -// ); + let child_p_id = dom.children_ids(root_div_id).unwrap()[0]; + let child_p = &dom[child_p_id]; + assert_eq!(child_p.state.bubbled.0, Some("p".to_string())); + assert_eq!(child_p.state.bubbled.1, Vec::new()); + assert_eq!(child_p.state.pushed.0, Some("p".to_string())); + assert_eq!( + child_p.state.pushed.1, + Some(Box::new(PushedDownStateTester( + Some("div".to_string()), + Some(Box::new(PushedDownStateTester( + Some("Root".to_string()), + None + ))) + ))) + ); + assert_eq!(child_p.state.node.0, Some("p".to_string())); + assert_eq!( + child_p.state.node.1, + vec![("color".to_string(), "red".to_string())] + ); -// let child_h1 = &dom[RealNodeId::UnaccessableId(4)]; -// assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string())); -// assert_eq!(child_h1.state.bubbled.1, Vec::new()); -// assert_eq!(child_h1.state.pushed.0, Some("h1".to_string())); -// assert_eq!( -// child_h1.state.pushed.1, -// Some(Box::new(PushedDownStateTester( -// Some("div".to_string()), -// Some(Box::new(PushedDownStateTester( -// Some("Root".to_string()), -// None -// ))) -// ))) -// ); -// assert_eq!(child_h1.state.node.0, Some("h1".to_string())); -// assert_eq!(child_h1.state.node.1, vec![]); -// } + let child_h1_id = dom.children_ids(root_div_id).unwrap()[1]; + let child_h1 = &dom[child_h1_id]; + assert_eq!(child_h1.state.bubbled.0, Some("h1".to_string())); + assert_eq!(child_h1.state.bubbled.1, Vec::new()); + assert_eq!(child_h1.state.pushed.0, Some("h1".to_string())); + assert_eq!( + child_h1.state.pushed.1, + Some(Box::new(PushedDownStateTester( + Some("div".to_string()), + Some(Box::new(PushedDownStateTester( + Some("Root".to_string()), + None + ))) + ))) + ); + assert_eq!(child_h1.state.node.0, Some("h1".to_string())); + assert_eq!(child_h1.state.node.1, vec![]); +} -// #[test] -// fn state_reduce_parent_called_minimally_on_update() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div { -// width: "100%", -// div{ -// div{ -// p{} -// } -// p{ -// "hello" -// } -// div{ -// h1{} -// } -// p{ -// "world" -// } -// } -// }) -// } +#[test] +fn state_reduce_parent_called_minimally_on_update() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let width = if cx.generation() == 0 { "100%" } else { "99%" }; + cx.render(rsx! { + div { + div{ + div{ + p{ + width: "{width}", + } + } + p{ + "hello" + } + div{ + h1{} + } + p{ + "world" + } + } + } + }) + } -// let vdom = VirtualDom::new(Base); + let mut vdom = VirtualDom::new(Base); -// let mutations = vdom.create_vnodes(rsx_without_templates! { -// div { -// width: "100%", -// div{ -// div{ -// p{} -// } -// p{ -// "hello" -// } -// div{ -// h1{} -// } -// p{ -// "world" -// } -// } -// } -// }); + let mut dom: RealDom = RealDom::new(); -// let mut dom: RealDom = RealDom::new(); + let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); + let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); + let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); + let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); -// let nodes_updated = dom.apply_mutations(vec![mutations]); -// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); -// let nodes_updated = dom.apply_mutations(vec![Mutations { -// edits: vec![DomEdit::SetAttribute { -// root: Some(1), -// field: "width", -// value: AttributeValue::Text("99%"), -// ns: Some("style"), -// }], -// dirty_scopes: rustc_hash::FxHashSet::default(), -// refs: Vec::new(), -// }]); -// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); + dom.traverse_depth_first(|n| { + assert_eq!(n.state.part2.parent_counter.0, 2); + assert_eq!(n.state.parent_counter.0, 2); + }); +} -// dom.traverse_depth_first(|n| { -// assert_eq!(n.state.part2.parent_counter.0, 2); -// assert_eq!(n.state.parent_counter.0, 2); -// }); -// } +#[test] +fn state_reduce_child_called_minimally_on_update() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let width = if cx.generation() == 0 { "100%" } else { "99%" }; + cx.render(rsx! { + div { + div{ + div{ + p{ + width: "{width}", + } + } + p{ + "hello" + } + div{ + h1{} + } + p{ + "world" + } + } + } + }) + } -// #[test] -// fn state_reduce_child_called_minimally_on_update() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// cx.render(rsx_without_templates!(div { -// div{ -// div{ -// p{ -// width: "100%", -// } -// } -// p{ -// "hello" -// } -// div{ -// h1{} -// } -// p{ -// "world" -// } -// } -// })) -// } + let mut vdom = VirtualDom::new(Base); -// let vdom = VirtualDom::new(Base); + let mut dom: RealDom = RealDom::new(); -// let mutations = vdom.create_vnodes(rsx_without_templates! { -// div { -// div{ -// div{ -// p{ -// width: "100%", -// } -// } -// p{ -// "hello" -// } -// div{ -// h1{} -// } -// p{ -// "world" -// } -// } -// } -// }); + let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); + let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); + let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); + let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); -// let mut dom: RealDom = RealDom::new(); + dom.traverse_depth_first(|n| { + assert_eq!(n.state.part1.child_counter.0, { + let id = n.node_data.node_id.0; + if id > 4 { + 1 + } else { + 2 + } + }); + assert_eq!(n.state.child_counter.0, { + let id = n.node_data.node_id.0; + if id > 4 { + 1 + } else { + 2 + } + }); + }); +} -// let nodes_updated = dom.apply_mutations(vec![mutations]); -// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); -// let nodes_updated = dom.apply_mutations(vec![Mutations { -// edits: vec![DomEdit::SetAttribute { -// root: Some(4), -// field: "width", -// value: AttributeValue::Text("99%"), -// ns: Some("style"), -// }], -// dirty_scopes: rustc_hash::FxHashSet::default(), -// refs: Vec::new(), -// }]); -// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); +#[derive(Debug, Clone, Default, State)] +struct UnorderedDependanciesState { + #[node_dep_state(c)] + b: BDepCallCounter, + #[node_dep_state()] + c: CDepCallCounter, + #[node_dep_state(b)] + a: ADepCallCounter, +} -// dom.traverse_depth_first(|n| { -// assert_eq!( -// n.state.part1.child_counter.0, -// if let Some(RealNodeId::ElementId(ElementId(id))) = n.node_data.id { -// if id > 4 { -// 1 -// } else { -// 2 -// } -// } else { -// panic!() -// } -// ); -// assert_eq!( -// n.state.child_counter.0, -// if let Some(RealNodeId::ElementId(ElementId(id))) = n.node_data.id { -// if id > 4 { -// 1 -// } else { -// 2 -// } -// } else { -// panic!() -// } -// ); -// }); -// } +#[derive(Debug, Clone, Default, PartialEq)] +struct ADepCallCounter(usize, BDepCallCounter); +impl NodeDepState for ADepCallCounter { + type Ctx = (); + type DepState = (BDepCallCounter,); + const NODE_MASK: NodeMask = NodeMask::NONE; + fn reduce( + &mut self, + _node: NodeView, + (sibling,): (&BDepCallCounter,), + _ctx: &Self::Ctx, + ) -> bool { + self.0 += 1; + self.1 = sibling.clone(); + true + } +} -// #[derive(Debug, Clone, Default, State)] -// struct UnorderedDependanciesState { -// #[node_dep_state(c)] -// b: BDepCallCounter, -// #[node_dep_state()] -// c: CDepCallCounter, -// #[node_dep_state(b)] -// a: ADepCallCounter, -// } +#[derive(Debug, Clone, Default, PartialEq)] +struct BDepCallCounter(usize, CDepCallCounter); +impl NodeDepState for BDepCallCounter { + type Ctx = (); + type DepState = (CDepCallCounter,); + const NODE_MASK: NodeMask = NodeMask::NONE; + fn reduce( + &mut self, + _node: NodeView, + (sibling,): (&CDepCallCounter,), + _ctx: &Self::Ctx, + ) -> bool { + self.0 += 1; + self.1 = sibling.clone(); + true + } +} -// #[derive(Debug, Clone, Default, PartialEq)] -// struct ADepCallCounter(usize, BDepCallCounter); -// impl<'a> NodeDepState<(&'a BDepCallCounter,)> for ADepCallCounter { -// type Ctx = (); -// const NODE_MASK: NodeMask = NodeMask::NONE; -// fn reduce( -// &mut self, -// _node: NodeView, -// (sibling,): (&'a BDepCallCounter,), -// _ctx: &Self::Ctx, -// ) -> bool { -// self.0 += 1; -// self.1 = sibling.clone(); -// true -// } -// } +#[derive(Debug, Clone, Default, PartialEq)] +struct CDepCallCounter(usize); +impl NodeDepState for CDepCallCounter { + type Ctx = (); + type DepState = (); + const NODE_MASK: NodeMask = NodeMask::ALL; + fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { + self.0 += 1; + true + } +} -// #[derive(Debug, Clone, Default, PartialEq)] -// struct BDepCallCounter(usize, CDepCallCounter); -// impl<'a> NodeDepState<(&'a CDepCallCounter,)> for BDepCallCounter { -// type Ctx = (); -// const NODE_MASK: NodeMask = NodeMask::NONE; -// fn reduce( -// &mut self, -// _node: NodeView, -// (sibling,): (&'a CDepCallCounter,), -// _ctx: &Self::Ctx, -// ) -> bool { -// self.0 += 1; -// self.1 = sibling.clone(); -// true -// } -// } +#[test] +fn dependancies_order_independant() { + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + render!(div { + width: "100%", + p{ + "hello" + } + }) + } -// #[derive(Debug, Clone, Default, PartialEq)] -// struct CDepCallCounter(usize); -// impl NodeDepState<()> for CDepCallCounter { -// type Ctx = (); -// const NODE_MASK: NodeMask = NodeMask::ALL; -// fn reduce(&mut self, _node: NodeView, _sibling: (), _ctx: &Self::Ctx) -> bool { -// self.0 += 1; -// true -// } -// } + let mut vdom = VirtualDom::new(Base); -// #[test] -// fn dependancies_order_independant() { -// #[allow(non_snake_case)] -// fn Base(cx: Scope) -> Element { -// render!(div { -// width: "100%", -// p{ -// "hello" -// } -// }) -// } + let mut dom: RealDom = RealDom::new(); -// let vdom = VirtualDom::new(Base); + let mutations = vdom.rebuild(); + let (nodes_updated, _) = dom.apply_mutations(mutations); + let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); -// let mutations = vdom.create_vnodes(rsx! { -// div { -// width: "100%", -// p{ -// "hello" -// } -// } -// }); + let c = CDepCallCounter(1); + let b = BDepCallCounter(1, c.clone()); + let a = ADepCallCounter(1, b.clone()); + dom.traverse_depth_first(|n| { + assert_eq!(&n.state.a, &a); + assert_eq!(&n.state.b, &b); + assert_eq!(&n.state.c, &c); + }); +} -// let mut dom: RealDom = RealDom::new(); - -// let nodes_updated = dom.apply_mutations(vec![mutations]); -// let _to_rerender = dom.update_state(nodes_updated, AnyMap::new()); - -// let c = CDepCallCounter(1); -// let b = BDepCallCounter(1, c.clone()); -// let a = ADepCallCounter(1, b.clone()); -// dom.traverse_depth_first(|n| { -// assert_eq!(&n.state.a, &a); -// assert_eq!(&n.state.b, &b); -// assert_eq!(&n.state.c, &c); -// }); -// } - -// #[derive(Clone, Default, State)] -// struct DependanciesStateTest { -// #[node_dep_state(c)] -// b: BDepCallCounter, -// #[node_dep_state()] -// c: CDepCallCounter, -// #[node_dep_state(b)] -// a: ADepCallCounter, -// #[state] -// child: UnorderedDependanciesState, -// } +#[derive(Clone, Default, State)] +struct DependanciesStateTest { + #[node_dep_state(c)] + b: BDepCallCounter, + #[node_dep_state()] + c: CDepCallCounter, + #[node_dep_state(b)] + a: ADepCallCounter, + #[state] + child: UnorderedDependanciesState, +} diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index bec658624..ac6d1f6d3 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -2,6 +2,7 @@ use crate::{node::NodeType, real_dom::RealDom, state::State, tree::TreeView, Nod use dioxus_core::{Mutation, Mutations}; use std::fmt::Debug; +#[derive(Debug)] pub enum ElementProduced { /// The iterator produced an element by progressing to the next node in a depth first order. Progressed(RealNodeId), @@ -124,20 +125,20 @@ impl PersistantElementIter { /// get the next element pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { - if self.stack.is_empty() { + let r=if self.stack.is_empty() { let id = NodeId(0); let new = (id, NodePosition::AtNode); self.stack.push(new); ElementProduced::Looped(id) } else { - let (last, o_child_idx) = self.stack.last_mut().unwrap(); + let (last, old_child_idx) = self.stack.last_mut().unwrap(); let node = &rdom[*last]; match &node.node_data.node_type { NodeType::Element { .. } => { let children = rdom.tree.children_ids(*last).unwrap(); - *o_child_idx = o_child_idx.map(|i| i + 1); + *old_child_idx = old_child_idx.map(|i| i + 1); // if we have children, go to the next child - let child_idx = o_child_idx.get_or_insert(0); + let child_idx = old_child_idx.get_or_insert(0); if child_idx >= children.len() { self.pop(); self.next(rdom) @@ -155,7 +156,9 @@ impl PersistantElementIter { ElementProduced::Progressed(self.pop()) } } - } + }; + println!("next: {:?}", r); + r } /// get the previous element @@ -183,17 +186,17 @@ impl PersistantElementIter { let new_node = NodeId(0); ElementProduced::Looped(push_back(&mut self.stack, new_node, rdom)) } else { - let (last, o_child_idx) = self.stack.last_mut().unwrap(); + let (last, old_child_idx) = self.stack.last_mut().unwrap(); let node = &rdom[*last]; match &node.node_data.node_type { NodeType::Element { .. } => { let children = rdom.tree.children_ids(*last).unwrap(); // if we have children, go to the next child - if let NodePosition::InChild(0) = o_child_idx { + if let NodePosition::InChild(0) = old_child_idx { ElementProduced::Progressed(self.pop()) } else { - *o_child_idx = o_child_idx.map(|i| i - 1); - if let NodePosition::InChild(child_idx) = o_child_idx { + *old_child_idx = old_child_idx.map(|i| i - 1); + if let NodePosition::InChild(child_idx) = old_child_idx { if *child_idx >= children.len() || children.is_empty() { self.pop(); self.prev(rdom) From 32e93dee655e3daf8892c8f2364a82f0965426f7 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 6 Dec 2022 08:27:32 -0600 Subject: [PATCH 38/43] tests passing --- .../tests/persistent_iterator.rs | 39 ++++---- .../native-core-macro/tests/update_state.rs | 92 +++++++------------ 2 files changed, 50 insertions(+), 81 deletions(-) diff --git a/packages/native-core-macro/tests/persistent_iterator.rs b/packages/native-core-macro/tests/persistent_iterator.rs index c35c6ab0a..0ad2e3ca2 100644 --- a/packages/native-core-macro/tests/persistent_iterator.rs +++ b/packages/native-core-macro/tests/persistent_iterator.rs @@ -1,9 +1,6 @@ use dioxus::prelude::*; use dioxus_native_core::{ - real_dom::{ RealDom}, - node::NodeType, - state::State, - utils::PersistantElementIter, + node::NodeType, real_dom::RealDom, state::State, utils::PersistantElementIter, }; use dioxus_native_core_macro::State; @@ -109,10 +106,10 @@ fn traverse() { fn persist_removes() { #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { - let children = match cx.generation()%2{ - 0=>3, - 1=>2, - _ => unreachable!() + let children = match cx.generation() % 2 { + 0 => 3, + 1 => 2, + _ => unreachable!(), }; render!( div{ @@ -158,7 +155,8 @@ fn persist_removes() { // "3" iter2.next(&rdom).id(); - let update = vdom.rebuild(); + vdom.mark_dirty(ScopeId(0)); + let update = vdom.render_immediate(); iter1.prune(&update, &rdom); iter2.prune(&update, &rdom); let _to_update = rdom.apply_mutations(update); @@ -188,10 +186,10 @@ fn persist_removes() { fn persist_instertions_before() { #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { - let children = match cx.generation()%2{ - 0=>3, - 1=>2, - _ => unreachable!() + let children = match cx.generation() % 2 { + 0 => 3, + 1 => 2, + _ => unreachable!(), }; render!( div{ @@ -212,7 +210,7 @@ fn persist_instertions_before() { let build = vdom.rebuild(); let _to_update = rdom.apply_mutations(build); - + let mut iter = PersistantElementIter::new(); // div iter.next(&rdom).id(); @@ -224,8 +222,9 @@ fn persist_instertions_before() { iter.next(&rdom).id(); // "2" iter.next(&rdom).id(); - - let update = vdom.rebuild(); + + vdom.mark_dirty(ScopeId(0)); + let update = vdom.render_immediate(); iter.prune(&update, &rdom); let _to_update = rdom.apply_mutations(update); @@ -242,10 +241,10 @@ fn persist_instertions_before() { fn persist_instertions_after() { #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { - let children = match cx.generation()%2{ - 0=>3, - 1=>2, - _ => unreachable!() + let children = match cx.generation() % 2 { + 0 => 3, + 1 => 2, + _ => unreachable!(), }; render!( div{ diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs index 456ea5f5b..b9b36c8ca 100644 --- a/packages/native-core-macro/tests/update_state.rs +++ b/packages/native-core-macro/tests/update_state.rs @@ -7,36 +7,12 @@ use dioxus_native_core::tree::TreeView; use dioxus_native_core::{node_ref::*, NodeId, RealNodeId, SendAnyMap}; use dioxus_native_core_macro::State; -#[derive(Debug, Clone, Default, State)] -struct CallCounterStatePart1 { - #[child_dep_state(child_counter)] - child_counter: ChildDepCallCounter, -} - -#[derive(Debug, Clone, Default, State)] -struct CallCounterStatePart2 { - #[parent_dep_state(parent_counter)] - parent_counter: ParentDepCallCounter, -} - -#[derive(Debug, Clone, Default, State)] -struct CallCounterStatePart3 { - #[node_dep_state()] - node_counter: NodeDepCallCounter, -} - #[derive(Debug, Clone, Default, State)] struct CallCounterState { #[child_dep_state(child_counter)] child_counter: ChildDepCallCounter, - #[state] - part2: CallCounterStatePart2, #[parent_dep_state(parent_counter)] parent_counter: ParentDepCallCounter, - #[state] - part1: CallCounterStatePart1, - #[state] - part3: CallCounterStatePart3, #[node_dep_state()] node_counter: NodeDepCallCounter, } @@ -69,6 +45,7 @@ impl ParentDepState for ParentDepCallCounter { const NODE_MASK: NodeMask = NodeMask::ALL; fn reduce(&mut self, _node: NodeView, _parent: Option<(&Self,)>, _ctx: &Self::Ctx) -> bool { self.0 += 1; + println!("ParentDepCallCounter::reduce on {:?}\n{}", _node, self.0); true } } @@ -170,15 +147,11 @@ fn state_initial() { #[allow(non_snake_case)] fn Base(cx: Scope) -> Element { render! { - div{ - p{} - h1{} - div { - p{ - color: "red" - } - h1{} + div { + p{ + color: "red" } + h1{} } } } @@ -262,11 +235,10 @@ fn state_reduce_parent_called_minimally_on_update() { let width = if cx.generation() == 0 { "100%" } else { "99%" }; cx.render(rsx! { div { + width: "{width}", div{ div{ - p{ - width: "{width}", - } + p{} } p{ "hello" @@ -288,12 +260,18 @@ fn state_reduce_parent_called_minimally_on_update() { let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); - let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); + vdom.mark_dirty(ScopeId(0)); + let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate()); let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); + let mut is_root = true; dom.traverse_depth_first(|n| { - assert_eq!(n.state.part2.parent_counter.0, 2); - assert_eq!(n.state.parent_counter.0, 2); + if is_root { + is_root = false; + assert_eq!(n.state.parent_counter.0, 1); + } else { + assert_eq!(n.state.parent_counter.0, 2); + } }); } @@ -303,20 +281,30 @@ fn state_reduce_child_called_minimally_on_update() { fn Base(cx: Scope) -> Element { let width = if cx.generation() == 0 { "100%" } else { "99%" }; cx.render(rsx! { + // updated: 2 div { + // updated: 2 div{ + // updated: 2 div{ + // updated: 2 p{ width: "{width}", } } + // updated: 1 p{ + // updated: 1 "hello" } + // updated: 1 div{ + // updated: 1 h1{} } + // updated: 1 p{ + // updated: 1 "world" } } @@ -330,26 +318,20 @@ fn state_reduce_child_called_minimally_on_update() { let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); - let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); + vdom.mark_dirty(ScopeId(0)); + let (nodes_updated, _) = dom.apply_mutations(vdom.render_immediate()); let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); + let mut traverse_count = 0; dom.traverse_depth_first(|n| { - assert_eq!(n.state.part1.child_counter.0, { - let id = n.node_data.node_id.0; - if id > 4 { - 1 - } else { - 2 - } - }); assert_eq!(n.state.child_counter.0, { - let id = n.node_data.node_id.0; - if id > 4 { + if traverse_count > 4 { 1 } else { 2 } }); + traverse_count += 1; }); } @@ -440,15 +422,3 @@ fn dependancies_order_independant() { assert_eq!(&n.state.c, &c); }); } - -#[derive(Clone, Default, State)] -struct DependanciesStateTest { - #[node_dep_state(c)] - b: BDepCallCounter, - #[node_dep_state()] - c: CDepCallCounter, - #[node_dep_state(b)] - a: ADepCallCounter, - #[state] - child: UnorderedDependanciesState, -} From 40b15f834b164c9540adabe6cea80d9f04588cf8 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 6 Dec 2022 08:31:06 -0600 Subject: [PATCH 39/43] move persistant iterator test --- packages/native-core-macro/.gitignore | 2 + .../tests/persistent_iterator.rs | 297 ----------------- packages/native-core/.gitignore | 2 + packages/native-core/Cargo.toml | 3 +- packages/native-core/src/utils.rs | 303 +++++++++++++++++- 5 files changed, 308 insertions(+), 299 deletions(-) create mode 100644 packages/native-core-macro/.gitignore delete mode 100644 packages/native-core-macro/tests/persistent_iterator.rs create mode 100644 packages/native-core/.gitignore diff --git a/packages/native-core-macro/.gitignore b/packages/native-core-macro/.gitignore new file mode 100644 index 000000000..96ef6c0b9 --- /dev/null +++ b/packages/native-core-macro/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/packages/native-core-macro/tests/persistent_iterator.rs b/packages/native-core-macro/tests/persistent_iterator.rs deleted file mode 100644 index 0ad2e3ca2..000000000 --- a/packages/native-core-macro/tests/persistent_iterator.rs +++ /dev/null @@ -1,297 +0,0 @@ -use dioxus::prelude::*; -use dioxus_native_core::{ - node::NodeType, real_dom::RealDom, state::State, utils::PersistantElementIter, -}; -use dioxus_native_core_macro::State; - -#[derive(State, Default, Clone, Debug)] -struct Empty {} - -#[test] -#[allow(unused_variables)] -fn traverse() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!( - div{ - div{ - "hello" - p{ - "world" - } - "hello world" - } - } - ) - } - - let mut vdom = VirtualDom::new(Base); - let mutations = vdom.rebuild(); - - let mut rdom: RealDom = RealDom::new(); - - let _to_update = rdom.apply_mutations(mutations); - - let mut iter = PersistantElementIter::new(); - let div_tag = "div".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - let text1 = "hello".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Text { text: text1, .. } - )); - let p_tag = "p".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - let text2 = "world".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Text { text: text2, .. } - )); - let text3 = "hello world".to_string(); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Text { text: text3, .. } - )); - assert!(matches!( - &rdom[iter.next(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text3, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text2, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text1, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); - assert!(matches!( - &rdom[iter.prev(&rdom).id()].node_data.node_type, - NodeType::Text { text: text3, .. } - )); -} - -#[test] -#[allow(unused_variables)] -fn persist_removes() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - let children = match cx.generation() % 2 { - 0 => 3, - 1 => 2, - _ => unreachable!(), - }; - render!( - div{ - (0..children).map(|i|{ - rsx!{ - p{ - key: "{i}", - "{i}" - } - } - }) - } - ) - } - let mut vdom = VirtualDom::new(Base); - - let mut rdom: RealDom = RealDom::new(); - - let build = vdom.rebuild(); - let _to_update = rdom.apply_mutations(build); - - // this will end on the node that is removed - let mut iter1 = PersistantElementIter::new(); - // this will end on the after node that is removed - let mut iter2 = PersistantElementIter::new(); - // div - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // p - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // "1" - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // p - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // "2" - iter1.next(&rdom).id(); - iter2.next(&rdom).id(); - // p - iter2.next(&rdom).id(); - // "3" - iter2.next(&rdom).id(); - - vdom.mark_dirty(ScopeId(0)); - let update = vdom.render_immediate(); - iter1.prune(&update, &rdom); - iter2.prune(&update, &rdom); - let _to_update = rdom.apply_mutations(update); - - let p_tag = "1".to_string(); - let idx = iter1.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - let text = "2".to_string(); - let idx = iter1.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Text { text, .. } - )); - let div_tag = "div".to_string(); - let idx = iter2.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: div_tag, .. } - )); -} - -#[test] -#[allow(unused_variables)] -fn persist_instertions_before() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - let children = match cx.generation() % 2 { - 0 => 3, - 1 => 2, - _ => unreachable!(), - }; - render!( - div{ - (0..children).map(|i|{ - rsx!{ - p{ - key: "{i}", - "{i}" - } - } - }) - } - ) - } - let mut vdom = VirtualDom::new(Base); - - let mut rdom: RealDom = RealDom::new(); - - let build = vdom.rebuild(); - let _to_update = rdom.apply_mutations(build); - - let mut iter = PersistantElementIter::new(); - // div - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "1" - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "2" - iter.next(&rdom).id(); - - vdom.mark_dirty(ScopeId(0)); - let update = vdom.render_immediate(); - iter.prune(&update, &rdom); - let _to_update = rdom.apply_mutations(update); - - let p_tag = "div".to_string(); - let idx = iter.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); -} - -#[test] -#[allow(unused_variables)] -fn persist_instertions_after() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - let children = match cx.generation() % 2 { - 0 => 3, - 1 => 2, - _ => unreachable!(), - }; - render!( - div{ - (0..children).map(|i|{ - rsx!{ - p{ - key: "{i}", - "{i}" - } - } - }) - } - ) - } - let mut vdom = VirtualDom::new(Base); - - let mut rdom: RealDom = RealDom::new(); - - let build = vdom.rebuild(); - let _to_update = rdom.apply_mutations(build); - - let mut iter = PersistantElementIter::new(); - // div - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "hello" - iter.next(&rdom).id(); - // p - iter.next(&rdom).id(); - // "world" - iter.next(&rdom).id(); - - let update = vdom.rebuild(); - iter.prune(&update, &rdom); - let _to_update = rdom.apply_mutations(update); - - let p_tag = "p".to_string(); - let idx = iter.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Element { tag: p_tag, .. } - )); - let text = "hello world".to_string(); - let idx = iter.next(&rdom).id(); - assert!(matches!( - &rdom[idx].node_data.node_type, - NodeType::Text { text, .. } - )); -} diff --git a/packages/native-core/.gitignore b/packages/native-core/.gitignore new file mode 100644 index 000000000..96ef6c0b9 --- /dev/null +++ b/packages/native-core/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index f15a743f3..309fba781 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -25,4 +25,5 @@ crossbeam-deque = "0.8.2" dashmap = "5.4.0" [dev-dependencies] -rand = "0.8.5" \ No newline at end of file +rand = "0.8.5" +dioxus = { path = "../dioxus", version = "^0.2.1" } \ No newline at end of file diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index ac6d1f6d3..7a55ad6db 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -125,7 +125,7 @@ impl PersistantElementIter { /// get the next element pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { - let r=if self.stack.is_empty() { + let r = if self.stack.is_empty() { let id = NodeId(0); let new = (id, NodePosition::AtNode); self.stack.push(new); @@ -227,3 +227,304 @@ impl PersistantElementIter { self.stack.pop().unwrap().0 } } + +#[derive(Default, Clone, Debug)] +struct Empty {} +impl State for Empty { + const PASSES: &'static [crate::AnyPass>] = &[]; + + const MASKS: &'static [crate::NodeMask] = &[]; +} + +#[test] +#[allow(unused_variables)] +fn traverse() { + use dioxus::prelude::*; + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + render!( + div{ + div{ + "hello" + p{ + "world" + } + "hello world" + } + } + ) + } + + let mut vdom = VirtualDom::new(Base); + let mutations = vdom.rebuild(); + + let mut rdom: RealDom = RealDom::new(); + + let _to_update = rdom.apply_mutations(mutations); + + let mut iter = PersistantElementIter::new(); + let div_tag = "div".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + let text1 = "hello".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Text { text: text1, .. } + )); + let p_tag = "p".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + let text2 = "world".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Text { text: text2, .. } + )); + let text3 = "hello world".to_string(); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Text { text: text3, .. } + )); + assert!(matches!( + &rdom[iter.next(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text3, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text2, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text1, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); + assert!(matches!( + &rdom[iter.prev(&rdom).id()].node_data.node_type, + NodeType::Text { text: text3, .. } + )); +} + +#[test] +#[allow(unused_variables)] +fn persist_removes() { + use dioxus::prelude::*; + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let children = match cx.generation() % 2 { + 0 => 3, + 1 => 2, + _ => unreachable!(), + }; + render!( + div{ + (0..children).map(|i|{ + rsx!{ + p{ + key: "{i}", + "{i}" + } + } + }) + } + ) + } + let mut vdom = VirtualDom::new(Base); + + let mut rdom: RealDom = RealDom::new(); + + let build = vdom.rebuild(); + let _to_update = rdom.apply_mutations(build); + + // this will end on the node that is removed + let mut iter1 = PersistantElementIter::new(); + // this will end on the after node that is removed + let mut iter2 = PersistantElementIter::new(); + // div + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // p + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // "1" + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // p + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // "2" + iter1.next(&rdom).id(); + iter2.next(&rdom).id(); + // p + iter2.next(&rdom).id(); + // "3" + iter2.next(&rdom).id(); + + vdom.mark_dirty(ScopeId(0)); + let update = vdom.render_immediate(); + iter1.prune(&update, &rdom); + iter2.prune(&update, &rdom); + let _to_update = rdom.apply_mutations(update); + + let p_tag = "1".to_string(); + let idx = iter1.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + let text = "2".to_string(); + let idx = iter1.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Text { text, .. } + )); + let div_tag = "div".to_string(); + let idx = iter2.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: div_tag, .. } + )); +} + +#[test] +#[allow(unused_variables)] +fn persist_instertions_before() { + use dioxus::prelude::*; + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let children = match cx.generation() % 2 { + 0 => 3, + 1 => 2, + _ => unreachable!(), + }; + render!( + div{ + (0..children).map(|i|{ + rsx!{ + p{ + key: "{i}", + "{i}" + } + } + }) + } + ) + } + let mut vdom = VirtualDom::new(Base); + + let mut rdom: RealDom = RealDom::new(); + + let build = vdom.rebuild(); + let _to_update = rdom.apply_mutations(build); + + let mut iter = PersistantElementIter::new(); + // div + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "1" + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "2" + iter.next(&rdom).id(); + + vdom.mark_dirty(ScopeId(0)); + let update = vdom.render_immediate(); + iter.prune(&update, &rdom); + let _to_update = rdom.apply_mutations(update); + + let p_tag = "div".to_string(); + let idx = iter.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); +} + +#[test] +#[allow(unused_variables)] +fn persist_instertions_after() { + use dioxus::prelude::*; + #[allow(non_snake_case)] + fn Base(cx: Scope) -> Element { + let children = match cx.generation() % 2 { + 0 => 3, + 1 => 2, + _ => unreachable!(), + }; + render!( + div{ + (0..children).map(|i|{ + rsx!{ + p{ + key: "{i}", + "{i}" + } + } + }) + } + ) + } + let mut vdom = VirtualDom::new(Base); + + let mut rdom: RealDom = RealDom::new(); + + let build = vdom.rebuild(); + let _to_update = rdom.apply_mutations(build); + + let mut iter = PersistantElementIter::new(); + // div + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "hello" + iter.next(&rdom).id(); + // p + iter.next(&rdom).id(); + // "world" + iter.next(&rdom).id(); + + let update = vdom.rebuild(); + iter.prune(&update, &rdom); + let _to_update = rdom.apply_mutations(update); + + let p_tag = "p".to_string(); + let idx = iter.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Element { tag: p_tag, .. } + )); + let text = "hello world".to_string(); + let idx = iter.next(&rdom).id(); + assert!(matches!( + &rdom[idx].node_data.node_type, + NodeType::Text { text, .. } + )); +} From 6ee4f7df4f4fefb9dd3e4add98dfbdfa0f6f96b5 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 6 Dec 2022 08:33:51 -0600 Subject: [PATCH 40/43] update examples --- packages/native-core/src/state.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/native-core/src/state.rs b/packages/native-core/src/state.rs index 365aa151c..7e6c5266a 100644 --- a/packages/native-core/src/state.rs +++ b/packages/native-core/src/state.rs @@ -56,17 +56,17 @@ pub(crate) fn union_ordered_iter<'a>( /// impl ChildDepState for Layout { /// type Ctx = (); /// // The layout depends on the layout of the children. -/// type DepState = Layout; +/// type DepState = (Layout,); /// fn reduce<'a>( /// &mut self, /// _node: NodeView, -/// children: impl Iterator, +/// children: impl Iterator, /// _ctx: &Self::Ctx, /// ) -> bool /// where /// Self::DepState: 'a{ /// /// Children are layed out form left to right. The width of the parent is the sum of the widths and the max of the heights. -/// let new = children.copied().reduce(|c1, c2| Layout{ +/// let new = children.map(|(&c,)|c).reduce(|c1, c2| Layout{ /// width: c1.width + c2.width, /// height: c1.height.max(c2.height) /// }).unwrap_or_default(); @@ -108,7 +108,7 @@ pub trait ChildDepState { /// impl ParentDepState for FontSize { /// type Ctx = (); /// // The font size depends on the font size of the parent element. -/// type DepState = Self; +/// type DepState = (Self,); /// const NODE_MASK: NodeMask = /// NodeMask::new_with_attrs(AttributeMask::Static(&[ /// "font-size" @@ -116,12 +116,12 @@ pub trait ChildDepState { /// fn reduce<'a>( /// &mut self, /// node: NodeView, -/// parent: Option<&'a Self::DepState>, +/// parent: Option<(&'a Self,)>, /// ctx: &Self::Ctx, /// ) -> bool{ /// let old = *self; /// // If the font size was set on the parent, it is passed down to the current element -/// if let Some(parent) = parent { +/// if let Some((parent,)) = parent { /// *self = *parent; /// } /// // If the current node overrides the font size, use that size insead. @@ -168,6 +168,7 @@ pub trait ParentDepState { /// /// impl NodeDepState for TabIndex { /// type Ctx = (); +/// type DepState = (); /// const NODE_MASK: NodeMask = /// NodeMask::new_with_attrs(AttributeMask::Static(&[ /// "tabindex" From 4be64cb9f04b0e265c17d54e751759833e78fa39 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 6 Dec 2022 09:08:25 -0600 Subject: [PATCH 41/43] fix gaps in layout --- packages/native-core/src/utils.rs | 6 ++---- packages/tui/src/hooks.rs | 10 +++++++++- packages/tui/src/layout.rs | 6 ++++-- packages/tui/src/lib.rs | 25 +++++++++++++++++-------- packages/tui/src/query.rs | 23 ++++++++++++++++------- packages/tui/src/render.rs | 20 +++++++++----------- 6 files changed, 57 insertions(+), 33 deletions(-) diff --git a/packages/native-core/src/utils.rs b/packages/native-core/src/utils.rs index 7a55ad6db..3ffe45916 100644 --- a/packages/native-core/src/utils.rs +++ b/packages/native-core/src/utils.rs @@ -125,7 +125,7 @@ impl PersistantElementIter { /// get the next element pub fn next(&mut self, rdom: &RealDom) -> ElementProduced { - let r = if self.stack.is_empty() { + if self.stack.is_empty() { let id = NodeId(0); let new = (id, NodePosition::AtNode); self.stack.push(new); @@ -156,9 +156,7 @@ impl PersistantElementIter { ElementProduced::Progressed(self.pop()) } } - }; - println!("next: {:?}", r); - r + } } /// get the previous element diff --git a/packages/tui/src/hooks.rs b/packages/tui/src/hooks.rs index 197c7ea79..b57b71e13 100644 --- a/packages/tui/src/hooks.rs +++ b/packages/tui/src/hooks.rs @@ -23,7 +23,7 @@ use std::{ use taffy::geometry::{Point, Size}; use taffy::{prelude::Layout, Taffy}; -use crate::FocusState; +use crate::{layout_to_screen_space, FocusState}; use crate::{TuiDom, TuiNode}; pub(crate) struct Event { @@ -243,7 +243,15 @@ impl InnerInputState { ) { fn layout_contains_point(layout: &Layout, point: ScreenPoint) -> bool { let Point { x, y } = layout.location; + let (x, y) = ( + layout_to_screen_space(x).round(), + layout_to_screen_space(y).round(), + ); let Size { width, height } = layout.size; + let (width, height) = ( + layout_to_screen_space(width).round(), + layout_to_screen_space(height).round(), + ); let layout_rect = Rect::new(Point2D::new(x, y), Size2D::new(width, height)); layout_rect.contains(point.cast()) diff --git a/packages/tui/src/layout.rs b/packages/tui/src/layout.rs index 1e14f159c..d863f13d8 100644 --- a/packages/tui/src/layout.rs +++ b/packages/tui/src/layout.rs @@ -7,6 +7,8 @@ use dioxus_native_core::state::ChildDepState; use dioxus_native_core_macro::sorted_str_slice; use taffy::prelude::*; +use crate::screen_to_layout_space; + #[derive(Debug, Clone, Copy, PartialEq)] pub(crate) enum PossiblyUninitalized { Uninitalized, @@ -66,10 +68,10 @@ impl ChildDepState for TaffyLayout { style = Style { size: Size { // characters are 1 point tall - height: Dimension::Points(1.0), + height: Dimension::Points(screen_to_layout_space(1)), // text is as long as it is declared - width: Dimension::Points(char_len as f32), + width: Dimension::Points(screen_to_layout_space(char_len as u16)), }, ..Default::default() }; diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index cb000f00e..058dfc7d5 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -35,6 +35,15 @@ pub use config::*; pub use hooks::*; pub(crate) use node::*; +// the layout space has a multiplier of 10 to minimize rounding errors +pub(crate)fn screen_to_layout_space(screen: u16) -> f32{ + screen as f32 * 10.0 +} + +pub(crate)fn layout_to_screen_space(layout: f32) -> f32{ + layout / 10.0 +} + #[derive(Clone)] pub struct TuiContext { tx: UnboundedSender, @@ -149,22 +158,22 @@ fn render_vdom( if !to_rerender.is_empty() || updated { updated = false; fn resize(dims: Rect, taffy: &mut Taffy, rdom: &TuiDom) { - let width = dims.width; - let height = dims.height; + let width = screen_to_layout_space(dims.width ); + let height = screen_to_layout_space(dims.height); let root_node = rdom[NodeId(0)].state.layout.node.unwrap(); // the root node fills the entire area let mut style=*taffy.style(root_node).unwrap(); style.size=Size { - width: Dimension::Points(width as f32), - height: Dimension::Points(height as f32), + width: Dimension::Points(width ), + height: Dimension::Points(height ), }; taffy.set_style(root_node, style).unwrap(); let size =Size { - width: AvailableSpace::Definite(width as f32), - height: AvailableSpace::Definite(height as f32), + width: AvailableSpace::Definite(width), + height: AvailableSpace::Definite(height), }; taffy .compute_layout( @@ -195,8 +204,8 @@ fn render_vdom( Rect { x: 0, y: 0, - width: 100, - height: 100, + width: 1000, + height: 1000, }, &mut taffy.lock().expect("taffy lock poisoned"), &rdom, diff --git a/packages/tui/src/query.rs b/packages/tui/src/query.rs index 15b7fd7f2..3fbcc15fa 100644 --- a/packages/tui/src/query.rs +++ b/packages/tui/src/query.rs @@ -11,7 +11,7 @@ use taffy::{ Taffy, }; -use crate::TuiDom; +use crate::{layout_to_screen_space, TuiDom}; /// Allows querying the layout of nodes after rendering. It will only provide a correct value after a node is rendered. /// Provided as a root context for all tui applictions. @@ -72,19 +72,28 @@ impl<'a> ElementRef<'a> { } pub fn size(&self) -> Option> { - self.layout().map(|l| l.size.map(|v| v as u32)) + self.layout().map(|l| l.size.map(|v| v.round() as u32)) } pub fn pos(&self) -> Option> { self.layout().map(|l| Point { - x: l.location.x as u32, - y: l.location.y as u32, + x: l.location.x.round() as u32, + y: l.location.y.round() as u32, }) } - pub fn layout(&self) -> Option<&Layout> { - self.stretch + pub fn layout(&self) -> Option { + let layout = self + .stretch .layout(self.inner[self.id].state.layout.node.ok()?) - .ok() + .ok(); + layout.map(|layout| Layout { + order: layout.order, + size: layout.size.map(|v| layout_to_screen_space(v)), + location: Point { + x: layout_to_screen_space(layout.location.x), + y: layout_to_screen_space(layout.location.y), + }, + }) } } diff --git a/packages/tui/src/render.rs b/packages/tui/src/render.rs index d779418fa..ef18f2989 100644 --- a/packages/tui/src/render.rs +++ b/packages/tui/src/render.rs @@ -8,6 +8,7 @@ use taffy::{ use tui::{backend::CrosstermBackend, layout::Rect, style::Color}; use crate::{ + layout_to_screen_space, style::{RinkColor, RinkStyle}, style_attributes::{BorderEdge, BorderStyle}, widget::{RinkBuffer, RinkCell, RinkWidget, WidgetWithContext}, @@ -36,15 +37,12 @@ pub(crate) fn render_vnode( location.x += parent_location.x; location.y += parent_location.y; - let Point { mut x, mut y } = location; - x = x.floor(); - y = y.floor(); - let Size { - mut width, - mut height, - } = size; - width = width.ceil(); - height = height.ceil(); + let Point { x: fx, y: fy } = location; + let x = layout_to_screen_space(fx).round() as u16; + let y = layout_to_screen_space(fy).round() as u16; + let Size { width, height } = *size; + let width = layout_to_screen_space(fx + width).round() as u16 + x; + let height = layout_to_screen_space(fy + height).round() as u16 - y; match &node.node_data.node_type { NodeType::Text { text } => { @@ -69,7 +67,7 @@ pub(crate) fn render_vnode( text, style: node.state.style.core, }; - let area = Rect::new(x as u16, y as u16, width as u16, height as u16); + let area = Rect::new(x, y, width, height); // the renderer will panic if a node is rendered out of range even if the size is zero if area.width > 0 && area.height > 0 { @@ -77,7 +75,7 @@ pub(crate) fn render_vnode( } } NodeType::Element { .. } => { - let area = Rect::new(x as u16, y as u16, width as u16, height as u16); + let area = Rect::new(x, y, width, height); // the renderer will panic if a node is rendered out of range even if the size is zero if area.width > 0 && area.height > 0 { From 4b1c6125d4b10458534b8350706e58d21ded7aab Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 6 Dec 2022 09:16:40 -0600 Subject: [PATCH 42/43] fix formatting --- packages/tui/examples/tui_colorpicker.rs | 2 +- packages/tui/src/lib.rs | 57 ++++++++++-------------- 2 files changed, 25 insertions(+), 34 deletions(-) diff --git a/packages/tui/examples/tui_colorpicker.rs b/packages/tui/examples/tui_colorpicker.rs index 3b1184930..e2c16d5bb 100644 --- a/packages/tui/examples/tui_colorpicker.rs +++ b/packages/tui/examples/tui_colorpicker.rs @@ -1,5 +1,5 @@ -use dioxus::prelude::*; use dioxus::core::RenderReturn; +use dioxus::prelude::*; use dioxus_tui::query::Query; use dioxus_tui::Size; diff --git a/packages/tui/src/lib.rs b/packages/tui/src/lib.rs index 058dfc7d5..38638f7c5 100644 --- a/packages/tui/src/lib.rs +++ b/packages/tui/src/lib.rs @@ -4,19 +4,22 @@ use crossterm::{ execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use futures_channel::mpsc::unbounded; use dioxus_core::*; -use dioxus_native_core::{real_dom::RealDom, NodeId, SendAnyMap, FxDashSet, NodeMask}; +use dioxus_native_core::{real_dom::RealDom, FxDashSet, NodeId, NodeMask, SendAnyMap}; use focus::FocusState; use futures::{ channel::mpsc::{UnboundedReceiver, UnboundedSender}, pin_mut, StreamExt, }; +use futures_channel::mpsc::unbounded; use query::Query; -use std::{cell::RefCell, sync::{Mutex, Arc}}; use std::rc::Rc; +use std::{ + cell::RefCell, + sync::{Arc, Mutex}, +}; use std::{io, time::Duration}; -use taffy::{Taffy}; +use taffy::Taffy; pub use taffy::{geometry::Point, prelude::*}; use tui::{backend::CrosstermBackend, layout::Rect, Terminal}; @@ -36,11 +39,11 @@ pub use hooks::*; pub(crate) use node::*; // the layout space has a multiplier of 10 to minimize rounding errors -pub(crate)fn screen_to_layout_space(screen: u16) -> f32{ +pub(crate) fn screen_to_layout_space(screen: u16) -> f32 { screen as f32 * 10.0 } -pub(crate)fn layout_to_screen_space(layout: f32) -> f32{ +pub(crate) fn layout_to_screen_space(layout: f32) -> f32 { layout / 10.0 } @@ -99,7 +102,7 @@ pub fn launch_cfg(app: Component<()>, cfg: Config) { { let mut rdom = rdom.borrow_mut(); let mutations = dom.rebuild(); - let (to_update,_) = rdom.apply_mutations(mutations); + let (to_update, _) = rdom.apply_mutations(mutations); let mut any_map = SendAnyMap::new(); any_map.insert(taffy.clone()); let _to_rerender = rdom.update_state(to_update, any_map); @@ -141,10 +144,10 @@ fn render_vdom( terminal.clear().unwrap(); } - let mut to_rerender =FxDashSet::default(); + let mut to_rerender = FxDashSet::default(); to_rerender.insert(NodeId(0)); let mut updated = true; - + loop { /* -> render the nodes in the right place with tui/crossterm @@ -158,45 +161,33 @@ fn render_vdom( if !to_rerender.is_empty() || updated { updated = false; fn resize(dims: Rect, taffy: &mut Taffy, rdom: &TuiDom) { - let width = screen_to_layout_space(dims.width ); + let width = screen_to_layout_space(dims.width); let height = screen_to_layout_space(dims.height); let root_node = rdom[NodeId(0)].state.layout.node.unwrap(); - + // the root node fills the entire area - - let mut style=*taffy.style(root_node).unwrap(); - style.size=Size { - width: Dimension::Points(width ), - height: Dimension::Points(height ), + + let mut style = *taffy.style(root_node).unwrap(); + style.size = Size { + width: Dimension::Points(width), + height: Dimension::Points(height), }; taffy.set_style(root_node, style).unwrap(); - - let size =Size { + + let size = Size { width: AvailableSpace::Definite(width), height: AvailableSpace::Definite(height), }; - taffy - .compute_layout( - root_node, - size - ) - .unwrap(); + taffy.compute_layout(root_node, size).unwrap(); } if let Some(terminal) = &mut terminal { terminal.draw(|frame| { let rdom = rdom.borrow(); - let mut taffy =taffy.lock().expect("taffy lock poisoned"); + let mut taffy = taffy.lock().expect("taffy lock poisoned"); // size is guaranteed to not change when rendering resize(frame.size(), &mut *taffy, &rdom); let root = &rdom[NodeId(0)]; - render::render_vnode( - frame, - &*taffy, - &rdom, - root, - cfg, - Point::ZERO, - ); + render::render_vnode(frame, &*taffy, &rdom, root, cfg, Point::ZERO); })?; } else { let rdom = rdom.borrow(); From 77381ceadd576127fa602ff61e8d7cd7092b3e79 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Tue, 6 Dec 2022 09:54:44 -0600 Subject: [PATCH 43/43] fix memory leak --- packages/native-core/src/real_dom.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index f5fb17603..ffda78ff7 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -76,6 +76,10 @@ impl RealDom { if self.node_id_mapping.len() <= element_id.0 { self.node_id_mapping.resize(element_id.0 + 1, None); } + if let Some(Some(old_id)) = self.node_id_mapping.get(element_id.0) { + // free the memory associated with the old node + self.tree.remove(*old_id); + } self.node_id_mapping[element_id.0] = Some(node_id); }