From a05047d01e606425fb0d6595e9d27d3e15f32050 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 12 Jul 2021 18:19:27 -0400 Subject: [PATCH 01/11] wip: use the new structure --- packages/core/src/bumpframe.rs | 23 +- packages/core/src/context.rs | 13 +- packages/core/src/diff.rs | 126 ++--- packages/core/src/events.rs | 1 - packages/core/src/lib.rs | 7 +- packages/core/src/nodebuilder.rs | 748 -------------------------- packages/core/src/nodes.rs | 710 +++++++++++------------- packages/core/src/oldbuilder.rs | 484 +++++++++++++++++ packages/core/src/scope.rs | 4 + packages/core/src/util.rs | 5 +- packages/core/src/virtual_dom.rs | 2 +- packages/{core => html}/src/styles.rs | 2 +- 12 files changed, 884 insertions(+), 1241 deletions(-) create mode 100644 packages/core/src/oldbuilder.rs rename packages/{core => html}/src/styles.rs (98%) diff --git a/packages/core/src/bumpframe.rs b/packages/core/src/bumpframe.rs index ade95d7db..ab28508af 100644 --- a/packages/core/src/bumpframe.rs +++ b/packages/core/src/bumpframe.rs @@ -1,20 +1,7 @@ -use crate::hooklist::HookList; -use crate::{arena::SharedArena, innerlude::*}; -use appendlist::AppendList; +use crate::innerlude::*; use bumpalo::Bump; -use slotmap::DefaultKey; -use slotmap::SlotMap; -use std::marker::PhantomData; -use std::{ - any::{Any, TypeId}, - cell::{Cell, RefCell}, - collections::{HashMap, HashSet, VecDeque}, - fmt::Debug, - future::Future, - ops::Deref, - pin::Pin, - rc::{Rc, Weak}, -}; + +use std::cell::RefCell; pub struct ActiveFrame { // We use a "generation" for users of contents in the bump frames to ensure their data isn't broken pub generation: RefCell, @@ -33,11 +20,11 @@ impl ActiveFrame { Self::from_frames( BumpFrame { bump: Bump::new(), - head_node: VNode::static_text(""), + head_node: NodeFactory::static_text(""), }, BumpFrame { bump: Bump::new(), - head_node: VNode::static_text(""), + head_node: NodeFactory::static_text(""), }, ) } diff --git a/packages/core/src/context.rs b/packages/core/src/context.rs index 6396d5e02..01b9aaef7 100644 --- a/packages/core/src/context.rs +++ b/packages/core/src/context.rs @@ -270,17 +270,8 @@ Any function prefixed with "use" should not be called conditionally. ) -> VNode<'src> { use futures_util::FutureExt; match fut.now_or_never() { - Some(out) => { - let suspended_cx = SuspendedContext {}; - let nodes = callback(suspended_cx, out); - return nodes; - } - None => { - // we need to register this task - VNode::Suspended { - real: Cell::new(RealDomNode::empty()), - } - } + Some(out) => callback(SuspendedContext {}, out), + None => NodeFactory::suspended(), } } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index ee39f62bf..7498405bb 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -158,50 +158,46 @@ where // // each function call assumes the stack is fresh (empty). pub fn diff_node(&mut self, old_node: &'bump VNode<'bump>, new_node: &'bump VNode<'bump>) { - match (old_node, new_node) { + match (&old_node.kind, &new_node.kind) { // Handle the "sane" cases first. // The rsx and html macros strongly discourage dynamic lists not encapsulated by a "Fragment". // So the sane (and fast!) cases are where the virtual structure stays the same and is easily diffable. - (VNode::Text(old), VNode::Text(new)) => { - if old.is_static { - log::debug!("encountered static text node: {:?}", old.text); - } - + (VNodeKind::Text(old), VNodeKind::Text(new)) => { + let root = old_node.dom_id.get(); if old.text != new.text { - self.dom.push(old.dom_id.get()); + self.dom.push(root); log::debug!("Text has changed {}, {}", old.text, new.text); self.dom.set_text(new.text); self.dom.pop(); } - - new.dom_id.set(old.dom_id.get()); + new_node.dom_id.set(root); } - (VNode::Element(old), VNode::Element(new)) => { + (VNodeKind::Element(old), VNodeKind::Element(new)) => { // If the element type is completely different, the element needs to be re-rendered completely // This is an optimization React makes due to how users structure their code // // In Dioxus, this is less likely to occur unless through a fragment + let root = old_node.dom_id.get(); if new.tag_name != old.tag_name || new.namespace != old.namespace { - self.dom.push(old.dom_id.get()); + self.dom.push(root); let meta = self.create(new_node); self.dom.replace_with(meta.added_to_stack); self.dom.pop(); return; } - let oldid = old.dom_id.get(); - new.dom_id.set(oldid); + new_node.dom_id.set(root); // push it just in case - self.dom.push(oldid); + self.dom.push(root); self.diff_listeners(old.listeners, new.listeners); self.diff_attr(old.attributes, new.attributes, new.namespace); self.diff_children(old.children, new.children); self.dom.pop(); } - (VNode::Component(old), VNode::Component(new)) => { + (VNodeKind::Component(old), VNodeKind::Component(new)) => { log::warn!("diffing components? {:#?}", new.user_fc); if old.user_fc == new.user_fc { // Make sure we're dealing with the same component (by function pointer) @@ -257,7 +253,7 @@ where } } - (VNode::Fragment(old), VNode::Fragment(new)) => { + (VNodeKind::Fragment(old), VNodeKind::Fragment(new)) => { // This is the case where options or direct vnodes might be used. // In this case, it's faster to just skip ahead to their diff if old.children.len() == 1 && new.children.len() == 1 { @@ -278,15 +274,19 @@ where // We also walk the "real node" list to make sure all latent roots are claened up // This covers the case any time a fragment or component shows up with pretty much anything else ( - VNode::Component(_) | VNode::Fragment(_) | VNode::Text(_) | VNode::Element(_), - VNode::Component(_) | VNode::Fragment(_) | VNode::Text(_) | VNode::Element(_), + VNodeKind::Component(_) + | VNodeKind::Fragment(_) + | VNodeKind::Text(_) + | VNodeKind::Element(_), + VNodeKind::Component(_) + | VNodeKind::Fragment(_) + | VNodeKind::Text(_) + | VNodeKind::Element(_), ) => { // Choose the node to use as the placeholder for replacewith - let back_node = match old_node { + let back_node = match old_node.kind { // We special case these two types to avoid allocating the small-vecs - VNode::Element(_) | VNode::Text(_) => old_node - .get_mounted_id(&self.components) - .expect("Element and text always have a real node"), + VNodeKind::Element(_) | VNodeKind::Text(_) => old_node.dom_id.get(), _ => { let mut old_iter = RealChildIterator::new(old_node, &self.components); @@ -314,8 +314,8 @@ where } // TODO - (VNode::Suspended { .. }, _) => todo!(), - (_, VNode::Suspended { .. }) => todo!(), + (VNodeKind::Suspended { .. }, _) => todo!(), + (_, VNodeKind::Suspended { .. }) => todo!(), } } } @@ -351,13 +351,14 @@ where // [... node] fn create(&mut self, node: &'bump VNode<'bump>) -> CreateMeta { log::warn!("Creating node! ... {:#?}", node); - match node { - VNode::Text(text) => { + match &node.kind { + VNodeKind::Text(text) => { let real_id = self.dom.create_text_node(text.text); - text.dom_id.set(real_id); - CreateMeta::new(text.is_static, 1) + todo!() + // text.dom_id.set(real_id); + // CreateMeta::new(text.is_static, 1) } - VNode::Element(el) => { + VNodeKind::Element(el) => { // we have the potential to completely eliminate working on this node in the future(!) // // This can only be done if all of the elements properties (attrs, children, listeners, etc) are static @@ -366,14 +367,14 @@ where let mut is_static: bool = true; let VElement { - key, tag_name, listeners, attributes, children, namespace, - dom_id, - is_static: el_is_static, + static_attrs, + static_children, + static_listeners, } = el; let real_id = if let Some(namespace) = namespace { @@ -381,7 +382,7 @@ where } else { self.dom.create_element(tag_name, None) }; - dom_id.set(real_id); + // dom_id.set(real_id); listeners.iter().enumerate().for_each(|(idx, listener)| { listener.mounted_node.set(real_id); @@ -408,7 +409,7 @@ where // // TODO move over // if children.len() == 1 { - // if let VNode::Text(text) = &children[0] { + // if let VNodeKind::Text(text) = &children[0] { // self.dom.set_text(text.text); // return; // } @@ -428,11 +429,11 @@ where log::debug!("created a dynamic node {:#?}", node); } - el_is_static.set(is_static); + // el_is_static.set(is_static); CreateMeta::new(is_static, 1) } - VNode::Component(vcomponent) => { + VNodeKind::Component(vcomponent) => { log::debug!("Mounting a new component"); let caller = vcomponent.caller.clone(); @@ -493,7 +494,7 @@ where // Fragments are the only nodes that can contain dynamic content (IE through curlies or iterators). // We can never ignore their contents, so the prescence of a fragment indicates that we need always diff them. // Fragments will just put all their nodes onto the stack after creation - VNode::Fragment(frag) => { + VNodeKind::Fragment(frag) => { let mut nodes_added = 0; for child in frag.children.iter().rev() { // different types of nodes will generate different amounts on the stack @@ -508,9 +509,10 @@ where CreateMeta::new(false, nodes_added) } - VNode::Suspended { real } => { - let id = self.dom.create_placeholder(); - real.set(id); + VNodeKind::Suspended => { + todo!(); + // let id = self.dom.create_placeholder(); + // real.set(id); CreateMeta::new(false, 1) } } @@ -620,7 +622,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { // The "fast" path is when the list of attributes name is identical and in the same order // With the Rsx and Html macros, this will almost always be the case 'outer: for new_attr in new { - if new_attr.is_volatile() { + if new_attr.is_volatile { // self.dom.commit_traversal(); self.dom .set_attribute(new_attr.name, new_attr.value, namespace); @@ -675,13 +677,13 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { if new.len() == 1 { match (&old.first(), &new[0]) { - (Some(VNode::Text(old_vtext)), VNode::Text(new_vtext)) - if old_vtext.text == new_vtext.text => - { - // Don't take this fast path... - } + // (Some(VNodeKind::Text(old_vtext)), VNodeKind::Text(new_vtext)) + // if old_vtext.text == new_vtext.text => + // { + // // Don't take this fast path... + // } - // (_, VNode::Text(text)) => { + // (_, VNodeKind::Text(text)) => { // // self.dom.commit_traversal(); // log::debug!("using optimized text set"); // self.dom.set_text(text.text); @@ -701,15 +703,15 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { return; } - let new_is_keyed = new[0].key().is_some(); - let old_is_keyed = old[0].key().is_some(); + let new_is_keyed = new[0].key.is_some(); + let old_is_keyed = old[0].key.is_some(); debug_assert!( - new.iter().all(|n| n.key().is_some() == new_is_keyed), + new.iter().all(|n| n.key.is_some() == new_is_keyed), "all siblings must be keyed or all siblings must be non-keyed" ); debug_assert!( - old.iter().all(|o| o.key().is_some() == old_is_keyed), + old.iter().all(|o| o.key.is_some() == old_is_keyed), "all siblings must be keyed or all siblings must be non-keyed" ); @@ -754,7 +756,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { let mut assert_unique_keys = |children: &'bump [VNode<'bump>]| { keys.clear(); for child in children { - let key = child.key(); + let key = child.key; debug_assert!( key.is_some(), "if any sibling is keyed, all siblings must be keyed" @@ -797,7 +799,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { .iter() .rev() .zip(new[shared_prefix_count..].iter().rev()) - .take_while(|&(old, new)| old.key() == new.key()) + .take_while(|&(old, new)| old.key == new.key) .count(); let old_shared_suffix_start = old.len() - shared_suffix_count; @@ -1259,7 +1261,7 @@ impl<'a, 'bump, Dom: RealDom<'bump>> DiffMachine<'a, 'bump, Dom> { pub fn remove_self_and_next_siblings(&self, old: &[VNode<'bump>]) { // debug_assert!(self.dom.traversal_is_committed()); for child in old { - if let VNode::Component(vcomp) = child { + if let VNodeKind::Component(vcomp) = child.kind { // dom // .create_text_node("placeholder for vcomponent"); @@ -1328,23 +1330,23 @@ impl<'a> Iterator for RealChildIterator<'a> { while returned_node.is_none() { if let Some((count, node)) = self.stack.last_mut() { - match node { + match &node.kind { // We can only exit our looping when we get "real" nodes // This includes fragments and components when they're empty (have a single root) - VNode::Element(_) | VNode::Text(_) => { + VNodeKind::Element(_) | VNodeKind::Text(_) => { // We've recursed INTO an element/text // We need to recurse *out* of it and move forward to the next should_pop = true; - returned_node = node.get_mounted_id(&self.scopes); + returned_node = Some(node.dom_id.get()); } // If we get a fragment we push the next child - VNode::Fragment(frag) => { + VNodeKind::Fragment(frag) => { let subcount = *count as usize; if frag.children.len() == 0 { should_pop = true; - returned_node = node.get_mounted_id(&self.scopes); + returned_node = Some(node.dom_id.get()); } if subcount >= frag.children.len() { @@ -1355,11 +1357,11 @@ impl<'a> Iterator for RealChildIterator<'a> { } // Immediately abort suspended nodes - can't do anything with them yet - // VNode::Suspended => should_pop = true, - VNode::Suspended { real } => todo!(), + // VNodeKind::Suspended => should_pop = true, + VNodeKind::Suspended => todo!(), // For components, we load their root and push them onto the stack - VNode::Component(sc) => { + VNodeKind::Component(sc) => { let scope = self.scopes.try_get(sc.ass_scope.get().unwrap()).unwrap(); // Simply swap the current node on the stack with the root of the component diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index ecf1ee58a..c3cd9b8d6 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -125,7 +125,6 @@ pub mod on { use std::{fmt::Debug, ops::Deref, rc::Rc}; use crate::{ - innerlude::ElementBuilder, innerlude::NodeFactory, innerlude::{Attribute, Listener, RealDomNode, VNode}, }; diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 5af0ddbdb..794bdc4dc 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -19,11 +19,9 @@ pub mod diff; pub mod error; pub mod events; pub mod hooklist; -pub mod nodebuilder; pub mod nodes; pub mod scope; pub mod signals; -pub mod styles; pub mod tasks; pub mod util; pub mod virtual_dom; @@ -36,7 +34,6 @@ pub(crate) mod innerlude { pub use crate::diff::*; pub use crate::error::*; pub use crate::events::*; - pub use crate::nodebuilder::*; pub use crate::nodes::*; pub use crate::scope::*; pub use crate::serialize::*; @@ -46,8 +43,6 @@ pub(crate) mod innerlude { pub type FC

= fn(Context

) -> VNode; - // Re-export the FC macro - pub use crate::nodebuilder as builder; pub use dioxus_core_macro::{html, rsx}; } @@ -61,8 +56,8 @@ pub use crate::{ pub mod prelude { pub use crate::component::{fc_to_builder, Fragment, Properties}; pub use crate::context::Context; + pub use crate::innerlude::DioxusElement; pub use crate::innerlude::{LazyNodes, NodeFactory, FC}; - pub use crate::nodebuilder::DioxusElement; pub use crate::nodes::VNode; pub use crate::VirtualDom; pub use dioxus_core_macro::{format_args_f, html, rsx, Props}; diff --git a/packages/core/src/nodebuilder.rs b/packages/core/src/nodebuilder.rs index b0ecbbfca..8b1378917 100644 --- a/packages/core/src/nodebuilder.rs +++ b/packages/core/src/nodebuilder.rs @@ -1,749 +1 @@ -//! Helpers for building virtual DOM VNodes. -use std::{ - any::Any, - borrow::BorrowMut, - cell::{Cell, RefCell}, - fmt::Arguments, - intrinsics::transmute, - marker::PhantomData, - u128, -}; - -use bumpalo::Bump; - -use crate::{ - events::VirtualEvent, - innerlude::{Properties, RealDomNode, Scope, VComponent, VFragment, VText, FC}, - nodes::{Attribute, Listener, NodeKey, VNode}, -}; - -/// A virtual DOM element builder. -/// -/// Typically constructed with element-specific constructors, eg the `div` -/// function for building `

` elements or the `button` function for building -/// `