diff --git a/packages/core/src/create.rs b/packages/core/src/create.rs index 6fece74c8..ae9f6a7e2 100644 --- a/packages/core/src/create.rs +++ b/packages/core/src/create.rs @@ -1,7 +1,7 @@ use crate::mutations::Mutation; use crate::mutations::Mutation::*; use crate::nodes::VNode; -use crate::nodes::{DynamicNode, DynamicNodeKind, TemplateNode}; +use crate::nodes::{DynamicNode, TemplateNode}; use crate::virtualdom::VirtualDom; use crate::{AttributeValue, TemplateAttribute}; @@ -30,8 +30,8 @@ impl VirtualDom { // Walk the roots backwards, creating nodes and assigning IDs // todo: adjust dynamic nodes to be in the order of roots and then leaves (ie BFS) - let mut dynamic_attrs = template.dynamic_attrs.iter().peekable(); - let mut dynamic_nodes = template.dynamic_nodes.iter().peekable(); + let mut dynamic_attrs = template.template.attr_paths.iter().enumerate().peekable(); + let mut dynamic_nodes = template.template.node_paths.iter().enumerate().peekable(); let mut on_stack = 0; for (root_idx, root) in template.template.roots.iter().enumerate() { @@ -46,7 +46,7 @@ impl VirtualDom { } TemplateNode::Dynamic(id) => { - self.create_dynamic_node(mutations, template, &template.dynamic_nodes[*id]) + self.create_dynamic_node(mutations, template, &template.dynamic_nodes[*id], *id) } TemplateNode::DynamicText { .. } => 1, @@ -56,9 +56,11 @@ impl VirtualDom { // we're on top of a node that has a dynamic attribute for a descendant // Set that attribute now before the stack gets in a weird state - while let Some(attr) = dynamic_attrs.next_if(|a| a.path[0] == root_idx as u8) { + while let Some((idx, path)) = dynamic_attrs.next_if(|(_, p)| p[0] == root_idx as u8) { + let attr = &template.dynamic_attrs[idx]; + if cur_route.is_none() { - cur_route = Some((self.next_element(template), &attr.path[1..])); + cur_route = Some((self.next_element(template), &path[1..])); } // Attach all the elementIDs to the nodes with dynamic content @@ -91,11 +93,12 @@ impl VirtualDom { } // We're on top of a node that has a dynamic child for a descendant - while let Some(node) = dynamic_nodes.next_if(|f| f.path[0] == root_idx as u8) { - let m = self.create_dynamic_node(mutations, template, node); + while let Some((idx, path)) = dynamic_nodes.next_if(|(_, p)| p[0] == root_idx as u8) { + let node = &template.dynamic_nodes[idx]; + let m = self.create_dynamic_node(mutations, template, node, idx); mutations.push(ReplacePlaceholder { m, - path: &node.path[1..], + path: &path[1..], }); } } @@ -151,21 +154,22 @@ impl VirtualDom { mutations: &mut Vec>, template: &'a VNode<'a>, node: &'a DynamicNode<'a>, + idx: usize, ) -> usize { - match &node.kind { - DynamicNodeKind::Text { id, value } => { + match &node { + DynamicNode::Text { id, value } => { let new_id = self.next_element(template); id.set(new_id); mutations.push(HydrateText { id: new_id, - path: &node.path[1..], + path: &template.template.node_paths[idx][1..], value, }); 1 } - DynamicNodeKind::Component { props, .. } => { + DynamicNode::Component { props, .. } => { let id = self.new_scope(*props); let template = self.run_scope(id); @@ -180,7 +184,7 @@ impl VirtualDom { created } - DynamicNodeKind::Fragment { children } => { + DynamicNode::Fragment { children } => { // children .iter() diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index d4fc0f8b6..dec1f405f 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -115,14 +115,16 @@ impl VirtualDom { while let Some((raw_parent, dyn_index)) = index { let parent = unsafe { &mut *raw_parent }; - let path = parent.dynamic_nodes[dyn_index].path; + let path = parent.template.node_paths[dyn_index]; listeners.extend( parent .dynamic_attrs .iter() - .filter(|attr| is_path_ascendant(attr.path, path)) - .filter(|attr| attr.name == event.name), + .enumerate() + .filter(|(idx, attr)| is_path_ascendant(parent.template.node_paths[*idx], path)) + .filter(|(idx, attr)| attr.name == event.name) + .map(|(_, attr)| attr), ); index = parent.parent; diff --git a/packages/core/src/factory.rs b/packages/core/src/factory.rs index 195cf4a0c..2a078c36d 100644 --- a/packages/core/src/factory.rs +++ b/packages/core/src/factory.rs @@ -5,25 +5,24 @@ use bumpalo::Bump; use crate::{ any_props::{AnyProps, VComponentProps}, arena::ElementId, - innerlude::{DynamicNode, DynamicNodeKind}, + innerlude::DynamicNode, Attribute, AttributeValue, Element, LazyNodes, Properties, Scope, ScopeState, VNode, }; impl ScopeState { /// Create some text that's allocated along with the other vnodes - /// pub fn text<'a>(&'a self, args: Arguments) -> DynamicNode<'a> { let (text, _) = self.raw_text(args); - - DynamicNode { - kind: DynamicNodeKind::Text { - id: Cell::new(ElementId(0)), - value: text, - }, - path: &[0], + DynamicNode::Text { + id: Cell::new(ElementId(0)), + value: text, } } + pub fn raw_text_inline<'a>(&'a self, args: Arguments) -> &'a str { + self.raw_text(args).0 + } + pub fn raw_text<'a>(&'a self, args: Arguments) -> (&'a str, bool) { match args.as_str() { Some(static_str) => (static_str, true), @@ -46,11 +45,8 @@ impl ScopeState { bump_vec.push(item.into_dynamic_node(self)); } - DynamicNode { - path: &[0, 0], - kind: crate::innerlude::DynamicNodeKind::Fragment { - children: bump_vec.into_bump_slice(), - }, + DynamicNode::Fragment { + children: bump_vec.into_bump_slice(), } } @@ -60,14 +56,14 @@ impl ScopeState { name: &'static str, val: impl IntoAttributeValue<'a>, namespace: Option<&'static str>, - is_volatile: bool, + volatile: bool, ) -> Attribute<'a> { Attribute { name, namespace, - mounted_element: Cell::new(ElementId(0)), - path: &[0], + volatile, value: val.into_value(self.bump()), + mounted_element: Cell::new(ElementId(0)), } } @@ -94,13 +90,10 @@ impl ScopeState { // self.scope.items.borrow_mut().borrowed_props.push(vcomp); // } - DynamicNode { - path: &[0], - kind: DynamicNodeKind::Component { - name: fn_name, - can_memoize: P::IS_STATIC, - props: detached_dyn, - }, + DynamicNode::Component { + name: fn_name, + can_memoize: P::IS_STATIC, + props: detached_dyn, } } } @@ -116,17 +109,19 @@ impl<'a, 'b> IntoVnode<'a> for LazyNodes<'a, 'b> { } impl<'a, 'b> IntoVnode<'a> for VNode<'a> { - fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a> { + fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> { self } } + impl<'a, 'b> IntoVnode<'a> for &'a VNode<'a> { - fn into_dynamic_node(self, cx: &'a ScopeState) -> VNode<'a> { + fn into_dynamic_node(self, _cx: &'a ScopeState) -> VNode<'a> { VNode { node_id: self.node_id.clone(), parent: self.parent, template: self.template, root_ids: self.root_ids, + key: self.key, dynamic_nodes: self.dynamic_nodes, dynamic_attrs: self.dynamic_attrs, } diff --git a/packages/core/src/garbage.rs b/packages/core/src/garbage.rs index ccaf7c253..30af43f31 100644 --- a/packages/core/src/garbage.rs +++ b/packages/core/src/garbage.rs @@ -1,8 +1,4 @@ -use crate::{ - nodes::{DynamicNodeKind, VNode}, - scopes::ScopeId, - virtualdom::VirtualDom, -}; +use crate::{nodes::VNode, scopes::ScopeId, virtualdom::VirtualDom, DynamicNode}; impl VirtualDom { pub fn drop_scope(&mut self, id: ScopeId) { @@ -16,14 +12,14 @@ impl VirtualDom { pub fn drop_template<'a>(&'a mut self, template: &'a VNode<'a>) { for node in template.dynamic_nodes.iter() { - match &node.kind { - DynamicNodeKind::Text { id, .. } => {} + match node { + DynamicNode::Text { id, .. } => {} - DynamicNodeKind::Component { .. } => { + DynamicNode::Component { .. } => { todo!() } - DynamicNodeKind::Fragment { children } => {} + DynamicNode::Fragment { children } => {} } } } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index fd0de5a12..521f8da00 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -72,7 +72,6 @@ pub use crate::innerlude::{ Attribute, AttributeValue, DynamicNode, - DynamicNodeKind, Element, EventPriority, LazyNodes, @@ -98,9 +97,9 @@ pub use crate::innerlude::{ /// This includes types like [`Scope`], [`Element`], and [`Component`]. pub mod prelude { pub use crate::innerlude::{ - fc_to_builder, Attribute, DynamicNode, DynamicNodeKind, Element, EventPriority, LazyNodes, - NodeFactory, Properties, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, - TemplateNode, UiEvent, VNode, VirtualDom, + fc_to_builder, Attribute, DynamicNode, Element, EventPriority, LazyNodes, NodeFactory, + Properties, Scope, ScopeId, ScopeState, TaskId, Template, TemplateAttribute, TemplateNode, + UiEvent, VNode, VirtualDom, }; } diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index c5c2305f9..39077dd1e 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -12,6 +12,8 @@ pub struct VNode<'a> { // The ID assigned for the root of this template pub node_id: Cell, + pub key: Option<&'a str>, + // When rendered, this template will be linked to its parent manually pub parent: Option<(*mut VNode<'static>, usize)>, @@ -28,6 +30,8 @@ pub struct VNode<'a> { pub struct Template<'a> { pub id: &'a str, pub roots: &'a [TemplateNode<'a>], + pub node_paths: &'a [&'a [u8]], + pub attr_paths: &'a [&'a [u8]], } impl<'a> std::hash::Hash for Template<'a> { @@ -68,12 +72,7 @@ pub enum TemplateNode<'a> { DynamicText(usize), } -pub struct DynamicNode<'a> { - pub path: &'static [u8], - pub kind: DynamicNodeKind<'a>, -} - -pub enum DynamicNodeKind<'a> { +pub enum DynamicNode<'a> { // Anything declared in component form // IE in caps or with underscores Component { @@ -102,10 +101,7 @@ pub enum TemplateAttribute<'a> { namespace: Option<&'static str>, volatile: bool, }, - Dynamic { - name: &'static str, - index: usize, - }, + Dynamic(usize), } pub struct Attribute<'a> { @@ -113,7 +109,7 @@ pub struct Attribute<'a> { pub value: AttributeValue<'a>, pub namespace: Option<&'static str>, pub mounted_element: Cell, - pub path: &'static [u8], + pub volatile: bool, } pub enum AttributeValue<'a> { diff --git a/packages/dioxus/tests/rsx_syntax.rs b/packages/dioxus/tests/rsx_syntax.rs index dd5d81069..37711ee38 100644 --- a/packages/dioxus/tests/rsx_syntax.rs +++ b/packages/dioxus/tests/rsx_syntax.rs @@ -5,11 +5,10 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element { let var = 123; cx.render(rsx! { - div { + div { key: "12345", class: "asd", class: "{asd}", onclick: move |_| {}, - div { "{var}" } div { h1 { "var" } @@ -25,8 +24,9 @@ fn basic_syntax_is_a_template(cx: Scope) -> Element { } fn basic_template(cx: Scope) -> Element { + let val = 123; cx.render(rsx! { - div { + div { class: "{val}", class: "{val}", class: "{val}", class: "{val}", (0..2).map(|i| rsx! { div { "asd {i}" } }) basic_child { } } diff --git a/packages/rsx/src/component.rs b/packages/rsx/src/component.rs index ef7b0983b..00fdd2858 100644 --- a/packages/rsx/src/component.rs +++ b/packages/rsx/src/component.rs @@ -55,6 +55,18 @@ impl Component { Ok(()) } + + pub fn key(&self) -> Option<&IfmtInput> { + match self + .fields + .iter() + .find(|f| f.name.to_string() == "key") + .map(|f| &f.content) + { + Some(ContentField::Formatted(fmt)) => Some(fmt), + _ => None, + } + } } impl Parse for Component { diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 99c7f34b2..ea255d19f 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -99,6 +99,18 @@ impl<'a> ToTokens for TemplateRenderer<'a> { dynamic_nodes: vec![], dynamic_attributes: vec![], current_path: vec![], + attr_paths: vec![], + node_paths: vec![], + }; + + let key = match self.roots.get(0) { + Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(), + Some(BodyNode::Component(comp)) if self.roots.len() == 1 => comp.key().cloned(), + _ => None, + }; + let key_tokens = match key { + Some(tok) => quote! { Some( __cx.raw_text_inline(#tok) ) }, + None => quote! { None }, }; let root_printer = self.roots.iter().enumerate().map(|(idx, root)| { @@ -112,15 +124,27 @@ impl<'a> ToTokens for TemplateRenderer<'a> { let roots = quote! { #( #root_printer ),* }; let node_printer = &context.dynamic_nodes; let dyn_attr_printer = &context.dynamic_attributes; + let node_paths = context + .node_paths + .iter() + .map(|items| quote!(&[#(#items),*])); + + let attr_paths = context + .attr_paths + .iter() + .map(|items| quote!(&[#(#items),*])); out_tokens.append_all(quote! { static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template { id: ::dioxus::core::get_line_num!(), - roots: &[ #roots ] + roots: &[ #roots ], + node_paths: &[ #(#node_paths),* ], + attr_paths: &[ #(#attr_paths),* ], }; ::dioxus::core::VNode { node_id: Default::default(), parent: None, + key: #key_tokens, template: TEMPLATE, root_ids: __cx.bump().alloc([]), dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]), @@ -135,6 +159,9 @@ pub struct DynamicContext<'a> { dynamic_nodes: Vec<&'a BodyNode>, dynamic_attributes: Vec<&'a ElementAttrNamed>, current_path: Vec, + + node_paths: Vec>, + attr_paths: Vec>, } impl<'a> DynamicContext<'a> { @@ -180,22 +207,12 @@ impl<'a> DynamicContext<'a> { ElementAttr::AttrExpression { .. } | ElementAttr::AttrText { .. } | ElementAttr::CustomAttrText { .. } - | ElementAttr::CustomAttrExpression { .. } => { + | ElementAttr::CustomAttrExpression { .. } + | ElementAttr::EventTokens { .. } => { let ct = self.dynamic_attributes.len(); self.dynamic_attributes.push(attr); - Some(quote! { ::dioxus::core::TemplateAttribute::Dynamic { - name: "asd", - index: #ct - } }) - } - - ElementAttr::EventTokens { .. } => { - let ct = self.dynamic_attributes.len(); - self.dynamic_attributes.push(attr); - Some(quote! { ::dioxus::core::TemplateAttribute::Dynamic { - name: "asd", - index: #ct - } }) + self.attr_paths.push(self.current_path.clone()); + Some(quote! { ::dioxus::core::TemplateAttribute::Dynamic(#ct) }) } }); @@ -228,6 +245,7 @@ impl<'a> DynamicContext<'a> { BodyNode::RawExpr(_) | BodyNode::Text(_) | BodyNode::Component(_) => { let ct = self.dynamic_nodes.len(); self.dynamic_nodes.push(root); + self.node_paths.push(self.current_path.clone()); quote! { ::dioxus::core::TemplateNode::Dynamic(#ct) } } } diff --git a/packages/ssr/src/template.rs b/packages/ssr/src/template.rs index 3a5cc8a98..3114bd815 100644 --- a/packages/ssr/src/template.rs +++ b/packages/ssr/src/template.rs @@ -72,7 +72,7 @@ impl StringCache { TemplateAttribute::Static { name, value, .. } => { write!(chain, " {}=\"{}\"", name, value)?; } - TemplateAttribute::Dynamic { index, .. } => { + TemplateAttribute::Dynamic(index) => { chain.segments.push(Segment::Attr(*index)) } } @@ -126,18 +126,18 @@ impl SsrRender { _ => {} }; } - Segment::Node(idx) => match &template.dynamic_nodes[*idx].kind { - DynamicNodeKind::Text { value, .. } => { + Segment::Node(idx) => match &template.dynamic_nodes[*idx] { + DynamicNode::Text { value, .. } => { // todo: escape the text write!(buf, "{}", value)? } - DynamicNodeKind::Fragment { children } => { + DynamicNode::Fragment { children } => { for child in *children { self.render_template(buf, child)?; } // } - DynamicNodeKind::Component { .. } => { + DynamicNode::Component { .. } => { // } }, @@ -194,10 +194,7 @@ fn children_processes_properly() { render! { div { ChildWithChildren { - p { - "{d}" - "hii" - } + p { "{d}" "hii" } } } }