From 8c47dfaf78a734460d5335897fb7e68a65bb4144 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 14 Sep 2023 14:08:39 -0500 Subject: [PATCH] clean up attribute parsing --- packages/autofmt/src/element.rs | 56 +++++----- packages/autofmt/src/writer.rs | 32 +++--- packages/rsx-rosetta/src/lib.rs | 26 +++-- packages/rsx/Cargo.toml | 2 + packages/rsx/src/attribute.rs | 165 ++++++++++++++++++++++++++++ packages/rsx/src/element.rs | 187 +++++--------------------------- packages/rsx/src/errors.rs | 4 +- packages/rsx/src/lib.rs | 67 ++++-------- 8 files changed, 278 insertions(+), 261 deletions(-) create mode 100644 packages/rsx/src/attribute.rs diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index dbc336401..971f4447e 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -209,12 +209,25 @@ impl Writer<'_> { Ok(()) } - fn write_attribute(&mut self, attr: &ElementAttrNamed) -> Result { - match &attr.attr { - ElementAttr::AttrText { name, value } => { - write!(self.out, "{name}: {value}", value = ifmt_to_string(value))?; + fn write_attribute_name(&mut self, attr: &ElementAttrName) -> Result { + match attr { + ElementAttrName::BuiltIn(name) => { + write!(self.out, "{}", name)?; } - ElementAttr::AttrExpression { name, value } => { + ElementAttrName::Custom(name) => { + write!(self.out, "\"{}\"", name.to_token_stream())?; + } + } + + Ok(()) + } + + fn write_attribute_value(&mut self, value: &ElementAttrValue) -> Result { + match value { + ElementAttrValue::AttrLiteral(value) => { + write!(self.out, "{value}", value = ifmt_to_string(value))?; + } + ElementAttrValue::AttrExpr(value) => { let out = prettyplease::unparse_expr(value); let mut lines = out.split('\n').peekable(); let first = lines.next().unwrap(); @@ -222,9 +235,9 @@ impl Writer<'_> { // a one-liner for whatever reason // Does not need a new line if lines.peek().is_none() { - write!(self.out, "{name}: {first}")?; + write!(self.out, "{first}")?; } else { - writeln!(self.out, "{name}: {first}")?; + writeln!(self.out, "{first}")?; while let Some(line) = lines.next() { self.out.indented_tab()?; @@ -237,22 +250,7 @@ impl Writer<'_> { } } } - - ElementAttr::CustomAttrText { name, value } => { - write!( - self.out, - "{name}: {value}", - name = name.to_token_stream(), - value = ifmt_to_string(value) - )?; - } - - ElementAttr::CustomAttrExpression { name, value } => { - let out = prettyplease::unparse_expr(value); - write!(self.out, "{}: {}", name.to_token_stream(), out)?; - } - - ElementAttr::EventTokens { name, tokens } => { + ElementAttrValue::EventTokens(tokens) => { let out = self.retrieve_formatted_expr(tokens).to_string(); let mut lines = out.split('\n').peekable(); @@ -261,9 +259,9 @@ impl Writer<'_> { // a one-liner for whatever reason // Does not need a new line if lines.peek().is_none() { - write!(self.out, "{name}: {first}")?; + write!(self.out, "{first}")?; } else { - writeln!(self.out, "{name}: {first}")?; + writeln!(self.out, "{first}")?; while let Some(line) = lines.next() { self.out.indented_tab()?; @@ -281,6 +279,14 @@ impl Writer<'_> { Ok(()) } + fn write_attribute(&mut self, attr: &ElementAttrNamed) -> Result { + self.write_attribute_name(&attr.attr.name)?; + write!(self.out, ": ")?; + self.write_attribute_value(&attr.attr.value)?; + + Ok(()) + } + // make sure the comments are actually relevant to this element. // test by making sure this element is the primary element on this line pub fn current_span_is_primary(&self, location: Span) -> bool { diff --git a/packages/autofmt/src/writer.rs b/packages/autofmt/src/writer.rs index 59f135592..c96e96c93 100644 --- a/packages/autofmt/src/writer.rs +++ b/packages/autofmt/src/writer.rs @@ -1,4 +1,4 @@ -use dioxus_rsx::{BodyNode, ElementAttr, ElementAttrNamed, ForLoop}; +use dioxus_rsx::{BodyNode, ElementAttrNamed, ElementAttrValue, ForLoop}; use proc_macro2::{LineColumn, Span}; use quote::ToTokens; use std::{ @@ -146,20 +146,18 @@ impl<'a> Writer<'a> { } } - total += match &attr.attr { - ElementAttr::AttrText { value, name } => { - ifmt_to_string(value).len() + name.span().line_length() + 6 + total += match &attr.attr.name { + dioxus_rsx::ElementAttrName::BuiltIn(name) => { + let name = name.to_string(); + name.len() } - ElementAttr::AttrExpression { name, value } => { - value.span().line_length() + name.span().line_length() + 6 - } - ElementAttr::CustomAttrText { value, name } => { - ifmt_to_string(value).len() + name.to_token_stream().to_string().len() + 6 - } - ElementAttr::CustomAttrExpression { name, value } => { - name.to_token_stream().to_string().len() + value.span().line_length() + 6 - } - ElementAttr::EventTokens { tokens, name } => { + dioxus_rsx::ElementAttrName::Custom(name) => name.value().len() + 2, + }; + + total += match &attr.attr.value { + ElementAttrValue::AttrLiteral(lit) => ifmt_to_string(lit).len(), + ElementAttrValue::AttrExpr(expr) => expr.span().line_length(), + ElementAttrValue::EventTokens(tokens) => { let location = Location::new(tokens.span().start()); let len = if let std::collections::hash_map::Entry::Vacant(e) = @@ -177,9 +175,11 @@ impl<'a> Writer<'a> { self.cached_formats[&location].len() }; - len + name.span().line_length() + 6 + len } }; + + total += 6; } total @@ -218,7 +218,7 @@ impl<'a> Writer<'a> { } } -trait SpanLength { +pub(crate) trait SpanLength { fn line_length(&self) -> usize; } impl SpanLength for Span { diff --git a/packages/rsx-rosetta/src/lib.rs b/packages/rsx-rosetta/src/lib.rs index 2ceb5f038..19b1d5375 100644 --- a/packages/rsx-rosetta/src/lib.rs +++ b/packages/rsx-rosetta/src/lib.rs @@ -36,9 +36,11 @@ pub fn rsx_node_from_html(node: &Node) -> Option { ElementAttrNamed { el_name: el_name.clone(), - attr: ElementAttr::AttrText { - value: ifmt_from_text(value.as_deref().unwrap_or("false")), - name: ident, + attr: ElementAttr { + value: dioxus_rsx::ElementAttrValue::AttrLiteral(ifmt_from_text( + value.as_deref().unwrap_or("false"), + )), + name: dioxus_rsx::ElementAttrName::BuiltIn(ident), }, } }) @@ -48,9 +50,12 @@ pub fn rsx_node_from_html(node: &Node) -> Option { if !class.is_empty() { attributes.push(ElementAttrNamed { el_name: el_name.clone(), - attr: ElementAttr::AttrText { - name: Ident::new("class", Span::call_site()), - value: ifmt_from_text(&class), + attr: ElementAttr { + name: dioxus_rsx::ElementAttrName::BuiltIn(Ident::new( + "class", + Span::call_site(), + )), + value: dioxus_rsx::ElementAttrValue::AttrLiteral(ifmt_from_text(&class)), }, }); } @@ -58,9 +63,12 @@ pub fn rsx_node_from_html(node: &Node) -> Option { if let Some(id) = &el.id { attributes.push(ElementAttrNamed { el_name: el_name.clone(), - attr: ElementAttr::AttrText { - name: Ident::new("id", Span::call_site()), - value: ifmt_from_text(id), + attr: ElementAttr { + name: dioxus_rsx::ElementAttrName::BuiltIn(Ident::new( + "id", + Span::call_site(), + )), + value: dioxus_rsx::ElementAttrValue::AttrLiteral(ifmt_from_text(id)), }, }); } diff --git a/packages/rsx/Cargo.toml b/packages/rsx/Cargo.toml index bc866acb7..0b7b23e98 100644 --- a/packages/rsx/Cargo.toml +++ b/packages/rsx/Cargo.toml @@ -22,5 +22,7 @@ internment = { version = "0.7.0", optional = true } krates = { version = "0.12.6", optional = true } [features] +default = ["html"] hot_reload = ["krates", "internment"] serde = ["dep:serde"] +html = [] diff --git a/packages/rsx/src/attribute.rs b/packages/rsx/src/attribute.rs new file mode 100644 index 000000000..76cf55a01 --- /dev/null +++ b/packages/rsx/src/attribute.rs @@ -0,0 +1,165 @@ +use std::fmt::{Display, Formatter}; + +use super::*; + +use proc_macro2::{Span, TokenStream as TokenStream2}; +use quote::{quote, ToTokens, TokenStreamExt}; +use syn::{ + parse::{Parse, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + Expr, Ident, LitStr, Result, Token, +}; + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub struct ElementAttrNamed { + pub el_name: ElementName, + pub attr: ElementAttr, +} + +impl ElementAttrNamed { + pub(crate) fn try_combine(&self, other: Self) -> Option { + if self.el_name == other.el_name && self.attr.name == other.attr.name { + if let Some(separator) = todo!() { + return Some(ElementAttrNamed { + el_name: self.el_name.clone(), + attr: ElementAttr { + name: self.attr.name.clone(), + value: self.attr.value.combine(separator, other.attr.value), + }, + }); + } + } + None + } +} + +impl ToTokens for ElementAttrNamed { + fn to_tokens(&self, tokens: &mut TokenStream2) { + let ElementAttrNamed { el_name, attr } = self; + + let ns = |name| match el_name { + ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 }, + ElementName::Custom(_) => quote! { None }, + }; + let volitile = |name| match el_name { + ElementName::Ident(_) => quote! { #el_name::#name.2 }, + ElementName::Custom(_) => quote! { false }, + }; + let attribute = |name: &ElementAttrName| match name { + ElementAttrName::BuiltIn(name) => match el_name { + ElementName::Ident(_) => quote! { #el_name::#name.0 }, + ElementName::Custom(_) => { + let as_string = name.to_string(); + quote!(#as_string) + } + }, + ElementAttrName::Custom(s) => quote! { #s }, + }; + + let attribute = { + match &attr.value { + ElementAttrValue::AttrLiteral(_) | ElementAttrValue::AttrExpr(_) => { + let name = &self.attr.name; + let ns = ns(name); + let volitile = volitile(name); + let attribute = attribute(name); + let value = match &self.attr.value { + ElementAttrValue::AttrLiteral(lit) => quote! { #lit }, + ElementAttrValue::AttrExpr(expr) => quote! { #expr }, + _ => unreachable!(), + }; + quote! { + __cx.attr( + #attribute, + #value, + #ns, + #volitile + ) + } + } + ElementAttrValue::EventTokens(tokens) => match &self.attr.name { + ElementAttrName::BuiltIn(name) => { + quote! { + dioxus_elements::events::#name(__cx, #tokens) + } + } + ElementAttrName::Custom(_) => todo!(), + }, + } + }; + + tokens.append_all(attribute); + } +} + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub struct ElementAttr { + pub name: ElementAttrName, + pub value: ElementAttrValue, +} + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub enum ElementAttrValue { + /// attribute: "value" + AttrLiteral(IfmtInput), + /// attribute: true + AttrExpr(Expr), + /// onclick: move |_| {} + EventTokens(Expr), +} + +impl ElementAttrValue { + fn combine(&self, separator: &str, other: Self) -> Self { + todo!() + } +} + +#[derive(PartialEq, Eq, Clone, Debug, Hash)] +pub enum ElementAttrName { + BuiltIn(Ident), + Custom(LitStr), +} + +impl ElementAttrName { + pub fn start(&self) -> Span { + match self { + ElementAttrName::BuiltIn(i) => i.span(), + ElementAttrName::Custom(s) => s.span(), + } + } +} + +impl ToTokens for ElementAttrName { + fn to_tokens(&self, tokens: &mut TokenStream2) { + match self { + ElementAttrName::BuiltIn(i) => tokens.append_all(quote! { #i }), + ElementAttrName::Custom(s) => tokens.append_all(quote! { #s }), + } + } +} + +impl Display for ElementAttrName { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ElementAttrName::BuiltIn(i) => write!(f, "{}", i), + ElementAttrName::Custom(s) => write!(f, "{}", s.value()), + } + } +} + +impl ElementAttr { + pub fn start(&self) -> Span { + self.name.start() + } + + pub fn is_expr(&self) -> bool { + matches!( + self, + ElementAttr { + value: ElementAttrValue::AttrExpr(_) | ElementAttrValue::EventTokens(_), + .. + } + ) + } +} diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 8f45d91ff..19557edd2 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -8,7 +8,7 @@ use syn::{ parse::{Parse, ParseBuffer, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Error, Expr, Ident, LitStr, Result, Token, + Expr, Ident, LitStr, Result, Token, }; // ======================================= @@ -34,7 +34,6 @@ impl Parse for Element { let mut attributes: Vec = vec![]; let mut children: Vec = vec![]; let mut key = None; - let mut _el_ref = None; // parse fields with commas // break when we don't get this pattern anymore @@ -49,19 +48,20 @@ impl Parse for Element { content.parse::()?; - if content.peek(LitStr) { + let value = if content.peek(LitStr) { let value = content.parse()?; - attributes.push(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr::CustomAttrText { name, value }, - }); + ElementAttrValue::AttrLiteral(value) } else { let value = content.parse::()?; - attributes.push(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr::CustomAttrExpression { name, value }, - }); - } + ElementAttrValue::AttrExpr(value) + }; + attributes.push(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr { + name: ElementAttrName::Custom(name), + value, + }, + }); if content.is_empty() { break; @@ -86,9 +86,9 @@ impl Parse for Element { if name_str.starts_with("on") { attributes.push(ElementAttrNamed { el_name: el_name.clone(), - attr: ElementAttr::EventTokens { - name, - tokens: content.parse()?, + attr: ElementAttr { + name: ElementAttrName::BuiltIn(name), + value: ElementAttrValue::EventTokens(content.parse()?), }, }); } else { @@ -96,26 +96,21 @@ impl Parse for Element { "key" => { key = Some(content.parse()?); } - "classes" => todo!("custom class list not supported yet"), - // "namespace" => todo!("custom namespace not supported yet"), - "node_ref" => { - _el_ref = Some(content.parse::()?); - } _ => { if content.peek(LitStr) { attributes.push(ElementAttrNamed { el_name: el_name.clone(), - attr: ElementAttr::AttrText { - name, - value: content.parse()?, + attr: ElementAttr { + name: ElementAttrName::BuiltIn(name), + value: ElementAttrValue::AttrLiteral(content.parse()?), }, }); } else { attributes.push(ElementAttrNamed { el_name: el_name.clone(), - attr: ElementAttr::AttrExpression { - name, - value: content.parse()?, + attr: ElementAttr { + name: ElementAttrName::BuiltIn(name), + value: ElementAttrValue::AttrExpr(content.parse()?), }, }); } @@ -137,6 +132,9 @@ impl Parse for Element { break; } + // Deduplicate any attributes that can be combined + // For example, if there are two `class` attributes, combine them into one + while !content.is_empty() { if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) { attr_after_element!(content.span()); @@ -177,12 +175,12 @@ impl ToTokens for Element { let listeners = self .attributes .iter() - .filter(|f| matches!(f.attr, ElementAttr::EventTokens { .. })); + .filter(|f| matches!(f.attr.value, ElementAttrValue::EventTokens { .. })); let attr = self .attributes .iter() - .filter(|f| !matches!(f.attr, ElementAttr::EventTokens { .. })); + .filter(|f| !matches!(f.attr.value, ElementAttrValue::EventTokens { .. })); tokens.append_all(quote! { __cx.element( @@ -264,136 +262,3 @@ impl ToTokens for ElementName { } } } - -#[derive(PartialEq, Eq, Clone, Debug, Hash)] -pub enum ElementAttr { - /// `attribute: "value"` - AttrText { name: Ident, value: IfmtInput }, - - /// `attribute: true` - AttrExpression { name: Ident, value: Expr }, - - /// `"attribute": "value"` - CustomAttrText { name: LitStr, value: IfmtInput }, - - /// `"attribute": true` - CustomAttrExpression { name: LitStr, value: Expr }, - - // /// onclick: move |_| {} - // EventClosure { name: Ident, closure: ExprClosure }, - /// onclick: {} - EventTokens { name: Ident, tokens: Expr }, -} - -impl ElementAttr { - pub fn start(&self) -> Span { - match self { - ElementAttr::AttrText { name, .. } => name.span(), - ElementAttr::AttrExpression { name, .. } => name.span(), - ElementAttr::CustomAttrText { name, .. } => name.span(), - ElementAttr::CustomAttrExpression { name, .. } => name.span(), - ElementAttr::EventTokens { name, .. } => name.span(), - } - } - - pub fn is_expr(&self) -> bool { - matches!( - self, - ElementAttr::AttrExpression { .. } - | ElementAttr::CustomAttrExpression { .. } - | ElementAttr::EventTokens { .. } - ) - } -} - -#[derive(PartialEq, Eq, Clone, Debug, Hash)] -pub struct ElementAttrNamed { - pub el_name: ElementName, - pub attr: ElementAttr, -} - -impl ToTokens for ElementAttrNamed { - fn to_tokens(&self, tokens: &mut TokenStream2) { - let ElementAttrNamed { el_name, attr } = self; - - let ns = |name| match el_name { - ElementName::Ident(i) => quote! { dioxus_elements::#i::#name.1 }, - ElementName::Custom(_) => quote! { None }, - }; - let volitile = |name| match el_name { - ElementName::Ident(_) => quote! { #el_name::#name.2 }, - ElementName::Custom(_) => quote! { false }, - }; - let attribute = |name: &Ident| match el_name { - ElementName::Ident(_) => quote! { #el_name::#name.0 }, - ElementName::Custom(_) => { - let as_string = name.to_string(); - quote!(#as_string) - } - }; - - let attribute = match attr { - ElementAttr::AttrText { name, value } => { - let ns = ns(name); - let volitile = volitile(name); - let attribute = attribute(name); - quote! { - __cx.attr( - #attribute, - #value, - #ns, - #volitile - ) - } - } - ElementAttr::AttrExpression { name, value } => { - let ns = ns(name); - let volitile = volitile(name); - let attribute = attribute(name); - quote! { - __cx.attr( - #attribute, - #value, - #ns, - #volitile - ) - } - } - ElementAttr::CustomAttrText { name, value } => { - quote! { - __cx.attr( - #name, - #value, - None, - false - ) - } - } - ElementAttr::CustomAttrExpression { name, value } => { - quote! { - __cx.attr( - #name, - #value, - None, - false - ) - } - } - ElementAttr::EventTokens { name, tokens } => { - quote! { - dioxus_elements::events::#name(__cx, #tokens) - } - } - }; - - tokens.append_all(attribute); - } -} - -// ::dioxus::core::Attribute { -// name: stringify!(#name), -// namespace: None, -// volatile: false, -// mounted_node: Default::default(), -// value: ::dioxus::core::AttributeValue::Text(#value), -// } diff --git a/packages/rsx/src/errors.rs b/packages/rsx/src/errors.rs index f89b28500..6cc34e1fb 100644 --- a/packages/rsx/src/errors.rs +++ b/packages/rsx/src/errors.rs @@ -1,12 +1,12 @@ macro_rules! missing_trailing_comma { ($span:expr) => { - return Err(Error::new($span, "missing trailing comma")); + return Err(syn::Error::new($span, "missing trailing comma")); }; } macro_rules! attr_after_element { ($span:expr) => { - return Err(Error::new($span, "expected element\n = help move the attribute above all the children and text elements")); + return Err(syn::Error::new($span, "expected element\n = help move the attribute above all the children and text elements")); }; } diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index f52ecad0e..ff00fd87e 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -13,6 +13,7 @@ #[macro_use] mod errors; +mod attribute; mod component; mod element; #[cfg(feature = "hot_reload")] @@ -23,6 +24,7 @@ mod node; use std::{fmt::Debug, hash::Hash}; // Re-export the namespaces into each other +pub use attribute::*; pub use component::*; #[cfg(feature = "hot_reload")] use dioxus_core::{Template, TemplateAttribute, TemplateNode}; @@ -307,16 +309,9 @@ impl DynamicMapping { match node { BodyNode::Element(el) => { for attr in el.attributes { - match &attr.attr { - ElementAttr::CustomAttrText { value, .. } - | ElementAttr::AttrText { value, .. } - if value.is_static() => {} - - ElementAttr::AttrExpression { .. } - | ElementAttr::AttrText { .. } - | ElementAttr::CustomAttrText { .. } - | ElementAttr::CustomAttrExpression { .. } - | ElementAttr::EventTokens { .. } => { + match &attr.attr.value { + ElementAttrValue::AttrLiteral(input) if input.is_static() => {} + _ => { self.insert_attribute(attr.attr); } } @@ -365,9 +360,10 @@ impl<'a> DynamicContext<'a> { let mut static_attrs = Vec::new(); for attr in &el.attributes { - match &attr.attr { - ElementAttr::AttrText { name, value } if value.is_static() => { + match &attr.attr.value { + ElementAttrValue::AttrLiteral(value) if value.is_static() => { let value = value.source.as_ref().unwrap(); + let name = &attr.attr.name; let attribute_name_rust = name.to_string(); let (name, namespace) = Ctx::map_attribute(&element_name_rust, &attribute_name_rust) @@ -379,20 +375,7 @@ impl<'a> DynamicContext<'a> { }) } - ElementAttr::CustomAttrText { name, value } if value.is_static() => { - let value = value.source.as_ref().unwrap(); - static_attrs.push(TemplateAttribute::Static { - name: intern(name.value().as_str()), - namespace: None, - value: intern(value.value().as_str()), - }) - } - - ElementAttr::AttrExpression { .. } - | ElementAttr::AttrText { .. } - | ElementAttr::CustomAttrText { .. } - | ElementAttr::CustomAttrExpression { .. } - | ElementAttr::EventTokens { .. } => { + _ => { let idx = match mapping { Some(mapping) => mapping.get_attribute_idx(&attr.attr)?, None => self.dynamic_attributes.len(), @@ -464,10 +447,16 @@ impl<'a> DynamicContext<'a> { ElementName::Ident(i) => quote! { dioxus_elements::#i::#name }, ElementName::Custom(_) => quote! { None }, }; - let static_attrs = el.attributes.iter().map(|attr| match &attr.attr { - ElementAttr::AttrText { name, value } if value.is_static() => { + let static_attrs = el.attributes.iter().map(|attr| match &attr.attr.value { + ElementAttrValue::AttrLiteral(value) if value.is_static() => { let value = value.to_static().unwrap(); - let ns = ns(quote!(#name.1)); + let ns = { + match &attr.attr.name { + ElementAttrName::BuiltIn(name) => ns(quote!(#name.1)), + ElementAttrName::Custom(_) => quote!(None), + } + }; + let name = &attr.attr.name; let name = match el_name { ElementName::Ident(_) => quote! { #el_name::#name.0 }, ElementName::Custom(_) => { @@ -487,25 +476,7 @@ impl<'a> DynamicContext<'a> { } } - ElementAttr::CustomAttrText { name, value } if value.is_static() => { - let value = value.to_static().unwrap(); - quote! { - ::dioxus::core::TemplateAttribute::Static { - name: #name, - namespace: None, - value: #value, - - // todo: we don't diff these so we never apply the volatile flag - // volatile: dioxus_elements::#el_name::#name.2, - } - } - } - - ElementAttr::AttrExpression { .. } - | ElementAttr::AttrText { .. } - | ElementAttr::CustomAttrText { .. } - | ElementAttr::CustomAttrExpression { .. } - | ElementAttr::EventTokens { .. } => { + _ => { let ct = self.dynamic_attributes.len(); self.dynamic_attributes.push(attr); self.attr_paths.push(self.current_path.clone());