From 47383529558eef499df01901e4cb5bcec8729e2c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Sun, 22 Jan 2023 16:41:15 -0600 Subject: [PATCH] remove native-core-macro --- Cargo.toml | 1 - packages/native-core-macro/.gitignore | 2 - packages/native-core-macro/Cargo.toml | 26 -- packages/native-core-macro/README.md | 43 -- packages/native-core-macro/src/lib.rs | 134 ------ .../native-core-macro/src/sorted_slice.rs | 29 -- .../tests/called_minimally_on_build.rs | 244 ---------- .../native-core-macro/tests/update_state.rs | 421 ------------------ packages/native-core/Cargo.toml | 1 - packages/native-core/src/node_ref.rs | 280 +++++------- packages/native-core/src/passes.rs | 9 +- packages/native-core/src/real_dom.rs | 8 +- packages/tui/Cargo.toml | 1 - 13 files changed, 118 insertions(+), 1081 deletions(-) delete mode 100644 packages/native-core-macro/.gitignore delete mode 100644 packages/native-core-macro/Cargo.toml delete mode 100644 packages/native-core-macro/README.md delete mode 100644 packages/native-core-macro/src/lib.rs delete mode 100644 packages/native-core-macro/src/sorted_slice.rs delete mode 100644 packages/native-core-macro/tests/called_minimally_on_build.rs delete mode 100644 packages/native-core-macro/tests/update_state.rs diff --git a/Cargo.toml b/Cargo.toml index 7d8cc6871..6e376d917 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ members = [ "packages/rsx", "packages/tui", "packages/native-core", - "packages/native-core-macro", "packages/rsx-rosetta", "packages/signals", "docs/guide", diff --git a/packages/native-core-macro/.gitignore b/packages/native-core-macro/.gitignore deleted file mode 100644 index 96ef6c0b9..000000000 --- a/packages/native-core-macro/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/packages/native-core-macro/Cargo.toml b/packages/native-core-macro/Cargo.toml deleted file mode 100644 index 6a37f75b2..000000000 --- a/packages/native-core-macro/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "dioxus-native-core-macro" -version = "0.2.0" -edition = "2021" -description = "Build natively rendered apps with Dioxus" -license = "MIT/Apache-2.0" -repository = "https://github.com/DioxusLabs/dioxus/" -homepage = "https://dioxuslabs.com" -documentation = "https://dioxuslabs.com" -keywords = ["dom", "ui", "gui", "react"] - - -[lib] -proc-macro = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -syn = { version = "1.0.11", features = ["extra-traits"] } -quote = "1.0" -dioxus-native-core = { path = "../native-core", version = "^0.2.0" } -dioxus = { path = "../dioxus" } - -[dev-dependencies] -smallvec = "1.6" -rustc-hash = "1.1.0" -anymap = "0.12.1" diff --git a/packages/native-core-macro/README.md b/packages/native-core-macro/README.md deleted file mode 100644 index 90d7123b1..000000000 --- a/packages/native-core-macro/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Dioxus Native Core Macro - -[![Crates.io][crates-badge]][crates-url] -[![MIT licensed][mit-badge]][mit-url] -[![Build Status][actions-badge]][actions-url] -[![Discord chat][discord-badge]][discord-url] - -[crates-badge]: https://img.shields.io/crates/v/dioxus-native-core-macro.svg -[crates-url]: https://crates.io/crates/dioxus-native-core-macro - -[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg -[mit-url]: https://github.com/dioxuslabs/dioxus/blob/master/LICENSE - -[actions-badge]: https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg -[actions-url]: https://github.com/dioxuslabs/dioxus/actions?query=workflow%3ACI+branch%3Amaster - -[discord-badge]: https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square -[discord-url]: https://discord.gg/XgGxMSkvUM - -[Website](https://dioxuslabs.com) | -[Guides](https://dioxuslabs.com/guide/) | -[API Docs](https://docs.rs/dioxus-native-core-macro/latest/dioxus_native_core_macro) | -[Chat](https://discord.gg/XgGxMSkvUM) - - -## Overview - -`dioxus-native-core-macro` provides a handful of macros used by native-core for native renderers like TUI, Blitz, and Freya to derive their state. - - -## Contributing - -- Report issues on our [issue tracker](https://github.com/dioxuslabs/dioxus/issues). -- Join the discord and ask questions! - -## License -This project is licensed under the [MIT license]. - -[mit license]: https://github.com/DioxusLabs/dioxus/blob/master/LICENSE-MIT - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in Dioxus by you shall be licensed as MIT without any additional -terms or conditions. diff --git a/packages/native-core-macro/src/lib.rs b/packages/native-core-macro/src/lib.rs deleted file mode 100644 index 5ab2c8388..000000000 --- a/packages/native-core-macro/src/lib.rs +++ /dev/null @@ -1,134 +0,0 @@ -extern crate proc_macro; - -mod sorted_slice; - -use proc_macro::TokenStream; -use quote::quote; -use sorted_slice::StrSlice; -use syn::{self, parse_macro_input}; - -/// Sorts a slice of string literals at compile time. -#[proc_macro] -pub fn sorted_str_slice(input: TokenStream) -> TokenStream { - let slice: StrSlice = parse_macro_input!(input as StrSlice); - let strings = slice.map.values(); - quote!([#(#strings, )*]).into() -} - -/// Derive's the state from any members that implement the Pass trait -#[proc_macro_derive(State, attributes(skip, skip_clone))] -pub fn state_macro_derive(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - impl_derive_macro(&ast) -} - -fn impl_derive_macro(ast: &syn::DeriveInput) -> TokenStream { - let custom_type = ast - .attrs - .iter() - .find(|a| a.path.is_ident("state")) - .and_then(|attr| { - // parse custom_type = "MyType" - let assignment = attr.parse_args::().unwrap(); - if let syn::Expr::Assign(assign) = assignment { - let (left, right) = (&*assign.left, &*assign.right); - if let syn::Expr::Path(e) = left { - let path = &e.path; - if let Some(ident) = path.get_ident() { - if ident == "custom_value" { - return match right { - syn::Expr::Path(e) => { - let path = &e.path; - Some(quote! {#path}) - } - _ => None, - }; - } - } - } - } - None - }) - .unwrap_or(quote! {()}); - let type_name = &ast.ident; - let fields: Vec<_> = match &ast.data { - syn::Data::Struct(data) => match &data.fields { - syn::Fields::Named(e) => &e.named, - syn::Fields::Unnamed(_) => todo!("unnamed fields"), - syn::Fields::Unit => todo!("unit structs"), - } - .iter() - .collect(), - _ => unimplemented!(), - }; - - let types = fields - .iter() - .filter(|field| !field.attrs.iter().any(|attr| attr.path.is_ident("skip"))) - .map(|field| &field.ty); - - let gen = quote! { - impl dioxus_native_core::State<#custom_type> for #type_name { - fn create_passes() -> Box<[dioxus_native_core::TypeErasedPass]> { - Box::new([ - #( - <#types as dioxus_native_core::Pass>::to_type_erased() - ),* - ]) - } - } - }; - - gen.into() -} - -/// Derive's the state from any elements that have a node_dep_state, child_dep_state, parent_dep_state, or state attribute. -#[proc_macro_derive(AnyMapLike)] -pub fn anymap_like_macro_derive(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - impl_anymap_like_derive_macro(&ast) -} - -fn impl_anymap_like_derive_macro(ast: &syn::DeriveInput) -> TokenStream { - let type_name = &ast.ident; - let fields: Vec<_> = match &ast.data { - syn::Data::Struct(data) => match &data.fields { - syn::Fields::Named(e) => &e.named, - syn::Fields::Unnamed(_) => todo!("unnamed fields"), - syn::Fields::Unit => todo!("unit structs"), - } - .iter() - .collect(), - _ => unimplemented!(), - }; - - let names: Vec<_> = fields - .iter() - .map(|field| field.ident.as_ref().unwrap()) - .collect(); - let types: Vec<_> = fields.iter().map(|field| &field.ty).collect(); - - let gen = quote! { - impl dioxus_native_core::AnyMapLike for #type_name { - fn get(&self) -> Option<&T> { - #( - if std::any::TypeId::of::() == std::any::TypeId::of::<#types>() { - return unsafe { Some(&*(&self.#names as *const _ as *const T)) } - } - )* - None - } - - fn get_mut(&mut self) -> Option<&mut T> { - #( - if std::any::TypeId::of::() == std::any::TypeId::of::<#types>() { - return unsafe { Some(&mut *(&mut self.#names as *mut _ as *mut T)) } - } - )* - None - } - } - }; - - gen.into() -} diff --git a/packages/native-core-macro/src/sorted_slice.rs b/packages/native-core-macro/src/sorted_slice.rs deleted file mode 100644 index aba2b8b65..000000000 --- a/packages/native-core-macro/src/sorted_slice.rs +++ /dev/null @@ -1,29 +0,0 @@ -extern crate proc_macro; - -use std::collections::BTreeMap; - -use syn::{ - self, bracketed, - parse::{Parse, ParseStream, Result}, - LitStr, Token, -}; - -pub struct StrSlice { - pub map: BTreeMap, -} - -impl Parse for StrSlice { - fn parse(input: ParseStream) -> Result { - let content; - bracketed!(content in input); - let mut map = BTreeMap::new(); - while let Ok(s) = content.parse::() { - map.insert(s.value(), s); - #[allow(unused_must_use)] - { - content.parse::(); - } - } - Ok(StrSlice { map }) - } -} diff --git a/packages/native-core-macro/tests/called_minimally_on_build.rs b/packages/native-core-macro/tests/called_minimally_on_build.rs deleted file mode 100644 index 69454dc43..000000000 --- a/packages/native-core-macro/tests/called_minimally_on_build.rs +++ /dev/null @@ -1,244 +0,0 @@ -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::SendAnyMap; -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::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<::ElementBorrowed<'_>>, - _: &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" - } - } - } - } - } - - let mut vdom = VirtualDom::new(Base); - - let mutations = vdom.rebuild(); - - let mut dom: RealDom<$s> = RealDom::new(); - - let (nodes_updated, _) = dom.apply_mutations(mutations); - let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::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); - )* - }); - } - } -} - -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 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, - } - - 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,))); - - #[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, 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)); -} - -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 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, - } - - 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,))); - - #[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, - } - - 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, ())); - - #[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, 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)); -} diff --git a/packages/native-core-macro/tests/update_state.rs b/packages/native-core-macro/tests/update_state.rs deleted file mode 100644 index fc36c42ed..000000000 --- a/packages/native-core-macro/tests/update_state.rs +++ /dev/null @@ -1,421 +0,0 @@ -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, SendAnyMap}; -use dioxus_native_core_macro::State; - -#[derive(Debug, Clone, Default, State)] -struct CallCounterState { - #[child_dep_state(child_counter)] - child_counter: ChildDepCallCounter, - #[parent_dep_state(parent_counter)] - parent_counter: ParentDepCallCounter, - #[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 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 = (); - 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 - } -} - -#[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; - 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, -} - -#[test] -fn state_initial() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render! { - div { - p{ - color: "red" - } - h1{} - } - } - } - - let mut vdom = VirtualDom::new(Base); - - let mutations = vdom.rebuild(); - - let mut dom: RealDom = RealDom::new(); - - 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_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_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_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 { - let width = if cx.generation() == 0 { "100%" } else { "99%" }; - cx.render(rsx! { - div { - width: "{width}", - div{ - div{ - p{} - } - p{ - "hello" - } - div{ - h1{} - } - p{ - "world" - } - } - } - }) - } - - let mut vdom = VirtualDom::new(Base); - - let mut dom: RealDom = RealDom::new(); - - let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); - let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); - 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| { - if is_root { - is_root = false; - assert_eq!(n.state.parent_counter.0, 1); - } else { - 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! { - // 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" - } - } - } - }) - } - - let mut vdom = VirtualDom::new(Base); - - let mut dom: RealDom = RealDom::new(); - - let (nodes_updated, _) = dom.apply_mutations(vdom.rebuild()); - let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::new()); - 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.child_counter.0, { - if traverse_count > 4 { - 1 - } else { - 2 - } - }); - traverse_count += 1; - }); -} - -#[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 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, 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 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 - } -} - -#[test] -fn dependancies_order_independant() { - #[allow(non_snake_case)] - fn Base(cx: Scope) -> Element { - render!(div { - width: "100%", - p{ - "hello" - } - }) - } - - let mut vdom = VirtualDom::new(Base); - - let mut dom: RealDom = RealDom::new(); - - let mutations = vdom.rebuild(); - let (nodes_updated, _) = dom.apply_mutations(mutations); - let _to_rerender = dom.update_state(nodes_updated, SendAnyMap::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); - }); -} diff --git a/packages/native-core/Cargo.toml b/packages/native-core/Cargo.toml index bf1f9ba92..e6b50b8a4 100644 --- a/packages/native-core/Cargo.toml +++ b/packages/native-core/Cargo.toml @@ -31,5 +31,4 @@ lightningcss = "1.0.0-alpha.39" [dev-dependencies] rand = "0.8.5" dioxus = { path = "../dioxus", version = "^0.3.0" } -dioxus-native-core-macro = { path = "../native-core-macro" } tokio = { version = "*", features = ["full"] } diff --git a/packages/native-core/src/node_ref.rs b/packages/native-core/src/node_ref.rs index a62a97d24..14145b5cc 100644 --- a/packages/native-core/src/node_ref.rs +++ b/packages/native-core/src/node_ref.rs @@ -1,47 +1,11 @@ -use std::cmp::Ordering; - use dioxus_core::ElementId; +use rustc_hash::FxHashSet; use crate::{ node::{ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeView}, NodeId, }; -/// Join two sorted iterators -pub(crate) fn union_ordered_iter<'a>( - s_iter: impl Iterator, - o_iter: impl Iterator, - new_len_guess: usize, -) -> Vec { - let mut s_peekable = s_iter.peekable(); - let mut o_peekable = o_iter.peekable(); - let mut v = Vec::with_capacity(new_len_guess); - while let Some(s_i) = s_peekable.peek() { - while let Some(o_i) = o_peekable.peek() { - match o_i.cmp(s_i) { - Ordering::Greater => { - break; - } - Ordering::Less => { - v.push(o_peekable.next().unwrap().to_string()); - } - Ordering::Equal => { - o_peekable.next(); - break; - } - } - } - v.push(s_peekable.next().unwrap().to_string()); - } - for o_i in o_peekable { - v.push(o_i.to_string()); - } - for w in v.windows(2) { - debug_assert!(w[1] > w[0]); - } - v -} - /// A view into a [VNode] with limited access. #[derive(Debug)] pub struct NodeView<'a, V: FromAnyValue = ()> { @@ -138,124 +102,50 @@ impl<'a, V: FromAnyValue> NodeView<'a, V> { #[derive(PartialEq, Eq, Clone, Debug)] pub enum AttributeMask { All, - /// A list of attribute names that are visible, this list must be sorted - Dynamic(Vec), - /// A list of attribute names that are visible, this list must be sorted - Static(&'static [&'static str]), + Some(FxHashSet>), } impl AttributeMask { - /// A empty attribute mask - pub const NONE: Self = Self::Static(&[]); - fn contains_attribute(&self, attr: &str) -> bool { match self { AttributeMask::All => true, - AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(), - AttributeMask::Static(l) => l.binary_search(&attr).is_ok(), + AttributeMask::Some(attrs) => attrs.contains(attr), } } /// Create a new dynamic attribute mask with a single attribute pub fn single(new: &str) -> Self { - Self::Dynamic(vec![new.to_string()]) - } - - /// Ensure the attribute list is sorted. - pub fn verify(&self) { - match &self { - AttributeMask::Static(attrs) => debug_assert!( - attrs.windows(2).all(|w| w[0] < w[1]), - "attritutes must be increasing" - ), - AttributeMask::Dynamic(attrs) => debug_assert!( - attrs.windows(2).all(|w| w[0] < w[1]), - "attritutes must be increasing" - ), - _ => (), - } + let mut set = FxHashSet::default(); + set.insert(new.into()); + Self::Some(set) } /// 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().map(|s| s.as_str()), - o.iter().map(|s| s.as_str()), - s.len() + o.len(), - )) + match (self, other) { + (AttributeMask::Some(s), AttributeMask::Some(o)) => { + AttributeMask::Some(s.intersection(o).cloned().collect()) } - (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()), - ), _ => AttributeMask::All, - }; - new.verify(); - new + } } /// Check if two attribute masks overlap fn overlaps(&self, other: &Self) -> bool { - 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 { - while other_attr < self_attr { - if let Some(attr) = other_iter.next() { - other_attr = attr; - } else { - return false; - } - } - if other_attr == self_attr { - return true; - } - } - } - false - } match (self, other) { - (AttributeMask::All, AttributeMask::All) => true, - (AttributeMask::All, AttributeMask::Dynamic(v)) => !v.is_empty(), - (AttributeMask::All, AttributeMask::Static(s)) => !s.is_empty(), - (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().map(|s| s.as_str()), v2.iter().map(|s| s.as_str())) - } - (AttributeMask::Dynamic(v), AttributeMask::Static(s)) => { - overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied()) - } - (AttributeMask::Static(s), AttributeMask::Dynamic(v)) => { - 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()) + (AttributeMask::All, AttributeMask::Some(attrs)) => !attrs.is_empty(), + (AttributeMask::Some(attrs), AttributeMask::All) => !attrs.is_empty(), + (AttributeMask::Some(attrs1), AttributeMask::Some(attrs2)) => { + !attrs1.is_disjoint(attrs2) } + _ => true, } } } impl Default for AttributeMask { fn default() -> Self { - AttributeMask::Static(&[]) + AttributeMask::Some(FxHashSet::default()) } } @@ -270,33 +160,33 @@ pub struct NodeMask { } impl NodeMask { - /// A node mask with no parts visible. - pub const NONE: Self = Self::new(); - /// A node mask with every part visible. - pub const ALL: Self = Self::new_with_attrs(AttributeMask::All) - .with_text() - .with_element() - .with_listeners(); - pub fn from_node(node: &NodeType) -> Self { match node { - NodeType::Text { .. } => Self::new().with_text(), + NodeType::Text { .. } => NodeMaskBuilder::new().with_text().build(), NodeType::Element(ElementNode { tag: _, namespace: _, attributes, listeners, - }) => { - let mut mask = Self::new_with_attrs(AttributeMask::Dynamic( - attributes.keys().map(|key| key.name.clone()).collect(), - )) - .with_element(); - if !listeners.is_empty() { - mask = mask.with_listeners(); - } - mask - } - NodeType::Placeholder => Self::new().with_element(), + }) => Self { + attritutes: AttributeMask::Some( + attributes + .keys() + .map(|key| key.name.as_str().into()) + .collect(), + ), + namespace: true, + tag: true, + text: false, + listeners: !listeners.is_empty(), + }, + NodeType::Placeholder => Self { + attritutes: AttributeMask::default(), + tag: true, + namespace: true, + text: false, + listeners: false, + }, } } @@ -320,8 +210,65 @@ impl NodeMask { } } + /// Allow the mask to view the given attributes + pub fn add_attributes(&mut self, attributes: AttributeMask) { + self.attritutes = self.attritutes.union(&attributes); + } + + /// Set the mask to view the tag + pub fn set_tag(&mut self) { + self.tag = true; + } + + /// Set the mask to view the namespace + pub fn set_namespace(&mut self) { + self.namespace = true; + } + + /// Set the mask to view the text + pub fn set_text(&mut self) { + self.text = true; + } + + /// Set the mask to view the listeners + pub fn set_listeners(&mut self) { + self.listeners = true; + } +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum AttributeMaskBuilder { + All, + Some(&'static [&'static str]), +} + +impl Default for AttributeMaskBuilder { + fn default() -> Self { + AttributeMaskBuilder::Some(&[]) + } +} + +/// A mask that limits what parts of a node a dependency can see. +#[derive(Default, PartialEq, Eq, Clone, Debug)] +pub struct NodeMaskBuilder { + attritutes: AttributeMaskBuilder, + tag: bool, + namespace: bool, + text: bool, + listeners: bool, +} + +impl NodeMaskBuilder { + /// A node mask with no parts visible. + pub const NONE: Self = Self::new(); + /// A node mask with every part visible. + pub const ALL: Self = Self::new_with_attrs(AttributeMaskBuilder::All) + .with_text() + .with_element() + .with_listeners(); + /// Create a new node mask with the given attributes - pub const fn new_with_attrs(attritutes: AttributeMask) -> Self { + pub const fn new_with_attrs(attritutes: AttributeMaskBuilder) -> Self { Self { attritutes, tag: false, @@ -333,7 +280,7 @@ impl NodeMask { /// Create a empty node mask pub const fn new() -> Self { - Self::new_with_attrs(AttributeMask::NONE) + Self::new_with_attrs(AttributeMaskBuilder::Some(&[])) } /// Allow the mask to view the tag @@ -342,22 +289,12 @@ impl NodeMask { self } - /// Set the mask to view the tag - pub fn set_tag(&mut self) { - self.tag = true; - } - /// Allow the mask to view the namespace pub const fn with_namespace(mut self) -> Self { self.namespace = true; self } - /// Set the mask to view the namespace - pub fn set_namespace(&mut self) { - self.namespace = true; - } - /// Allow the mask to view the namespace and tag pub const fn with_element(self) -> Self { self.with_namespace().with_tag() @@ -369,24 +306,25 @@ impl NodeMask { self } - /// Set the mask to view the text - pub fn set_text(&mut self) { - self.text = true; - } - /// Allow the mask to view the listeners pub const fn with_listeners(mut self) -> Self { self.listeners = true; self } - /// Set the mask to view the listeners - pub fn set_listeners(&mut self) { - self.listeners = true; - } - - /// Allow the mask to view the given attributes - pub fn add_attributes(&mut self, attributes: AttributeMask) { - self.attritutes = self.attritutes.union(&attributes); + /// Build the mask + pub fn build(self) -> NodeMask { + NodeMask { + attritutes: match self.attritutes { + AttributeMaskBuilder::All => AttributeMask::All, + AttributeMaskBuilder::Some(attrs) => { + AttributeMask::Some(attrs.iter().map(|s| (*s).into()).collect()) + } + }, + tag: self.tag, + namespace: self.namespace, + text: self.text, + listeners: self.listeners, + } } } diff --git a/packages/native-core/src/passes.rs b/packages/native-core/src/passes.rs index 854acfea6..cb21cc696 100644 --- a/packages/native-core/src/passes.rs +++ b/packages/native-core/src/passes.rs @@ -7,7 +7,7 @@ use std::marker::PhantomData; use std::sync::Arc; use crate::node::{FromAnyValue, NodeData}; -use crate::node_ref::NodeView; +use crate::node_ref::{NodeMaskBuilder, NodeView}; use crate::real_dom::RealDom; use crate::tree::TreeStateView; use crate::{FxDashMap, FxDashSet, SendAnyMap}; @@ -116,7 +116,7 @@ pub trait Pass: Any { type ChildDependencies: Dependancy; /// This is a tuple of (T: Any, ..) type NodeDependencies: Dependancy; - const NODE_MASK: NodeMask; + const NODE_MASK: NodeMaskBuilder; fn pass<'a>( &mut self, @@ -158,13 +158,14 @@ pub trait Pass: Any { Self: Sized, { Self::validate(); + let node_mask = Self::NODE_MASK.build(); TypeErasedPass { this_type_id: TypeId::of::(), combined_dependancy_type_ids: Self::all_dependanices().iter().copied().collect(), parent_dependant: !Self::parent_type_ids().is_empty(), child_dependant: !Self::child_type_ids().is_empty(), dependants: FxHashSet::default(), - mask: Self::NODE_MASK, + mask: node_mask, pass_direction: Self::pass_direction(), pass: Box::new( |node_id: NodeId, tree: &mut TreeStateView, context: &SendAnyMap| { @@ -181,7 +182,7 @@ pub trait Pass: Any { let myself = unsafe { &mut *node_raw }; myself.pass( - NodeView::new(node_data, Self::NODE_MASK), + NodeView::new(node_data, Self::NODE_MASK.build()), node, parent, children, diff --git a/packages/native-core/src/real_dom.rs b/packages/native-core/src/real_dom.rs index b856c0d6c..367a11eb7 100644 --- a/packages/native-core/src/real_dom.rs +++ b/packages/native-core/src/real_dom.rs @@ -7,7 +7,7 @@ use std::collections::VecDeque; use crate::node::{ ElementNode, FromAnyValue, NodeData, NodeType, OwnedAttributeDiscription, OwnedAttributeValue, }; -use crate::node_ref::{AttributeMask, NodeMask}; +use crate::node_ref::{AttributeMask, NodeMask, NodeMaskBuilder}; use crate::passes::{resolve_passes, DirtyNodeStates, TypeErasedPass}; use crate::tree::{EntryBuilder, NodeId, Tree}; use crate::{FxDashSet, SendAnyMap}; @@ -64,7 +64,7 @@ impl RealDom { let mut nodes_updated = FxHashMap::default(); let root_id = NodeId(0); - nodes_updated.insert(root_id, NodeMask::ALL); + nodes_updated.insert(root_id, NodeMaskBuilder::ALL.build()); RealDom { tree, @@ -389,7 +389,7 @@ impl RealDom { pub fn get_mut(&mut self, id: NodeId) -> Option> { self.tree.contains(id).then(|| NodeMut { id, - dirty: NodeMask::new(), + dirty: NodeMask::default(), dom: self, }) } @@ -616,7 +616,7 @@ impl<'a, V: FromAnyValue + Send> NodeMut<'a, V> { pub fn set_type(&mut self, new: NodeType) { self.node_data_mut().node_type = new; - self.dirty = NodeMask::ALL; + self.dirty = NodeMaskBuilder::ALL.build(); } pub fn read(&self) -> Option<&T> { diff --git a/packages/tui/Cargo.toml b/packages/tui/Cargo.toml index 5341d40f4..8d50b7bc6 100644 --- a/packages/tui/Cargo.toml +++ b/packages/tui/Cargo.toml @@ -17,7 +17,6 @@ dioxus = { path = "../dioxus", version = "^0.3.0" } dioxus-core = { path = "../core", version = "^0.3.0" } dioxus-html = { path = "../html", version = "^0.3.0" } dioxus-native-core = { path = "../native-core", version = "^0.2.0" } -dioxus-native-core-macro = { path = "../native-core-macro", version = "^0.2.0" } tui = "0.17.0" crossterm = "0.23.0"