feat: keys

This commit is contained in:
Jonathan Kelley 2022-11-03 01:24:20 -07:00
parent dddbcfd5e2
commit 584504feb7
10 changed files with 113 additions and 94 deletions

View file

@ -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<Mutation<'a>>,
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()

View file

@ -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;

View file

@ -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,
}

View file

@ -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 } => {}
}
}
}

View file

@ -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,
};
}

View file

@ -12,6 +12,8 @@ pub struct VNode<'a> {
// The ID assigned for the root of this template
pub node_id: Cell<ElementId>,
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<ElementId>,
pub path: &'static [u8],
pub volatile: bool,
}
pub enum AttributeValue<'a> {

View file

@ -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 { }
}

View file

@ -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 {

View file

@ -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<u8>,
node_paths: Vec<Vec<u8>>,
attr_paths: Vec<Vec<u8>>,
}
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) }
}
}

View file

@ -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" }
}
}
}