|
@@ -1,1085 +1,1064 @@
|
|
|
-use dioxus_core::{
|
|
|
- OwnedAttributeValue, OwnedPathSeg, OwnedTraverse, TemplateAttributeValue, TemplateNodeId,
|
|
|
- TextTemplate, TextTemplateSegment, UpdateOp,
|
|
|
-};
|
|
|
use proc_macro2::TokenStream;
|
|
|
use quote::TokenStreamExt;
|
|
|
use quote::{quote, ToTokens};
|
|
|
use syn::{Expr, Ident, LitStr};
|
|
|
|
|
|
-#[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
-pub fn try_parse_template(
|
|
|
- rsx: &str,
|
|
|
- location: OwnedCodeLocation,
|
|
|
- previous_template: Option<DynamicTemplateContextBuilder>,
|
|
|
-) -> Result<(OwnedTemplate, DynamicTemplateContextBuilder), Error> {
|
|
|
- use crate::CallBody;
|
|
|
-
|
|
|
- let call_body: CallBody =
|
|
|
- syn::parse_str(rsx).map_err(|e| Error::ParseError(ParseError::new(e, location.clone())))?;
|
|
|
- let mut template_builder = TemplateBuilder::from_roots_always(call_body.roots);
|
|
|
- if let Some(prev) = previous_template {
|
|
|
- template_builder = template_builder
|
|
|
- .try_switch_dynamic_context(prev)
|
|
|
- .ok_or_else(|| {
|
|
|
- Error::RecompileRequiredError(RecompileReason::Variable(
|
|
|
- "dynamic context updated".to_string(),
|
|
|
- ))
|
|
|
- })?;
|
|
|
- }
|
|
|
- let dyn_ctx = template_builder.dynamic_context.clone();
|
|
|
- Ok((template_builder.try_into_owned(&location)?, dyn_ctx))
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
-use hot_reload_imports::*;
|
|
|
-#[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
-mod hot_reload_imports {
|
|
|
- pub use crate::{
|
|
|
- attributes::attrbute_to_static_str,
|
|
|
- elements::element_to_static_str,
|
|
|
- error::{Error, ParseError, RecompileReason},
|
|
|
- };
|
|
|
- pub use dioxus_core::prelude::OwnedTemplate;
|
|
|
- pub use dioxus_core::{
|
|
|
- AttributeDiscription, OwnedAttributeValue, OwnedCodeLocation, OwnedDynamicNodeMapping,
|
|
|
- OwnedTemplateNode, Template, TemplateAttribute, TemplateAttributeValue, TemplateElement,
|
|
|
- TemplateNodeId, TemplateNodeType, TextTemplate, TextTemplateSegment,
|
|
|
- };
|
|
|
- pub use std::collections::HashMap;
|
|
|
-}
|
|
|
-use crate::{BodyNode, ElementAttr, FormattedSegment, Segment};
|
|
|
-
|
|
|
-struct TemplateElementBuilder {
|
|
|
- tag: Ident,
|
|
|
- attributes: Vec<TemplateAttributeBuilder>,
|
|
|
- children: Vec<TemplateNodeId>,
|
|
|
- listeners: Vec<usize>,
|
|
|
- locally_static: bool,
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
-type OwndedTemplateElement = TemplateElement<
|
|
|
- Vec<TemplateAttribute<OwnedAttributeValue>>,
|
|
|
- OwnedAttributeValue,
|
|
|
- Vec<TemplateNodeId>,
|
|
|
- Vec<usize>,
|
|
|
->;
|
|
|
-
|
|
|
-impl TemplateElementBuilder {
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwndedTemplateElement, Error> {
|
|
|
- let Self {
|
|
|
- tag,
|
|
|
- attributes,
|
|
|
- children,
|
|
|
- listeners,
|
|
|
- ..
|
|
|
- } = self;
|
|
|
- let (element_tag, element_ns) =
|
|
|
- element_to_static_str(&tag.to_string()).ok_or_else(|| {
|
|
|
- Error::ParseError(ParseError::new(
|
|
|
- syn::Error::new(tag.span(), format!("unknown element: {}", tag)),
|
|
|
- location.clone(),
|
|
|
- ))
|
|
|
- })?;
|
|
|
-
|
|
|
- let mut owned_attributes = Vec::new();
|
|
|
- for a in attributes {
|
|
|
- owned_attributes.push(a.try_into_owned(location, element_tag, element_ns)?);
|
|
|
- }
|
|
|
-
|
|
|
- Ok(TemplateElement::new(
|
|
|
- element_tag,
|
|
|
- element_ns,
|
|
|
- owned_attributes,
|
|
|
- children,
|
|
|
- listeners,
|
|
|
- ))
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl ToTokens for TemplateElementBuilder {
|
|
|
- fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
- let Self {
|
|
|
- tag,
|
|
|
- attributes,
|
|
|
- children,
|
|
|
- listeners,
|
|
|
- ..
|
|
|
- } = self;
|
|
|
- let children = children.iter().map(|id| {
|
|
|
- let raw = id.0;
|
|
|
- quote! {TemplateNodeId(#raw)}
|
|
|
- });
|
|
|
- tokens.append_all(quote! {
|
|
|
- TemplateElement::new(
|
|
|
- dioxus_elements::#tag::TAG_NAME,
|
|
|
- dioxus_elements::#tag::NAME_SPACE,
|
|
|
- &[#(#attributes),*],
|
|
|
- &[#(#children),*],
|
|
|
- &[#(#listeners),*],
|
|
|
- )
|
|
|
- })
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-enum AttributeName {
|
|
|
- Ident(Ident),
|
|
|
- Str(LitStr),
|
|
|
-}
|
|
|
-
|
|
|
-struct TemplateAttributeBuilder {
|
|
|
- element_tag: Ident,
|
|
|
- name: AttributeName,
|
|
|
- value: TemplateAttributeValue<OwnedAttributeValue>,
|
|
|
-}
|
|
|
-
|
|
|
-impl TemplateAttributeBuilder {
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- fn try_into_owned(
|
|
|
- self,
|
|
|
- location: &OwnedCodeLocation,
|
|
|
- element_tag: &'static str,
|
|
|
- element_ns: Option<&'static str>,
|
|
|
- ) -> Result<TemplateAttribute<OwnedAttributeValue>, Error> {
|
|
|
- let Self { name, value, .. } = self;
|
|
|
- let (name, span, literal) = match name {
|
|
|
- AttributeName::Ident(name) => (name.to_string(), name.span(), false),
|
|
|
- AttributeName::Str(name) => (name.value(), name.span(), true),
|
|
|
- };
|
|
|
- let (name, namespace, volatile) = attrbute_to_static_str(&name, element_tag, element_ns)
|
|
|
- .ok_or_else(|| {
|
|
|
- if literal {
|
|
|
- // literals will be captured when a full recompile is triggered
|
|
|
- Error::RecompileRequiredError(RecompileReason::Attribute(name.to_string()))
|
|
|
- } else {
|
|
|
- Error::ParseError(ParseError::new(
|
|
|
- syn::Error::new(span, format!("unknown attribute: {}", name)),
|
|
|
- location.clone(),
|
|
|
- ))
|
|
|
- }
|
|
|
- })?;
|
|
|
- let attribute = AttributeDiscription {
|
|
|
- name,
|
|
|
- namespace,
|
|
|
- volatile,
|
|
|
- };
|
|
|
- Ok(TemplateAttribute { value, attribute })
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl ToTokens for TemplateAttributeBuilder {
|
|
|
- fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
- let Self {
|
|
|
- element_tag,
|
|
|
- name,
|
|
|
- value,
|
|
|
- } = self;
|
|
|
- let value = match value {
|
|
|
- TemplateAttributeValue::Static(val) => {
|
|
|
- let val = match val {
|
|
|
- OwnedAttributeValue::Text(txt) => quote! {StaticAttributeValue::Text(#txt)},
|
|
|
- OwnedAttributeValue::Float32(f) => quote! {StaticAttributeValue::Float32(#f)},
|
|
|
- OwnedAttributeValue::Float64(f) => quote! {StaticAttributeValue::Float64(#f)},
|
|
|
- OwnedAttributeValue::Int32(i) => quote! {StaticAttributeValue::Int32(#i)},
|
|
|
- OwnedAttributeValue::Int64(i) => quote! {StaticAttributeValue::Int64(#i)},
|
|
|
- OwnedAttributeValue::Uint32(u) => quote! {StaticAttributeValue::Uint32(#u)},
|
|
|
- OwnedAttributeValue::Uint64(u) => quote! {StaticAttributeValue::Uint64(#u)},
|
|
|
- OwnedAttributeValue::Bool(b) => quote! {StaticAttributeValue::Bool(#b)},
|
|
|
- OwnedAttributeValue::Vec3Float(f1, f2, f3) => {
|
|
|
- quote! {StaticAttributeValue::Vec3Float(#f1, #f2, #f3)}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Vec3Int(f1, f2, f3) => {
|
|
|
- quote! {StaticAttributeValue::Vec3Int(#f1, #f2, #f3)}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Vec3Uint(f1, f2, f3) => {
|
|
|
- quote! {StaticAttributeValue::Vec3Uint(#f1, #f2, #f3)}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Vec4Float(f1, f2, f3, f4) => {
|
|
|
- quote! {StaticAttributeValue::Vec4Float(#f1, #f2, #f3, #f4)}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Vec4Int(f1, f2, f3, f4) => {
|
|
|
- quote! {StaticAttributeValue::Vec4Int(#f1, #f2, #f3, #f4)}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Vec4Uint(f1, f2, f3, f4) => {
|
|
|
- quote! {StaticAttributeValue::Vec4Uint(#f1, #f2, #f3, #f4)}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Bytes(b) => {
|
|
|
- quote! {StaticAttributeValue::Bytes(&[#(#b),*])}
|
|
|
- }
|
|
|
- OwnedAttributeValue::Any(_) => todo!(),
|
|
|
- };
|
|
|
- quote! {TemplateAttributeValue::Static(#val)}
|
|
|
- }
|
|
|
- TemplateAttributeValue::Dynamic(idx) => quote! {TemplateAttributeValue::Dynamic(#idx)},
|
|
|
- };
|
|
|
- match name {
|
|
|
- AttributeName::Ident(name) => tokens.append_all(quote! {
|
|
|
- TemplateAttribute{
|
|
|
- attribute: dioxus_elements::#element_tag::#name,
|
|
|
- value: #value,
|
|
|
- }
|
|
|
- }),
|
|
|
- AttributeName::Str(lit) => tokens.append_all(quote! {
|
|
|
- TemplateAttribute{
|
|
|
- attribute: dioxus::prelude::AttributeDiscription{
|
|
|
- name: #lit,
|
|
|
- namespace: None,
|
|
|
- volatile: false
|
|
|
- },
|
|
|
- value: #value,
|
|
|
- }
|
|
|
- }),
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-enum TemplateNodeTypeBuilder {
|
|
|
- Element(TemplateElementBuilder),
|
|
|
- Text(TextTemplate<Vec<TextTemplateSegment<String>>, String>),
|
|
|
- DynamicNode(usize),
|
|
|
-}
|
|
|
-
|
|
|
-#[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
-type OwnedTemplateNodeType = TemplateNodeType<
|
|
|
- Vec<TemplateAttribute<OwnedAttributeValue>>,
|
|
|
- OwnedAttributeValue,
|
|
|
- Vec<TemplateNodeId>,
|
|
|
- Vec<usize>,
|
|
|
- Vec<TextTemplateSegment<String>>,
|
|
|
- String,
|
|
|
->;
|
|
|
-
|
|
|
-impl TemplateNodeTypeBuilder {
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNodeType, Error> {
|
|
|
- match self {
|
|
|
- TemplateNodeTypeBuilder::Element(el) => {
|
|
|
- Ok(TemplateNodeType::Element(el.try_into_owned(location)?))
|
|
|
- }
|
|
|
- TemplateNodeTypeBuilder::Text(txt) => Ok(TemplateNodeType::Text(txt)),
|
|
|
- TemplateNodeTypeBuilder::DynamicNode(idx) => Ok(TemplateNodeType::DynamicNode(idx)),
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl ToTokens for TemplateNodeTypeBuilder {
|
|
|
- fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
- match self {
|
|
|
- TemplateNodeTypeBuilder::Element(el) => tokens.append_all(quote! {
|
|
|
- TemplateNodeType::Element(#el)
|
|
|
- }),
|
|
|
- TemplateNodeTypeBuilder::Text(txt) => {
|
|
|
- let mut length = 0;
|
|
|
-
|
|
|
- let segments = txt.segments.iter().map(|seg| match seg {
|
|
|
- TextTemplateSegment::Static(s) => {
|
|
|
- length += s.len();
|
|
|
- quote!(TextTemplateSegment::Static(#s))
|
|
|
- }
|
|
|
- TextTemplateSegment::Dynamic(idx) => quote!(TextTemplateSegment::Dynamic(#idx)),
|
|
|
- });
|
|
|
-
|
|
|
- tokens.append_all(quote! {
|
|
|
- TemplateNodeType::Text(TextTemplate::new(&[#(#segments),*], #length))
|
|
|
- });
|
|
|
- }
|
|
|
- TemplateNodeTypeBuilder::DynamicNode(idx) => tokens.append_all(quote! {
|
|
|
- TemplateNodeType::DynamicNode(#idx)
|
|
|
- }),
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-struct TemplateNodeBuilder {
|
|
|
- id: TemplateNodeId,
|
|
|
- depth: usize,
|
|
|
- parent: Option<TemplateNodeId>,
|
|
|
- node_type: TemplateNodeTypeBuilder,
|
|
|
- fully_static: bool,
|
|
|
-}
|
|
|
-
|
|
|
-impl TemplateNodeBuilder {
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNode, Error> {
|
|
|
- let TemplateNodeBuilder {
|
|
|
- id,
|
|
|
- node_type,
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- ..
|
|
|
- } = self;
|
|
|
- let node_type = node_type.try_into_owned(location)?;
|
|
|
- Ok(OwnedTemplateNode {
|
|
|
- id,
|
|
|
- node_type,
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
- let Self {
|
|
|
- id,
|
|
|
- node_type,
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- ..
|
|
|
- } = self;
|
|
|
- let raw_id = id.0;
|
|
|
- let parent = match parent {
|
|
|
- Some(id) => {
|
|
|
- let id = id.0;
|
|
|
- quote! {Some(TemplateNodeId(#id))}
|
|
|
- }
|
|
|
- None => quote! {None},
|
|
|
- };
|
|
|
-
|
|
|
- tokens.append_all(quote! {
|
|
|
- TemplateNode {
|
|
|
- id: TemplateNodeId(#raw_id),
|
|
|
- node_type: #node_type,
|
|
|
- parent: #parent,
|
|
|
- depth: #depth,
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#[derive(Default)]
|
|
|
-pub struct TemplateBuilder {
|
|
|
- nodes: Vec<TemplateNodeBuilder>,
|
|
|
- root_nodes: Vec<TemplateNodeId>,
|
|
|
- dynamic_context: DynamicTemplateContextBuilder,
|
|
|
-}
|
|
|
-
|
|
|
-impl TemplateBuilder {
|
|
|
- /// Create a template builder from nodes if it would improve performance to do so.
|
|
|
- pub fn from_roots(roots: Vec<BodyNode>) -> Option<Self> {
|
|
|
- let mut builder = Self::default();
|
|
|
-
|
|
|
- for (i, root) in roots.into_iter().enumerate() {
|
|
|
- let id = builder.build_node(root, None, vec![i], 0);
|
|
|
- builder.root_nodes.push(id);
|
|
|
- }
|
|
|
-
|
|
|
- // only build a template if there is at least one static node
|
|
|
- if builder
|
|
|
- .nodes
|
|
|
- .iter()
|
|
|
- .all(|r| matches!(&r.node_type, TemplateNodeTypeBuilder::DynamicNode(_)))
|
|
|
- {
|
|
|
- None
|
|
|
- } else {
|
|
|
- Some(builder)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// Create a template builder from nodes regardless of performance.
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- fn from_roots_always(roots: Vec<BodyNode>) -> Self {
|
|
|
- let mut builder = Self::default();
|
|
|
-
|
|
|
- for (i, root) in roots.into_iter().enumerate() {
|
|
|
- let id = builder.build_node(root, None, vec![i], 0);
|
|
|
- builder.root_nodes.push(id);
|
|
|
- }
|
|
|
-
|
|
|
- builder
|
|
|
- }
|
|
|
-
|
|
|
- fn build_node(
|
|
|
- &mut self,
|
|
|
- node: BodyNode,
|
|
|
- parent: Option<TemplateNodeId>,
|
|
|
- path: Vec<usize>,
|
|
|
- depth: usize,
|
|
|
- ) -> TemplateNodeId {
|
|
|
- let id = TemplateNodeId(self.nodes.len());
|
|
|
- match node {
|
|
|
- BodyNode::Element(el) => {
|
|
|
- let mut locally_static = true;
|
|
|
- let mut attributes = Vec::new();
|
|
|
- let mut listeners = Vec::new();
|
|
|
- for attr in el.attributes {
|
|
|
- match attr.attr {
|
|
|
- ElementAttr::AttrText { name, value } => {
|
|
|
- if let Some(static_value) = value.to_static() {
|
|
|
- attributes.push(TemplateAttributeBuilder {
|
|
|
- element_tag: el.name.clone(),
|
|
|
- name: AttributeName::Ident(name),
|
|
|
- value: TemplateAttributeValue::Static(
|
|
|
- OwnedAttributeValue::Text(static_value),
|
|
|
- ),
|
|
|
- })
|
|
|
- } else {
|
|
|
- locally_static = false;
|
|
|
- attributes.push(TemplateAttributeBuilder {
|
|
|
- element_tag: el.name.clone(),
|
|
|
- name: AttributeName::Ident(name),
|
|
|
- value: TemplateAttributeValue::Dynamic(
|
|
|
- self.dynamic_context.add_attr(quote!(#value)),
|
|
|
- ),
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- ElementAttr::CustomAttrText { name, value } => {
|
|
|
- if let Some(static_value) = value.to_static() {
|
|
|
- attributes.push(TemplateAttributeBuilder {
|
|
|
- element_tag: el.name.clone(),
|
|
|
- name: AttributeName::Str(name),
|
|
|
- value: TemplateAttributeValue::Static(
|
|
|
- OwnedAttributeValue::Text(static_value),
|
|
|
- ),
|
|
|
- })
|
|
|
- } else {
|
|
|
- locally_static = false;
|
|
|
- attributes.push(TemplateAttributeBuilder {
|
|
|
- element_tag: el.name.clone(),
|
|
|
- name: AttributeName::Str(name),
|
|
|
- value: TemplateAttributeValue::Dynamic(
|
|
|
- self.dynamic_context.add_attr(quote!(#value)),
|
|
|
- ),
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
- ElementAttr::AttrExpression { name, value } => {
|
|
|
- locally_static = false;
|
|
|
- attributes.push(TemplateAttributeBuilder {
|
|
|
- element_tag: el.name.clone(),
|
|
|
- name: AttributeName::Ident(name),
|
|
|
- value: TemplateAttributeValue::Dynamic(
|
|
|
- self.dynamic_context.add_attr(quote!(#value)),
|
|
|
- ),
|
|
|
- })
|
|
|
- }
|
|
|
- ElementAttr::CustomAttrExpression { name, value } => {
|
|
|
- locally_static = false;
|
|
|
- attributes.push(TemplateAttributeBuilder {
|
|
|
- element_tag: el.name.clone(),
|
|
|
- name: AttributeName::Str(name),
|
|
|
- value: TemplateAttributeValue::Dynamic(
|
|
|
- self.dynamic_context.add_attr(quote!(#value)),
|
|
|
- ),
|
|
|
- })
|
|
|
- }
|
|
|
- ElementAttr::EventTokens { name, tokens } => {
|
|
|
- locally_static = false;
|
|
|
- listeners.push(self.dynamic_context.add_listener(name, tokens))
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if let Some(key) = el.key {
|
|
|
- self.dynamic_context.add_key(quote!(
|
|
|
- dioxus::core::exports::bumpalo::format!(in __bump, "{}", #key)
|
|
|
- .into_bump_str()
|
|
|
- ));
|
|
|
- }
|
|
|
- self.nodes.push(TemplateNodeBuilder {
|
|
|
- id,
|
|
|
- node_type: TemplateNodeTypeBuilder::Element(TemplateElementBuilder {
|
|
|
- tag: el.name,
|
|
|
- attributes,
|
|
|
- children: Vec::new(),
|
|
|
- listeners,
|
|
|
- locally_static,
|
|
|
- }),
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- fully_static: false,
|
|
|
- });
|
|
|
- let children: Vec<_> = el
|
|
|
- .children
|
|
|
- .into_iter()
|
|
|
- .enumerate()
|
|
|
- .map(|(i, child)| {
|
|
|
- let mut new_path = path.clone();
|
|
|
- new_path.push(i);
|
|
|
- self.build_node(child, Some(id), new_path, depth + 1)
|
|
|
- })
|
|
|
- .collect();
|
|
|
- let children_fully_static = children.iter().all(|c| self.nodes[c.0].fully_static);
|
|
|
- let parent = &mut self.nodes[id.0];
|
|
|
- parent.fully_static = locally_static && children_fully_static;
|
|
|
- if let TemplateNodeTypeBuilder::Element(element) = &mut parent.node_type {
|
|
|
- element.children = children;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- BodyNode::Component(comp) => {
|
|
|
- self.nodes.push(TemplateNodeBuilder {
|
|
|
- id,
|
|
|
- node_type: TemplateNodeTypeBuilder::DynamicNode(
|
|
|
- self.dynamic_context.add_node(BodyNode::Component(comp)),
|
|
|
- ),
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- fully_static: false,
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- BodyNode::Text(txt) => {
|
|
|
- let mut segments = Vec::new();
|
|
|
- let mut length = 0;
|
|
|
- let mut fully_static = true;
|
|
|
-
|
|
|
- for segment in txt.segments {
|
|
|
- segments.push(match segment {
|
|
|
- Segment::Literal(lit) => {
|
|
|
- length += lit.len();
|
|
|
- TextTemplateSegment::Static(lit)
|
|
|
- }
|
|
|
- Segment::Formatted(fmted) => {
|
|
|
- fully_static = false;
|
|
|
- TextTemplateSegment::Dynamic(self.dynamic_context.add_text(fmted))
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- self.nodes.push(TemplateNodeBuilder {
|
|
|
- id,
|
|
|
- node_type: TemplateNodeTypeBuilder::Text(TextTemplate::new(segments, length)),
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- fully_static,
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- BodyNode::RawExpr(expr) => {
|
|
|
- self.nodes.push(TemplateNodeBuilder {
|
|
|
- id,
|
|
|
- node_type: TemplateNodeTypeBuilder::DynamicNode(
|
|
|
- self.dynamic_context.add_node(BodyNode::RawExpr(expr)),
|
|
|
- ),
|
|
|
- parent,
|
|
|
- depth,
|
|
|
- fully_static: false,
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- id
|
|
|
- }
|
|
|
-
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- pub fn try_switch_dynamic_context(
|
|
|
- mut self,
|
|
|
- dynamic_context: DynamicTemplateContextBuilder,
|
|
|
- ) -> Option<Self> {
|
|
|
- let attribute_mapping: HashMap<String, usize> = dynamic_context
|
|
|
- .attributes
|
|
|
- .iter()
|
|
|
- .enumerate()
|
|
|
- .map(|(i, ts)| (ts.to_string(), i))
|
|
|
- .collect();
|
|
|
- let text_mapping: HashMap<String, usize> = dynamic_context
|
|
|
- .text
|
|
|
- .iter()
|
|
|
- .enumerate()
|
|
|
- .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
|
|
|
- .collect();
|
|
|
- let listener_mapping: HashMap<(String, Expr), usize> = dynamic_context
|
|
|
- .listeners
|
|
|
- .iter()
|
|
|
- .enumerate()
|
|
|
- .map(|(i, ts)| (ts.clone(), i))
|
|
|
- .collect();
|
|
|
- let node_mapping: HashMap<String, usize> = dynamic_context
|
|
|
- .nodes
|
|
|
- .iter()
|
|
|
- .enumerate()
|
|
|
- .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
|
|
|
- .collect();
|
|
|
-
|
|
|
- for node in &mut self.nodes {
|
|
|
- match &mut node.node_type {
|
|
|
- TemplateNodeTypeBuilder::Element(element) => {
|
|
|
- for listener in &mut element.listeners {
|
|
|
- *listener =
|
|
|
- *listener_mapping.get(&self.dynamic_context.listeners[*listener])?;
|
|
|
- }
|
|
|
- for attribute in &mut element.attributes {
|
|
|
- if let TemplateAttributeValue::Dynamic(idx) = &mut attribute.value {
|
|
|
- *idx = *attribute_mapping
|
|
|
- .get(&self.dynamic_context.attributes[*idx].to_string())?;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- TemplateNodeTypeBuilder::Text(txt) => {
|
|
|
- for seg in &mut txt.segments {
|
|
|
- if let TextTemplateSegment::Dynamic(idx) = seg {
|
|
|
- *idx = *text_mapping.get(
|
|
|
- &self.dynamic_context.text[*idx]
|
|
|
- .to_token_stream()
|
|
|
- .to_string(),
|
|
|
- )?;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- TemplateNodeTypeBuilder::DynamicNode(idx) => {
|
|
|
- *idx = *node_mapping.get(
|
|
|
- &self.dynamic_context.nodes[*idx]
|
|
|
- .to_token_stream()
|
|
|
- .to_string(),
|
|
|
- )?;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- self.dynamic_context = dynamic_context;
|
|
|
-
|
|
|
- Some(self)
|
|
|
- }
|
|
|
-
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- pub fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplate, Error> {
|
|
|
- let mut nodes = Vec::new();
|
|
|
- let dynamic_mapping = self.dynamic_mapping(&nodes);
|
|
|
- let dynamic_path = self.dynamic_path();
|
|
|
- for node in self.nodes {
|
|
|
- nodes.push(node.try_into_owned(location)?);
|
|
|
- }
|
|
|
-
|
|
|
- Ok(OwnedTemplate {
|
|
|
- nodes,
|
|
|
- root_nodes: self.root_nodes,
|
|
|
- dynamic_mapping,
|
|
|
- dynamic_path,
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
- pub fn dynamic_mapping(
|
|
|
- &self,
|
|
|
- resolved_nodes: &Vec<OwnedTemplateNode>,
|
|
|
- ) -> OwnedDynamicNodeMapping {
|
|
|
- let dynamic_context = &self.dynamic_context;
|
|
|
- let mut node_mapping = vec![None; dynamic_context.nodes.len()];
|
|
|
- let nodes = &self.nodes;
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
|
|
|
- node_mapping[*idx] = Some(n.id)
|
|
|
- }
|
|
|
- }
|
|
|
- let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
|
|
|
- for seg in &txt.segments {
|
|
|
- match seg {
|
|
|
- TextTemplateSegment::Static(_) => (),
|
|
|
- TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
- for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
- match attr.value {
|
|
|
- TemplateAttributeValue::Static(_) => (),
|
|
|
- TemplateAttributeValue::Dynamic(idx) => {
|
|
|
- attribute_mapping[idx].push((n.id, i));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let mut listener_mapping = Vec::new();
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
- if !el.listeners.is_empty() {
|
|
|
- listener_mapping.push(n.id);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let mut volatile_mapping = Vec::new();
|
|
|
- for n in resolved_nodes {
|
|
|
- if let TemplateNodeType::Element(el) = &n.node_type {
|
|
|
- for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
- if attr.attribute.volatile {
|
|
|
- volatile_mapping.push((n.id, i));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- OwnedDynamicNodeMapping::new(
|
|
|
- node_mapping,
|
|
|
- text_mapping,
|
|
|
- attribute_mapping,
|
|
|
- volatile_mapping,
|
|
|
- listener_mapping,
|
|
|
- )
|
|
|
- }
|
|
|
-
|
|
|
- fn dynamic_path(&self) -> Option<OwnedPathSeg> {
|
|
|
- let mut last_seg: Option<OwnedPathSeg> = None;
|
|
|
- let mut nodes_to_insert_after = Vec::new();
|
|
|
- // iterating from the last root to the first
|
|
|
- for root in self.root_nodes.iter().rev() {
|
|
|
- let root_node = &self.nodes[root.0];
|
|
|
- if let TemplateNodeTypeBuilder::DynamicNode(_) = root_node.node_type {
|
|
|
- match &mut last_seg {
|
|
|
- // if there has been no static nodes, we can append the child to the parent node
|
|
|
- None => nodes_to_insert_after.push(*root),
|
|
|
- // otherwise we insert the child before the last static node
|
|
|
- Some(seg) => {
|
|
|
- seg.ops.push(UpdateOp::InsertBefore(*root));
|
|
|
- }
|
|
|
- }
|
|
|
- } else if let Some(mut new) = self.construct_path_segment(*root) {
|
|
|
- if let Some(last) = last_seg.take() {
|
|
|
- match new.traverse {
|
|
|
- OwnedTraverse::Halt => {
|
|
|
- new.traverse = OwnedTraverse::NextSibling(Box::new(last));
|
|
|
- }
|
|
|
- OwnedTraverse::FirstChild(b) => {
|
|
|
- new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
|
|
|
- }
|
|
|
- _ => unreachable!(),
|
|
|
- }
|
|
|
- } else {
|
|
|
- for node in nodes_to_insert_after.drain(..) {
|
|
|
- new.ops.push(UpdateOp::InsertAfter(node));
|
|
|
- }
|
|
|
- }
|
|
|
- last_seg = Some(new);
|
|
|
- } else if let Some(last) = last_seg.take() {
|
|
|
- last_seg = Some(OwnedPathSeg {
|
|
|
- ops: Vec::new(),
|
|
|
- traverse: OwnedTraverse::NextSibling(Box::new(last)),
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- last_seg
|
|
|
- }
|
|
|
-
|
|
|
- fn construct_path_segment(&self, node_id: TemplateNodeId) -> Option<OwnedPathSeg> {
|
|
|
- let n = &self.nodes[node_id.0];
|
|
|
- if n.fully_static {
|
|
|
- return None;
|
|
|
- }
|
|
|
- match &n.node_type {
|
|
|
- TemplateNodeTypeBuilder::Element(el) => {
|
|
|
- let mut last_seg: Option<OwnedPathSeg> = None;
|
|
|
- let mut children_to_append = Vec::new();
|
|
|
- // iterating from the last child to the first
|
|
|
- for child in el.children.iter().rev() {
|
|
|
- let child_node = &self.nodes[child.0];
|
|
|
- if let TemplateNodeTypeBuilder::DynamicNode(_) = child_node.node_type {
|
|
|
- match &mut last_seg {
|
|
|
- // if there has been no static nodes, we can append the child to the parent node
|
|
|
- None => children_to_append.push(*child),
|
|
|
- // otherwise we insert the child before the last static node
|
|
|
- Some(seg) => {
|
|
|
- seg.ops.push(UpdateOp::InsertBefore(*child));
|
|
|
- }
|
|
|
- }
|
|
|
- } else if let Some(mut new) = self.construct_path_segment(*child) {
|
|
|
- if let Some(last) = last_seg.take() {
|
|
|
- match new.traverse {
|
|
|
- OwnedTraverse::Halt => {
|
|
|
- new.traverse = OwnedTraverse::NextSibling(Box::new(last));
|
|
|
- }
|
|
|
- OwnedTraverse::FirstChild(b) => {
|
|
|
- new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
|
|
|
- }
|
|
|
- _ => unreachable!(),
|
|
|
- }
|
|
|
- }
|
|
|
- last_seg = Some(new);
|
|
|
- } else if let Some(last) = last_seg.take() {
|
|
|
- last_seg = Some(OwnedPathSeg {
|
|
|
- ops: Vec::new(),
|
|
|
- traverse: OwnedTraverse::NextSibling(Box::new(last)),
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- let mut ops = Vec::new();
|
|
|
- if !el.locally_static || n.parent.is_none() {
|
|
|
- ops.push(UpdateOp::StoreNode(node_id));
|
|
|
- }
|
|
|
- for child in children_to_append.into_iter().rev() {
|
|
|
- ops.push(UpdateOp::AppendChild(child));
|
|
|
- }
|
|
|
- Some(OwnedPathSeg {
|
|
|
- ops,
|
|
|
- traverse: match last_seg {
|
|
|
- Some(last) => OwnedTraverse::FirstChild(Box::new(last)),
|
|
|
- None => OwnedTraverse::Halt,
|
|
|
- },
|
|
|
- })
|
|
|
- }
|
|
|
- TemplateNodeTypeBuilder::Text(_) => Some(OwnedPathSeg {
|
|
|
- ops: vec![UpdateOp::StoreNode(n.id)],
|
|
|
- traverse: dioxus_core::OwnedTraverse::Halt,
|
|
|
- }),
|
|
|
- TemplateNodeTypeBuilder::DynamicNode(_) => unreachable!(
|
|
|
- "constructing path segment for dynamic nodes is handled in the parent node"
|
|
|
- ),
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl ToTokens for TemplateBuilder {
|
|
|
- fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
- let Self {
|
|
|
- nodes,
|
|
|
- root_nodes,
|
|
|
- dynamic_context,
|
|
|
- } = self;
|
|
|
-
|
|
|
- let mut node_mapping = vec![None; dynamic_context.nodes.len()];
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
|
|
|
- node_mapping[*idx] = Some(n.id);
|
|
|
- }
|
|
|
- }
|
|
|
- let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
|
|
|
- for seg in &txt.segments {
|
|
|
- match seg {
|
|
|
- TextTemplateSegment::Static(_) => (),
|
|
|
- TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
- for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
- match attr.value {
|
|
|
- TemplateAttributeValue::Static(_) => (),
|
|
|
- TemplateAttributeValue::Dynamic(idx) => {
|
|
|
- attribute_mapping[idx].push((n.id, i));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let mut listener_mapping = Vec::new();
|
|
|
- for n in nodes {
|
|
|
- if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
- if !el.listeners.is_empty() {
|
|
|
- listener_mapping.push(n.id);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- let root_nodes = root_nodes.iter().map(|id| {
|
|
|
- let raw = id.0;
|
|
|
- quote! { TemplateNodeId(#raw) }
|
|
|
- });
|
|
|
- let node_mapping_quoted = node_mapping.iter().map(|op| match op {
|
|
|
- Some(id) => {
|
|
|
- let raw_id = id.0;
|
|
|
- quote! {Some(TemplateNodeId(#raw_id))}
|
|
|
- }
|
|
|
- None => quote! {None},
|
|
|
- });
|
|
|
- let text_mapping_quoted = text_mapping.iter().map(|inner| {
|
|
|
- let raw = inner.iter().map(|id| id.0);
|
|
|
- quote! {&[#(TemplateNodeId(#raw)),*]}
|
|
|
- });
|
|
|
- let attribute_mapping_quoted = attribute_mapping.iter().map(|inner| {
|
|
|
- let raw = inner.iter().map(|(id, _)| id.0);
|
|
|
- let indecies = inner.iter().map(|(_, idx)| idx);
|
|
|
- quote! {&[#((TemplateNodeId(#raw), #indecies)),*]}
|
|
|
- });
|
|
|
- let listener_mapping_quoted = listener_mapping.iter().map(|id| {
|
|
|
- let raw = id.0;
|
|
|
- quote! {TemplateNodeId(#raw)}
|
|
|
- });
|
|
|
- let mut nodes_quoted = TokenStream::new();
|
|
|
- for n in nodes {
|
|
|
- n.to_tokens(&mut nodes_quoted);
|
|
|
- quote! {,}.to_tokens(&mut nodes_quoted);
|
|
|
- }
|
|
|
-
|
|
|
- let dynamic_path = match self.dynamic_path() {
|
|
|
- Some(seg) => {
|
|
|
- let seg = quote_owned_segment(seg);
|
|
|
- quote! {Some(#seg)}
|
|
|
- }
|
|
|
- None => quote! {None},
|
|
|
- };
|
|
|
-
|
|
|
- let quoted = quote! {
|
|
|
- {
|
|
|
- const __NODES: dioxus::prelude::StaticTemplateNodes = &[#nodes_quoted];
|
|
|
- const __TEXT_MAPPING: &'static [&'static [dioxus::prelude::TemplateNodeId]] = &[#(#text_mapping_quoted),*];
|
|
|
- const __ATTRIBUTE_MAPPING: &'static [&'static [(dioxus::prelude::TemplateNodeId, usize)]] = &[#(#attribute_mapping_quoted),*];
|
|
|
- const __ROOT_NODES: &'static [dioxus::prelude::TemplateNodeId] = &[#(#root_nodes),*];
|
|
|
- const __NODE_MAPPING: &'static [Option<dioxus::prelude::TemplateNodeId>] = &[#(#node_mapping_quoted),*];
|
|
|
- const __NODES_WITH_LISTENERS: &'static [dioxus::prelude::TemplateNodeId] = &[#(#listener_mapping_quoted),*];
|
|
|
- static __VOLITALE_MAPPING_INNER: dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = dioxus::core::exports::once_cell::sync::Lazy::new(||{
|
|
|
- // check each property to see if it is volatile
|
|
|
- let mut volatile = Vec::new();
|
|
|
- for n in __NODES {
|
|
|
- if let TemplateNodeType::Element(el) = &n.node_type {
|
|
|
- for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
- if attr.attribute.volatile {
|
|
|
- volatile.push((n.id, i));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- volatile
|
|
|
- });
|
|
|
- static __VOLITALE_MAPPING: &'static dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = &__VOLITALE_MAPPING_INNER;
|
|
|
- static __STATIC_VOLITALE_MAPPING: dioxus::prelude::LazyStaticVec<(dioxus::prelude::TemplateNodeId, usize)> = LazyStaticVec(__VOLITALE_MAPPING);
|
|
|
- static __TEMPLATE: dioxus::prelude::Template = Template::Static(&StaticTemplate {
|
|
|
- nodes: __NODES,
|
|
|
- root_nodes: __ROOT_NODES,
|
|
|
- dynamic_mapping: StaticDynamicNodeMapping::new(__NODE_MAPPING, __TEXT_MAPPING, __ATTRIBUTE_MAPPING, __STATIC_VOLITALE_MAPPING, __NODES_WITH_LISTENERS),
|
|
|
- dynamic_path: #dynamic_path,
|
|
|
- });
|
|
|
-
|
|
|
- let __bump = __cx.bump();
|
|
|
- __cx.template_ref(dioxus::prelude::TemplateId(get_line_num!()), __TEMPLATE.clone(), #dynamic_context)
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- tokens.append_all(quoted)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#[derive(Default, Clone, Debug)]
|
|
|
-pub struct DynamicTemplateContextBuilder {
|
|
|
- nodes: Vec<BodyNode>,
|
|
|
- text: Vec<FormattedSegment>,
|
|
|
- attributes: Vec<TokenStream>,
|
|
|
- listeners: Vec<(String, Expr)>,
|
|
|
- key: Option<TokenStream>,
|
|
|
-}
|
|
|
-
|
|
|
-impl DynamicTemplateContextBuilder {
|
|
|
- fn add_node(&mut self, node: BodyNode) -> usize {
|
|
|
- let node_id = self.nodes.len();
|
|
|
-
|
|
|
- self.nodes.push(node);
|
|
|
-
|
|
|
- node_id
|
|
|
- }
|
|
|
-
|
|
|
- fn add_text(&mut self, text: FormattedSegment) -> usize {
|
|
|
- let text_id = self.text.len();
|
|
|
-
|
|
|
- self.text.push(text);
|
|
|
-
|
|
|
- text_id
|
|
|
- }
|
|
|
-
|
|
|
- fn add_attr(&mut self, attr: TokenStream) -> usize {
|
|
|
- let attr_id = self.attributes.len();
|
|
|
-
|
|
|
- self.attributes.push(attr);
|
|
|
-
|
|
|
- attr_id
|
|
|
- }
|
|
|
-
|
|
|
- fn add_listener(&mut self, name: Ident, listener: Expr) -> usize {
|
|
|
- let listener_id = self.listeners.len();
|
|
|
-
|
|
|
- self.listeners.push((name.to_string(), listener));
|
|
|
-
|
|
|
- listener_id
|
|
|
- }
|
|
|
-
|
|
|
- fn add_key(&mut self, key: TokenStream) {
|
|
|
- self.key = Some(key);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl ToTokens for DynamicTemplateContextBuilder {
|
|
|
- fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
- let nodes = &self.nodes;
|
|
|
- let text = &self.text;
|
|
|
- let attributes = &self.attributes;
|
|
|
- let listeners_names = self
|
|
|
- .listeners
|
|
|
- .iter()
|
|
|
- .map(|(n, _)| syn::parse_str::<Ident>(n).expect(n));
|
|
|
- let listeners_exprs = self.listeners.iter().map(|(_, e)| e);
|
|
|
- let key = match &self.key {
|
|
|
- Some(k) => quote!(Some(#k)),
|
|
|
- None => quote!(None),
|
|
|
- };
|
|
|
- tokens.append_all(quote! {
|
|
|
- TemplateContext {
|
|
|
- nodes: __cx.bump().alloc([#(#nodes),*]),
|
|
|
- text_segments: __cx.bump().alloc([#(&*dioxus::core::exports::bumpalo::format!(in __bump, "{}", #text).into_bump_str()),*]),
|
|
|
- attributes: __cx.bump().alloc([#({#attributes}.into_value(__cx.bump())),*]),
|
|
|
- listeners: __cx.bump().alloc([#(dioxus_elements::on::#listeners_names(__cx, #listeners_exprs)),*]),
|
|
|
- key: #key,
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn quote_owned_segment(seg: OwnedPathSeg) -> proc_macro2::TokenStream {
|
|
|
- let OwnedPathSeg { ops, traverse } = seg;
|
|
|
-
|
|
|
- let ops = ops
|
|
|
- .into_iter()
|
|
|
- .map(|op| match op {
|
|
|
- UpdateOp::StoreNode(id) => {
|
|
|
- let id = quote_template_node_id(id);
|
|
|
- quote!(UpdateOp::StoreNode(#id))
|
|
|
- }
|
|
|
- UpdateOp::InsertBefore(id) => {
|
|
|
- let id = quote_template_node_id(id);
|
|
|
- quote!(UpdateOp::InsertBefore(#id))
|
|
|
- }
|
|
|
- UpdateOp::InsertAfter(id) => {
|
|
|
- let id = quote_template_node_id(id);
|
|
|
- quote!(UpdateOp::InsertAfter(#id))
|
|
|
- }
|
|
|
- UpdateOp::AppendChild(id) => {
|
|
|
- let id = quote_template_node_id(id);
|
|
|
- quote!(UpdateOp::AppendChild(#id))
|
|
|
- }
|
|
|
- })
|
|
|
- .collect::<Vec<_>>();
|
|
|
-
|
|
|
- let traverse = quote_owned_traverse(traverse);
|
|
|
-
|
|
|
- quote! {
|
|
|
- StaticPathSeg {
|
|
|
- ops: &[#(#ops),*],
|
|
|
- traverse: #traverse,
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn quote_owned_traverse(traverse: OwnedTraverse) -> proc_macro2::TokenStream {
|
|
|
- match traverse {
|
|
|
- OwnedTraverse::Halt => {
|
|
|
- quote! {StaticTraverse::Halt}
|
|
|
- }
|
|
|
- OwnedTraverse::FirstChild(seg) => {
|
|
|
- let seg = quote_owned_segment(*seg);
|
|
|
- quote! {StaticTraverse::FirstChild(&#seg)}
|
|
|
- }
|
|
|
- OwnedTraverse::NextSibling(seg) => {
|
|
|
- let seg = quote_owned_segment(*seg);
|
|
|
- quote! {StaticTraverse::NextSibling(&#seg)}
|
|
|
- }
|
|
|
- OwnedTraverse::Both(b) => {
|
|
|
- let (child, sibling) = *b;
|
|
|
- let child = quote_owned_segment(child);
|
|
|
- let sibling = quote_owned_segment(sibling);
|
|
|
- quote! {StaticTraverse::Both(&(#child, #sibling))}
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-fn quote_template_node_id(id: TemplateNodeId) -> proc_macro2::TokenStream {
|
|
|
- let raw = id.0;
|
|
|
- quote! {
|
|
|
- TemplateNodeId(#raw)
|
|
|
- }
|
|
|
-}
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// pub fn try_parse_template(
|
|
|
+// rsx: &str,
|
|
|
+// location: OwnedCodeLocation,
|
|
|
+// previous_template: Option<DynamicTemplateContextBuilder>,
|
|
|
+// ) -> Result<(OwnedTemplate, DynamicTemplateContextBuilder), Error> {
|
|
|
+// use crate::CallBody;
|
|
|
+
|
|
|
+// let call_body: CallBody =
|
|
|
+// syn::parse_str(rsx).map_err(|e| Error::ParseError(ParseError::new(e, location.clone())))?;
|
|
|
+// let mut template_builder = TemplateBuilder::from_roots_always(call_body.roots);
|
|
|
+// if let Some(prev) = previous_template {
|
|
|
+// template_builder = template_builder
|
|
|
+// .try_switch_dynamic_context(prev)
|
|
|
+// .ok_or_else(|| {
|
|
|
+// Error::RecompileRequiredError(RecompileReason::Variable(
|
|
|
+// "dynamic context updated".to_string(),
|
|
|
+// ))
|
|
|
+// })?;
|
|
|
+// }
|
|
|
+// let dyn_ctx = template_builder.dynamic_context.clone();
|
|
|
+// Ok((template_builder.try_into_owned(&location)?, dyn_ctx))
|
|
|
+// }
|
|
|
+
|
|
|
+// use crate::{BodyNode, ElementAttr, FormattedSegment, Segment};
|
|
|
+
|
|
|
+// struct TemplateElementBuilder {
|
|
|
+// tag: Ident,
|
|
|
+// attributes: Vec<TemplateAttributeBuilder>,
|
|
|
+// children: Vec<TemplateNodeId>,
|
|
|
+// listeners: Vec<usize>,
|
|
|
+// locally_static: bool,
|
|
|
+// }
|
|
|
+
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// type OwndedTemplateElement = TemplateElement<
|
|
|
+// Vec<TemplateAttribute<OwnedAttributeValue>>,
|
|
|
+// OwnedAttributeValue,
|
|
|
+// Vec<TemplateNodeId>,
|
|
|
+// Vec<usize>,
|
|
|
+// >;
|
|
|
+
|
|
|
+// impl TemplateElementBuilder {
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwndedTemplateElement, Error> {
|
|
|
+// let Self {
|
|
|
+// tag,
|
|
|
+// attributes,
|
|
|
+// children,
|
|
|
+// listeners,
|
|
|
+// ..
|
|
|
+// } = self;
|
|
|
+// let (element_tag, element_ns) =
|
|
|
+// element_to_static_str(&tag.to_string()).ok_or_else(|| {
|
|
|
+// Error::ParseError(ParseError::new(
|
|
|
+// syn::Error::new(tag.span(), format!("unknown element: {}", tag)),
|
|
|
+// location.clone(),
|
|
|
+// ))
|
|
|
+// })?;
|
|
|
+
|
|
|
+// let mut owned_attributes = Vec::new();
|
|
|
+// for a in attributes {
|
|
|
+// owned_attributes.push(a.try_into_owned(location, element_tag, element_ns)?);
|
|
|
+// }
|
|
|
+
|
|
|
+// Ok(TemplateElement::new(
|
|
|
+// element_tag,
|
|
|
+// element_ns,
|
|
|
+// owned_attributes,
|
|
|
+// children,
|
|
|
+// listeners,
|
|
|
+// ))
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// impl ToTokens for TemplateElementBuilder {
|
|
|
+// fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
+// let Self {
|
|
|
+// tag,
|
|
|
+// attributes,
|
|
|
+// children,
|
|
|
+// listeners,
|
|
|
+// ..
|
|
|
+// } = self;
|
|
|
+// let children = children.iter().map(|id| {
|
|
|
+// let raw = id.0;
|
|
|
+// quote! {TemplateNodeId(#raw)}
|
|
|
+// });
|
|
|
+// tokens.append_all(quote! {
|
|
|
+// TemplateElement::new(
|
|
|
+// dioxus_elements::#tag::TAG_NAME,
|
|
|
+// dioxus_elements::#tag::NAME_SPACE,
|
|
|
+// &[#(#attributes),*],
|
|
|
+// &[#(#children),*],
|
|
|
+// &[#(#listeners),*],
|
|
|
+// )
|
|
|
+// })
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// enum AttributeName {
|
|
|
+// Ident(Ident),
|
|
|
+// Str(LitStr),
|
|
|
+// }
|
|
|
+
|
|
|
+// struct TemplateAttributeBuilder {
|
|
|
+// element_tag: Ident,
|
|
|
+// name: AttributeName,
|
|
|
+// value: TemplateAttributeValue<OwnedAttributeValue>,
|
|
|
+// }
|
|
|
+
|
|
|
+// impl TemplateAttributeBuilder {
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// fn try_into_owned(
|
|
|
+// self,
|
|
|
+// location: &OwnedCodeLocation,
|
|
|
+// element_tag: &'static str,
|
|
|
+// element_ns: Option<&'static str>,
|
|
|
+// ) -> Result<TemplateAttribute<OwnedAttributeValue>, Error> {
|
|
|
+// let Self { name, value, .. } = self;
|
|
|
+// let (name, span, literal) = match name {
|
|
|
+// AttributeName::Ident(name) => (name.to_string(), name.span(), false),
|
|
|
+// AttributeName::Str(name) => (name.value(), name.span(), true),
|
|
|
+// };
|
|
|
+// let (name, namespace, volatile) = attrbute_to_static_str(&name, element_tag, element_ns)
|
|
|
+// .ok_or_else(|| {
|
|
|
+// if literal {
|
|
|
+// // literals will be captured when a full recompile is triggered
|
|
|
+// Error::RecompileRequiredError(RecompileReason::Attribute(name.to_string()))
|
|
|
+// } else {
|
|
|
+// Error::ParseError(ParseError::new(
|
|
|
+// syn::Error::new(span, format!("unknown attribute: {}", name)),
|
|
|
+// location.clone(),
|
|
|
+// ))
|
|
|
+// }
|
|
|
+// })?;
|
|
|
+// let attribute = AttributeDiscription {
|
|
|
+// name,
|
|
|
+// namespace,
|
|
|
+// volatile,
|
|
|
+// };
|
|
|
+// Ok(TemplateAttribute { value, attribute })
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// impl ToTokens for TemplateAttributeBuilder {
|
|
|
+// fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
+// let Self {
|
|
|
+// element_tag,
|
|
|
+// name,
|
|
|
+// value,
|
|
|
+// } = self;
|
|
|
+// let value = match value {
|
|
|
+// TemplateAttributeValue::Static(val) => {
|
|
|
+// let val = match val {
|
|
|
+// OwnedAttributeValue::Text(txt) => quote! {StaticAttributeValue::Text(#txt)},
|
|
|
+// OwnedAttributeValue::Float32(f) => quote! {StaticAttributeValue::Float32(#f)},
|
|
|
+// OwnedAttributeValue::Float64(f) => quote! {StaticAttributeValue::Float64(#f)},
|
|
|
+// OwnedAttributeValue::Int32(i) => quote! {StaticAttributeValue::Int32(#i)},
|
|
|
+// OwnedAttributeValue::Int64(i) => quote! {StaticAttributeValue::Int64(#i)},
|
|
|
+// OwnedAttributeValue::Uint32(u) => quote! {StaticAttributeValue::Uint32(#u)},
|
|
|
+// OwnedAttributeValue::Uint64(u) => quote! {StaticAttributeValue::Uint64(#u)},
|
|
|
+// OwnedAttributeValue::Bool(b) => quote! {StaticAttributeValue::Bool(#b)},
|
|
|
+// OwnedAttributeValue::Vec3Float(f1, f2, f3) => {
|
|
|
+// quote! {StaticAttributeValue::Vec3Float(#f1, #f2, #f3)}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Vec3Int(f1, f2, f3) => {
|
|
|
+// quote! {StaticAttributeValue::Vec3Int(#f1, #f2, #f3)}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Vec3Uint(f1, f2, f3) => {
|
|
|
+// quote! {StaticAttributeValue::Vec3Uint(#f1, #f2, #f3)}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Vec4Float(f1, f2, f3, f4) => {
|
|
|
+// quote! {StaticAttributeValue::Vec4Float(#f1, #f2, #f3, #f4)}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Vec4Int(f1, f2, f3, f4) => {
|
|
|
+// quote! {StaticAttributeValue::Vec4Int(#f1, #f2, #f3, #f4)}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Vec4Uint(f1, f2, f3, f4) => {
|
|
|
+// quote! {StaticAttributeValue::Vec4Uint(#f1, #f2, #f3, #f4)}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Bytes(b) => {
|
|
|
+// quote! {StaticAttributeValue::Bytes(&[#(#b),*])}
|
|
|
+// }
|
|
|
+// OwnedAttributeValue::Any(_) => todo!(),
|
|
|
+// };
|
|
|
+// quote! {TemplateAttributeValue::Static(#val)}
|
|
|
+// }
|
|
|
+// TemplateAttributeValue::Dynamic(idx) => quote! {TemplateAttributeValue::Dynamic(#idx)},
|
|
|
+// };
|
|
|
+// match name {
|
|
|
+// AttributeName::Ident(name) => tokens.append_all(quote! {
|
|
|
+// TemplateAttribute{
|
|
|
+// attribute: dioxus_elements::#element_tag::#name,
|
|
|
+// value: #value,
|
|
|
+// }
|
|
|
+// }),
|
|
|
+// AttributeName::Str(lit) => tokens.append_all(quote! {
|
|
|
+// TemplateAttribute{
|
|
|
+// attribute: dioxus::prelude::AttributeDiscription{
|
|
|
+// name: #lit,
|
|
|
+// namespace: None,
|
|
|
+// volatile: false
|
|
|
+// },
|
|
|
+// value: #value,
|
|
|
+// }
|
|
|
+// }),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// enum TemplateNodeTypeBuilder {
|
|
|
+// Element(TemplateElementBuilder),
|
|
|
+// Text(TextTemplate<Vec<TextTemplateSegment<String>>, String>),
|
|
|
+// DynamicNode(usize),
|
|
|
+// }
|
|
|
+
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// type OwnedTemplateNodeType = TemplateNodeType<
|
|
|
+// Vec<TemplateAttribute<OwnedAttributeValue>>,
|
|
|
+// OwnedAttributeValue,
|
|
|
+// Vec<TemplateNodeId>,
|
|
|
+// Vec<usize>,
|
|
|
+// Vec<TextTemplateSegment<String>>,
|
|
|
+// String,
|
|
|
+// >;
|
|
|
+
|
|
|
+// impl TemplateNodeTypeBuilder {
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNodeType, Error> {
|
|
|
+// match self {
|
|
|
+// TemplateNodeTypeBuilder::Element(el) => {
|
|
|
+// Ok(TemplateNodeType::Element(el.try_into_owned(location)?))
|
|
|
+// }
|
|
|
+// TemplateNodeTypeBuilder::Text(txt) => Ok(TemplateNodeType::Text(txt)),
|
|
|
+// TemplateNodeTypeBuilder::DynamicNode(idx) => Ok(TemplateNodeType::DynamicNode(idx)),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// impl ToTokens for TemplateNodeTypeBuilder {
|
|
|
+// fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
+// match self {
|
|
|
+// TemplateNodeTypeBuilder::Element(el) => tokens.append_all(quote! {
|
|
|
+// TemplateNodeType::Element(#el)
|
|
|
+// }),
|
|
|
+// TemplateNodeTypeBuilder::Text(txt) => {
|
|
|
+// let mut length = 0;
|
|
|
+
|
|
|
+// let segments = txt.segments.iter().map(|seg| match seg {
|
|
|
+// TextTemplateSegment::Static(s) => {
|
|
|
+// length += s.len();
|
|
|
+// quote!(TextTemplateSegment::Static(#s))
|
|
|
+// }
|
|
|
+// TextTemplateSegment::Dynamic(idx) => quote!(TextTemplateSegment::Dynamic(#idx)),
|
|
|
+// });
|
|
|
+
|
|
|
+// tokens.append_all(quote! {
|
|
|
+// TemplateNodeType::Text(TextTemplate::new(&[#(#segments),*], #length))
|
|
|
+// });
|
|
|
+// }
|
|
|
+// TemplateNodeTypeBuilder::DynamicNode(idx) => tokens.append_all(quote! {
|
|
|
+// TemplateNodeType::DynamicNode(#idx)
|
|
|
+// }),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// struct TemplateNodeBuilder {
|
|
|
+// id: TemplateNodeId,
|
|
|
+// depth: usize,
|
|
|
+// parent: Option<TemplateNodeId>,
|
|
|
+// node_type: TemplateNodeTypeBuilder,
|
|
|
+// fully_static: bool,
|
|
|
+// }
|
|
|
+
|
|
|
+// impl TemplateNodeBuilder {
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplateNode, Error> {
|
|
|
+// let TemplateNodeBuilder {
|
|
|
+// id,
|
|
|
+// node_type,
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// ..
|
|
|
+// } = self;
|
|
|
+// let node_type = node_type.try_into_owned(location)?;
|
|
|
+// Ok(OwnedTemplateNode {
|
|
|
+// id,
|
|
|
+// node_type,
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// })
|
|
|
+// }
|
|
|
+
|
|
|
+// fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
+// let Self {
|
|
|
+// id,
|
|
|
+// node_type,
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// ..
|
|
|
+// } = self;
|
|
|
+// let raw_id = id.0;
|
|
|
+// let parent = match parent {
|
|
|
+// Some(id) => {
|
|
|
+// let id = id.0;
|
|
|
+// quote! {Some(TemplateNodeId(#id))}
|
|
|
+// }
|
|
|
+// None => quote! {None},
|
|
|
+// };
|
|
|
+
|
|
|
+// tokens.append_all(quote! {
|
|
|
+// TemplateNode {
|
|
|
+// id: TemplateNodeId(#raw_id),
|
|
|
+// node_type: #node_type,
|
|
|
+// parent: #parent,
|
|
|
+// depth: #depth,
|
|
|
+// }
|
|
|
+// })
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// #[derive(Default)]
|
|
|
+// pub struct TemplateBuilder {
|
|
|
+// nodes: Vec<TemplateNodeBuilder>,
|
|
|
+// root_nodes: Vec<TemplateNodeId>,
|
|
|
+// dynamic_context: DynamicTemplateContextBuilder,
|
|
|
+// }
|
|
|
+
|
|
|
+// impl TemplateBuilder {
|
|
|
+// /// Create a template builder from nodes if it would improve performance to do so.
|
|
|
+// pub fn from_roots(roots: Vec<BodyNode>) -> Option<Self> {
|
|
|
+// let mut builder = Self::default();
|
|
|
+
|
|
|
+// for (i, root) in roots.into_iter().enumerate() {
|
|
|
+// let id = builder.build_node(root, None, vec![i], 0);
|
|
|
+// builder.root_nodes.push(id);
|
|
|
+// }
|
|
|
+
|
|
|
+// // only build a template if there is at least one static node
|
|
|
+// if builder
|
|
|
+// .nodes
|
|
|
+// .iter()
|
|
|
+// .all(|r| matches!(&r.node_type, TemplateNodeTypeBuilder::DynamicNode(_)))
|
|
|
+// {
|
|
|
+// None
|
|
|
+// } else {
|
|
|
+// Some(builder)
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// /// Create a template builder from nodes regardless of performance.
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// fn from_roots_always(roots: Vec<BodyNode>) -> Self {
|
|
|
+// let mut builder = Self::default();
|
|
|
+
|
|
|
+// for (i, root) in roots.into_iter().enumerate() {
|
|
|
+// let id = builder.build_node(root, None, vec![i], 0);
|
|
|
+// builder.root_nodes.push(id);
|
|
|
+// }
|
|
|
+
|
|
|
+// builder
|
|
|
+// }
|
|
|
+
|
|
|
+// fn build_node(
|
|
|
+// &mut self,
|
|
|
+// node: BodyNode,
|
|
|
+// parent: Option<TemplateNodeId>,
|
|
|
+// path: Vec<usize>,
|
|
|
+// depth: usize,
|
|
|
+// ) -> TemplateNodeId {
|
|
|
+// let id = TemplateNodeId(self.nodes.len());
|
|
|
+// match node {
|
|
|
+// BodyNode::Element(el) => {
|
|
|
+// let mut locally_static = true;
|
|
|
+// let mut attributes = Vec::new();
|
|
|
+// let mut listeners = Vec::new();
|
|
|
+// for attr in el.attributes {
|
|
|
+// match attr.attr {
|
|
|
+// ElementAttr::AttrText { name, value } => {
|
|
|
+// if let Some(static_value) = value.to_static() {
|
|
|
+// attributes.push(TemplateAttributeBuilder {
|
|
|
+// element_tag: el.name.clone(),
|
|
|
+// name: AttributeName::Ident(name),
|
|
|
+// value: TemplateAttributeValue::Static(
|
|
|
+// OwnedAttributeValue::Text(static_value),
|
|
|
+// ),
|
|
|
+// })
|
|
|
+// } else {
|
|
|
+// locally_static = false;
|
|
|
+// attributes.push(TemplateAttributeBuilder {
|
|
|
+// element_tag: el.name.clone(),
|
|
|
+// name: AttributeName::Ident(name),
|
|
|
+// value: TemplateAttributeValue::Dynamic(
|
|
|
+// self.dynamic_context.add_attr(quote!(#value)),
|
|
|
+// ),
|
|
|
+// })
|
|
|
+// }
|
|
|
+// }
|
|
|
+// ElementAttr::CustomAttrText { name, value } => {
|
|
|
+// if let Some(static_value) = value.to_static() {
|
|
|
+// attributes.push(TemplateAttributeBuilder {
|
|
|
+// element_tag: el.name.clone(),
|
|
|
+// name: AttributeName::Str(name),
|
|
|
+// value: TemplateAttributeValue::Static(
|
|
|
+// OwnedAttributeValue::Text(static_value),
|
|
|
+// ),
|
|
|
+// })
|
|
|
+// } else {
|
|
|
+// locally_static = false;
|
|
|
+// attributes.push(TemplateAttributeBuilder {
|
|
|
+// element_tag: el.name.clone(),
|
|
|
+// name: AttributeName::Str(name),
|
|
|
+// value: TemplateAttributeValue::Dynamic(
|
|
|
+// self.dynamic_context.add_attr(quote!(#value)),
|
|
|
+// ),
|
|
|
+// })
|
|
|
+// }
|
|
|
+// }
|
|
|
+// ElementAttr::AttrExpression { name, value } => {
|
|
|
+// locally_static = false;
|
|
|
+// attributes.push(TemplateAttributeBuilder {
|
|
|
+// element_tag: el.name.clone(),
|
|
|
+// name: AttributeName::Ident(name),
|
|
|
+// value: TemplateAttributeValue::Dynamic(
|
|
|
+// self.dynamic_context.add_attr(quote!(#value)),
|
|
|
+// ),
|
|
|
+// })
|
|
|
+// }
|
|
|
+// ElementAttr::CustomAttrExpression { name, value } => {
|
|
|
+// locally_static = false;
|
|
|
+// attributes.push(TemplateAttributeBuilder {
|
|
|
+// element_tag: el.name.clone(),
|
|
|
+// name: AttributeName::Str(name),
|
|
|
+// value: TemplateAttributeValue::Dynamic(
|
|
|
+// self.dynamic_context.add_attr(quote!(#value)),
|
|
|
+// ),
|
|
|
+// })
|
|
|
+// }
|
|
|
+// ElementAttr::EventTokens { name, tokens } => {
|
|
|
+// locally_static = false;
|
|
|
+// listeners.push(self.dynamic_context.add_listener(name, tokens))
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// if let Some(key) = el.key {
|
|
|
+// self.dynamic_context.add_key(quote!(
|
|
|
+// dioxus::core::exports::bumpalo::format!(in __bump, "{}", #key)
|
|
|
+// .into_bump_str()
|
|
|
+// ));
|
|
|
+// }
|
|
|
+// self.nodes.push(TemplateNodeBuilder {
|
|
|
+// id,
|
|
|
+// node_type: TemplateNodeTypeBuilder::Element(TemplateElementBuilder {
|
|
|
+// tag: el.name,
|
|
|
+// attributes,
|
|
|
+// children: Vec::new(),
|
|
|
+// listeners,
|
|
|
+// locally_static,
|
|
|
+// }),
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// fully_static: false,
|
|
|
+// });
|
|
|
+// let children: Vec<_> = el
|
|
|
+// .children
|
|
|
+// .into_iter()
|
|
|
+// .enumerate()
|
|
|
+// .map(|(i, child)| {
|
|
|
+// let mut new_path = path.clone();
|
|
|
+// new_path.push(i);
|
|
|
+// self.build_node(child, Some(id), new_path, depth + 1)
|
|
|
+// })
|
|
|
+// .collect();
|
|
|
+// let children_fully_static = children.iter().all(|c| self.nodes[c.0].fully_static);
|
|
|
+// let parent = &mut self.nodes[id.0];
|
|
|
+// parent.fully_static = locally_static && children_fully_static;
|
|
|
+// if let TemplateNodeTypeBuilder::Element(element) = &mut parent.node_type {
|
|
|
+// element.children = children;
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// BodyNode::Component(comp) => {
|
|
|
+// self.nodes.push(TemplateNodeBuilder {
|
|
|
+// id,
|
|
|
+// node_type: TemplateNodeTypeBuilder::DynamicNode(
|
|
|
+// self.dynamic_context.add_node(BodyNode::Component(comp)),
|
|
|
+// ),
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// fully_static: false,
|
|
|
+// });
|
|
|
+// }
|
|
|
+
|
|
|
+// BodyNode::Text(txt) => {
|
|
|
+// let mut segments = Vec::new();
|
|
|
+// let mut length = 0;
|
|
|
+// let mut fully_static = true;
|
|
|
+
|
|
|
+// for segment in txt.segments {
|
|
|
+// segments.push(match segment {
|
|
|
+// Segment::Literal(lit) => {
|
|
|
+// length += lit.len();
|
|
|
+// TextTemplateSegment::Static(lit)
|
|
|
+// }
|
|
|
+// Segment::Formatted(fmted) => {
|
|
|
+// fully_static = false;
|
|
|
+// TextTemplateSegment::Dynamic(self.dynamic_context.add_text(fmted))
|
|
|
+// }
|
|
|
+// })
|
|
|
+// }
|
|
|
+
|
|
|
+// self.nodes.push(TemplateNodeBuilder {
|
|
|
+// id,
|
|
|
+// node_type: TemplateNodeTypeBuilder::Text(TextTemplate::new(segments, length)),
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// fully_static,
|
|
|
+// });
|
|
|
+// }
|
|
|
+
|
|
|
+// BodyNode::RawExpr(expr) => {
|
|
|
+// self.nodes.push(TemplateNodeBuilder {
|
|
|
+// id,
|
|
|
+// node_type: TemplateNodeTypeBuilder::DynamicNode(
|
|
|
+// self.dynamic_context.add_node(BodyNode::RawExpr(expr)),
|
|
|
+// ),
|
|
|
+// parent,
|
|
|
+// depth,
|
|
|
+// fully_static: false,
|
|
|
+// });
|
|
|
+// }
|
|
|
+// }
|
|
|
+// id
|
|
|
+// }
|
|
|
+
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// pub fn try_switch_dynamic_context(
|
|
|
+// mut self,
|
|
|
+// dynamic_context: DynamicTemplateContextBuilder,
|
|
|
+// ) -> Option<Self> {
|
|
|
+// let attribute_mapping: HashMap<String, usize> = dynamic_context
|
|
|
+// .attributes
|
|
|
+// .iter()
|
|
|
+// .enumerate()
|
|
|
+// .map(|(i, ts)| (ts.to_string(), i))
|
|
|
+// .collect();
|
|
|
+// let text_mapping: HashMap<String, usize> = dynamic_context
|
|
|
+// .text
|
|
|
+// .iter()
|
|
|
+// .enumerate()
|
|
|
+// .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
|
|
|
+// .collect();
|
|
|
+// let listener_mapping: HashMap<(String, Expr), usize> = dynamic_context
|
|
|
+// .listeners
|
|
|
+// .iter()
|
|
|
+// .enumerate()
|
|
|
+// .map(|(i, ts)| (ts.clone(), i))
|
|
|
+// .collect();
|
|
|
+// let node_mapping: HashMap<String, usize> = dynamic_context
|
|
|
+// .nodes
|
|
|
+// .iter()
|
|
|
+// .enumerate()
|
|
|
+// .map(|(i, ts)| (ts.to_token_stream().to_string(), i))
|
|
|
+// .collect();
|
|
|
+
|
|
|
+// for node in &mut self.nodes {
|
|
|
+// match &mut node.node_type {
|
|
|
+// TemplateNodeTypeBuilder::Element(element) => {
|
|
|
+// for listener in &mut element.listeners {
|
|
|
+// *listener =
|
|
|
+// *listener_mapping.get(&self.dynamic_context.listeners[*listener])?;
|
|
|
+// }
|
|
|
+// for attribute in &mut element.attributes {
|
|
|
+// if let TemplateAttributeValue::Dynamic(idx) = &mut attribute.value {
|
|
|
+// *idx = *attribute_mapping
|
|
|
+// .get(&self.dynamic_context.attributes[*idx].to_string())?;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// TemplateNodeTypeBuilder::Text(txt) => {
|
|
|
+// for seg in &mut txt.segments {
|
|
|
+// if let TextTemplateSegment::Dynamic(idx) = seg {
|
|
|
+// *idx = *text_mapping.get(
|
|
|
+// &self.dynamic_context.text[*idx]
|
|
|
+// .to_token_stream()
|
|
|
+// .to_string(),
|
|
|
+// )?;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// TemplateNodeTypeBuilder::DynamicNode(idx) => {
|
|
|
+// *idx = *node_mapping.get(
|
|
|
+// &self.dynamic_context.nodes[*idx]
|
|
|
+// .to_token_stream()
|
|
|
+// .to_string(),
|
|
|
+// )?;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// self.dynamic_context = dynamic_context;
|
|
|
+
|
|
|
+// Some(self)
|
|
|
+// }
|
|
|
+
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// pub fn try_into_owned(self, location: &OwnedCodeLocation) -> Result<OwnedTemplate, Error> {
|
|
|
+// let mut nodes = Vec::new();
|
|
|
+// let dynamic_mapping = self.dynamic_mapping(&nodes);
|
|
|
+// let dynamic_path = self.dynamic_path();
|
|
|
+// for node in self.nodes {
|
|
|
+// nodes.push(node.try_into_owned(location)?);
|
|
|
+// }
|
|
|
+
|
|
|
+// Ok(OwnedTemplate {
|
|
|
+// nodes,
|
|
|
+// root_nodes: self.root_nodes,
|
|
|
+// dynamic_mapping,
|
|
|
+// dynamic_path,
|
|
|
+// })
|
|
|
+// }
|
|
|
+
|
|
|
+// #[cfg(any(feature = "hot-reload", debug_assertions))]
|
|
|
+// pub fn dynamic_mapping(
|
|
|
+// &self,
|
|
|
+// resolved_nodes: &Vec<OwnedTemplateNode>,
|
|
|
+// ) -> OwnedDynamicNodeMapping {
|
|
|
+// let dynamic_context = &self.dynamic_context;
|
|
|
+// let mut node_mapping = vec![None; dynamic_context.nodes.len()];
|
|
|
+// let nodes = &self.nodes;
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
|
|
|
+// node_mapping[*idx] = Some(n.id)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
|
|
|
+// for seg in &txt.segments {
|
|
|
+// match seg {
|
|
|
+// TextTemplateSegment::Static(_) => (),
|
|
|
+// TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
+// for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
+// match attr.value {
|
|
|
+// TemplateAttributeValue::Static(_) => (),
|
|
|
+// TemplateAttributeValue::Dynamic(idx) => {
|
|
|
+// attribute_mapping[idx].push((n.id, i));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut listener_mapping = Vec::new();
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
+// if !el.listeners.is_empty() {
|
|
|
+// listener_mapping.push(n.id);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// let mut volatile_mapping = Vec::new();
|
|
|
+// for n in resolved_nodes {
|
|
|
+// if let TemplateNodeType::Element(el) = &n.node_type {
|
|
|
+// for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
+// if attr.attribute.volatile {
|
|
|
+// volatile_mapping.push((n.id, i));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// OwnedDynamicNodeMapping::new(
|
|
|
+// node_mapping,
|
|
|
+// text_mapping,
|
|
|
+// attribute_mapping,
|
|
|
+// volatile_mapping,
|
|
|
+// listener_mapping,
|
|
|
+// )
|
|
|
+// }
|
|
|
+
|
|
|
+// fn dynamic_path(&self) -> Option<OwnedPathSeg> {
|
|
|
+// let mut last_seg: Option<OwnedPathSeg> = None;
|
|
|
+// let mut nodes_to_insert_after = Vec::new();
|
|
|
+// // iterating from the last root to the first
|
|
|
+// for root in self.root_nodes.iter().rev() {
|
|
|
+// let root_node = &self.nodes[root.0];
|
|
|
+// if let TemplateNodeTypeBuilder::DynamicNode(_) = root_node.node_type {
|
|
|
+// match &mut last_seg {
|
|
|
+// // if there has been no static nodes, we can append the child to the parent node
|
|
|
+// None => nodes_to_insert_after.push(*root),
|
|
|
+// // otherwise we insert the child before the last static node
|
|
|
+// Some(seg) => {
|
|
|
+// seg.ops.push(UpdateOp::InsertBefore(*root));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else if let Some(mut new) = self.construct_path_segment(*root) {
|
|
|
+// if let Some(last) = last_seg.take() {
|
|
|
+// match new.traverse {
|
|
|
+// OwnedTraverse::Halt => {
|
|
|
+// new.traverse = OwnedTraverse::NextSibling(Box::new(last));
|
|
|
+// }
|
|
|
+// OwnedTraverse::FirstChild(b) => {
|
|
|
+// new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
|
|
|
+// }
|
|
|
+// _ => unreachable!(),
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// for node in nodes_to_insert_after.drain(..) {
|
|
|
+// new.ops.push(UpdateOp::InsertAfter(node));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// last_seg = Some(new);
|
|
|
+// } else if let Some(last) = last_seg.take() {
|
|
|
+// last_seg = Some(OwnedPathSeg {
|
|
|
+// ops: Vec::new(),
|
|
|
+// traverse: OwnedTraverse::NextSibling(Box::new(last)),
|
|
|
+// });
|
|
|
+// }
|
|
|
+// }
|
|
|
+// last_seg
|
|
|
+// }
|
|
|
+
|
|
|
+// fn construct_path_segment(&self, node_id: TemplateNodeId) -> Option<OwnedPathSeg> {
|
|
|
+// let n = &self.nodes[node_id.0];
|
|
|
+// if n.fully_static {
|
|
|
+// return None;
|
|
|
+// }
|
|
|
+// match &n.node_type {
|
|
|
+// TemplateNodeTypeBuilder::Element(el) => {
|
|
|
+// let mut last_seg: Option<OwnedPathSeg> = None;
|
|
|
+// let mut children_to_append = Vec::new();
|
|
|
+// // iterating from the last child to the first
|
|
|
+// for child in el.children.iter().rev() {
|
|
|
+// let child_node = &self.nodes[child.0];
|
|
|
+// if let TemplateNodeTypeBuilder::DynamicNode(_) = child_node.node_type {
|
|
|
+// match &mut last_seg {
|
|
|
+// // if there has been no static nodes, we can append the child to the parent node
|
|
|
+// None => children_to_append.push(*child),
|
|
|
+// // otherwise we insert the child before the last static node
|
|
|
+// Some(seg) => {
|
|
|
+// seg.ops.push(UpdateOp::InsertBefore(*child));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else if let Some(mut new) = self.construct_path_segment(*child) {
|
|
|
+// if let Some(last) = last_seg.take() {
|
|
|
+// match new.traverse {
|
|
|
+// OwnedTraverse::Halt => {
|
|
|
+// new.traverse = OwnedTraverse::NextSibling(Box::new(last));
|
|
|
+// }
|
|
|
+// OwnedTraverse::FirstChild(b) => {
|
|
|
+// new.traverse = OwnedTraverse::Both(Box::new((*b, last)));
|
|
|
+// }
|
|
|
+// _ => unreachable!(),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// last_seg = Some(new);
|
|
|
+// } else if let Some(last) = last_seg.take() {
|
|
|
+// last_seg = Some(OwnedPathSeg {
|
|
|
+// ops: Vec::new(),
|
|
|
+// traverse: OwnedTraverse::NextSibling(Box::new(last)),
|
|
|
+// });
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut ops = Vec::new();
|
|
|
+// if !el.locally_static || n.parent.is_none() {
|
|
|
+// ops.push(UpdateOp::StoreNode(node_id));
|
|
|
+// }
|
|
|
+// for child in children_to_append.into_iter().rev() {
|
|
|
+// ops.push(UpdateOp::AppendChild(child));
|
|
|
+// }
|
|
|
+// Some(OwnedPathSeg {
|
|
|
+// ops,
|
|
|
+// traverse: match last_seg {
|
|
|
+// Some(last) => OwnedTraverse::FirstChild(Box::new(last)),
|
|
|
+// None => OwnedTraverse::Halt,
|
|
|
+// },
|
|
|
+// })
|
|
|
+// }
|
|
|
+// TemplateNodeTypeBuilder::Text(_) => Some(OwnedPathSeg {
|
|
|
+// ops: vec![UpdateOp::StoreNode(n.id)],
|
|
|
+// traverse: dioxus_core::OwnedTraverse::Halt,
|
|
|
+// }),
|
|
|
+// TemplateNodeTypeBuilder::DynamicNode(_) => unreachable!(
|
|
|
+// "constructing path segment for dynamic nodes is handled in the parent node"
|
|
|
+// ),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// impl ToTokens for TemplateBuilder {
|
|
|
+// fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
+// let Self {
|
|
|
+// nodes,
|
|
|
+// root_nodes,
|
|
|
+// dynamic_context,
|
|
|
+// } = self;
|
|
|
+
|
|
|
+// let mut node_mapping = vec![None; dynamic_context.nodes.len()];
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::DynamicNode(idx) = &n.node_type {
|
|
|
+// node_mapping[*idx] = Some(n.id);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut text_mapping = vec![Vec::new(); dynamic_context.text.len()];
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::Text(txt) = &n.node_type {
|
|
|
+// for seg in &txt.segments {
|
|
|
+// match seg {
|
|
|
+// TextTemplateSegment::Static(_) => (),
|
|
|
+// TextTemplateSegment::Dynamic(idx) => text_mapping[*idx].push(n.id),
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut attribute_mapping = vec![Vec::new(); dynamic_context.attributes.len()];
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
+// for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
+// match attr.value {
|
|
|
+// TemplateAttributeValue::Static(_) => (),
|
|
|
+// TemplateAttributeValue::Dynamic(idx) => {
|
|
|
+// attribute_mapping[idx].push((n.id, i));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// let mut listener_mapping = Vec::new();
|
|
|
+// for n in nodes {
|
|
|
+// if let TemplateNodeTypeBuilder::Element(el) = &n.node_type {
|
|
|
+// if !el.listeners.is_empty() {
|
|
|
+// listener_mapping.push(n.id);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// let root_nodes = root_nodes.iter().map(|id| {
|
|
|
+// let raw = id.0;
|
|
|
+// quote! { TemplateNodeId(#raw) }
|
|
|
+// });
|
|
|
+// let node_mapping_quoted = node_mapping.iter().map(|op| match op {
|
|
|
+// Some(id) => {
|
|
|
+// let raw_id = id.0;
|
|
|
+// quote! {Some(TemplateNodeId(#raw_id))}
|
|
|
+// }
|
|
|
+// None => quote! {None},
|
|
|
+// });
|
|
|
+// let text_mapping_quoted = text_mapping.iter().map(|inner| {
|
|
|
+// let raw = inner.iter().map(|id| id.0);
|
|
|
+// quote! {&[#(TemplateNodeId(#raw)),*]}
|
|
|
+// });
|
|
|
+// let attribute_mapping_quoted = attribute_mapping.iter().map(|inner| {
|
|
|
+// let raw = inner.iter().map(|(id, _)| id.0);
|
|
|
+// let indecies = inner.iter().map(|(_, idx)| idx);
|
|
|
+// quote! {&[#((TemplateNodeId(#raw), #indecies)),*]}
|
|
|
+// });
|
|
|
+// let listener_mapping_quoted = listener_mapping.iter().map(|id| {
|
|
|
+// let raw = id.0;
|
|
|
+// quote! {TemplateNodeId(#raw)}
|
|
|
+// });
|
|
|
+// let mut nodes_quoted = TokenStream::new();
|
|
|
+// for n in nodes {
|
|
|
+// n.to_tokens(&mut nodes_quoted);
|
|
|
+// quote! {,}.to_tokens(&mut nodes_quoted);
|
|
|
+// }
|
|
|
+
|
|
|
+// let dynamic_path = match self.dynamic_path() {
|
|
|
+// Some(seg) => {
|
|
|
+// let seg = quote_owned_segment(seg);
|
|
|
+// quote! {Some(#seg)}
|
|
|
+// }
|
|
|
+// None => quote! {None},
|
|
|
+// };
|
|
|
+
|
|
|
+// let quoted = quote! {
|
|
|
+// {
|
|
|
+// const __NODES: dioxus::prelude::StaticTemplateNodes = &[#nodes_quoted];
|
|
|
+// const __TEXT_MAPPING: &'static [&'static [dioxus::prelude::TemplateNodeId]] = &[#(#text_mapping_quoted),*];
|
|
|
+// const __ATTRIBUTE_MAPPING: &'static [&'static [(dioxus::prelude::TemplateNodeId, usize)]] = &[#(#attribute_mapping_quoted),*];
|
|
|
+// const __ROOT_NODES: &'static [dioxus::prelude::TemplateNodeId] = &[#(#root_nodes),*];
|
|
|
+// const __NODE_MAPPING: &'static [Option<dioxus::prelude::TemplateNodeId>] = &[#(#node_mapping_quoted),*];
|
|
|
+// const __NODES_WITH_LISTENERS: &'static [dioxus::prelude::TemplateNodeId] = &[#(#listener_mapping_quoted),*];
|
|
|
+// static __VOLITALE_MAPPING_INNER: dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = dioxus::core::exports::once_cell::sync::Lazy::new(||{
|
|
|
+// // check each property to see if it is volatile
|
|
|
+// let mut volatile = Vec::new();
|
|
|
+// for n in __NODES {
|
|
|
+// if let TemplateNodeType::Element(el) = &n.node_type {
|
|
|
+// for (i, attr) in el.attributes.iter().enumerate() {
|
|
|
+// if attr.attribute.volatile {
|
|
|
+// volatile.push((n.id, i));
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// volatile
|
|
|
+// });
|
|
|
+// static __VOLITALE_MAPPING: &'static dioxus::core::exports::once_cell::sync::Lazy<Vec<(dioxus::prelude::TemplateNodeId, usize)>> = &__VOLITALE_MAPPING_INNER;
|
|
|
+// static __STATIC_VOLITALE_MAPPING: dioxus::prelude::LazyStaticVec<(dioxus::prelude::TemplateNodeId, usize)> = LazyStaticVec(__VOLITALE_MAPPING);
|
|
|
+// static __TEMPLATE: dioxus::prelude::Template = Template::Static(&StaticTemplate {
|
|
|
+// nodes: __NODES,
|
|
|
+// root_nodes: __ROOT_NODES,
|
|
|
+// dynamic_mapping: StaticDynamicNodeMapping::new(__NODE_MAPPING, __TEXT_MAPPING, __ATTRIBUTE_MAPPING, __STATIC_VOLITALE_MAPPING, __NODES_WITH_LISTENERS),
|
|
|
+// dynamic_path: #dynamic_path,
|
|
|
+// });
|
|
|
+
|
|
|
+// let __bump = __cx.bump();
|
|
|
+// __cx.template_ref(dioxus::prelude::TemplateId(get_line_num!()), __TEMPLATE.clone(), #dynamic_context)
|
|
|
+// }
|
|
|
+// };
|
|
|
+
|
|
|
+// tokens.append_all(quoted)
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// #[derive(Default, Clone, Debug)]
|
|
|
+// pub struct DynamicTemplateContextBuilder {
|
|
|
+// nodes: Vec<BodyNode>,
|
|
|
+// text: Vec<FormattedSegment>,
|
|
|
+// attributes: Vec<TokenStream>,
|
|
|
+// listeners: Vec<(String, Expr)>,
|
|
|
+// key: Option<TokenStream>,
|
|
|
+// }
|
|
|
+
|
|
|
+// impl DynamicTemplateContextBuilder {
|
|
|
+// fn add_node(&mut self, node: BodyNode) -> usize {
|
|
|
+// let node_id = self.nodes.len();
|
|
|
+
|
|
|
+// self.nodes.push(node);
|
|
|
+
|
|
|
+// node_id
|
|
|
+// }
|
|
|
+
|
|
|
+// fn add_text(&mut self, text: FormattedSegment) -> usize {
|
|
|
+// let text_id = self.text.len();
|
|
|
+
|
|
|
+// self.text.push(text);
|
|
|
+
|
|
|
+// text_id
|
|
|
+// }
|
|
|
+
|
|
|
+// fn add_attr(&mut self, attr: TokenStream) -> usize {
|
|
|
+// let attr_id = self.attributes.len();
|
|
|
+
|
|
|
+// self.attributes.push(attr);
|
|
|
+
|
|
|
+// attr_id
|
|
|
+// }
|
|
|
+
|
|
|
+// fn add_listener(&mut self, name: Ident, listener: Expr) -> usize {
|
|
|
+// let listener_id = self.listeners.len();
|
|
|
+
|
|
|
+// self.listeners.push((name.to_string(), listener));
|
|
|
+
|
|
|
+// listener_id
|
|
|
+// }
|
|
|
+
|
|
|
+// fn add_key(&mut self, key: TokenStream) {
|
|
|
+// self.key = Some(key);
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// impl ToTokens for DynamicTemplateContextBuilder {
|
|
|
+// fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
|
+// let nodes = &self.nodes;
|
|
|
+// let text = &self.text;
|
|
|
+// let attributes = &self.attributes;
|
|
|
+// let listeners_names = self
|
|
|
+// .listeners
|
|
|
+// .iter()
|
|
|
+// .map(|(n, _)| syn::parse_str::<Ident>(n).expect(n));
|
|
|
+// let listeners_exprs = self.listeners.iter().map(|(_, e)| e);
|
|
|
+// let key = match &self.key {
|
|
|
+// Some(k) => quote!(Some(#k)),
|
|
|
+// None => quote!(None),
|
|
|
+// };
|
|
|
+// tokens.append_all(quote! {
|
|
|
+// TemplateContext {
|
|
|
+// nodes: __cx.bump().alloc([#(#nodes),*]),
|
|
|
+// text_segments: __cx.bump().alloc([#(&*dioxus::core::exports::bumpalo::format!(in __bump, "{}", #text).into_bump_str()),*]),
|
|
|
+// attributes: __cx.bump().alloc([#({#attributes}.into_value(__cx.bump())),*]),
|
|
|
+// listeners: __cx.bump().alloc([#(dioxus_elements::on::#listeners_names(__cx, #listeners_exprs)),*]),
|
|
|
+// key: #key,
|
|
|
+// }
|
|
|
+// })
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// fn quote_owned_segment(seg: OwnedPathSeg) -> proc_macro2::TokenStream {
|
|
|
+// let OwnedPathSeg { ops, traverse } = seg;
|
|
|
+
|
|
|
+// let ops = ops
|
|
|
+// .into_iter()
|
|
|
+// .map(|op| match op {
|
|
|
+// UpdateOp::StoreNode(id) => {
|
|
|
+// let id = quote_template_node_id(id);
|
|
|
+// quote!(UpdateOp::StoreNode(#id))
|
|
|
+// }
|
|
|
+// UpdateOp::InsertBefore(id) => {
|
|
|
+// let id = quote_template_node_id(id);
|
|
|
+// quote!(UpdateOp::InsertBefore(#id))
|
|
|
+// }
|
|
|
+// UpdateOp::InsertAfter(id) => {
|
|
|
+// let id = quote_template_node_id(id);
|
|
|
+// quote!(UpdateOp::InsertAfter(#id))
|
|
|
+// }
|
|
|
+// UpdateOp::AppendChild(id) => {
|
|
|
+// let id = quote_template_node_id(id);
|
|
|
+// quote!(UpdateOp::AppendChild(#id))
|
|
|
+// }
|
|
|
+// })
|
|
|
+// .collect::<Vec<_>>();
|
|
|
+
|
|
|
+// let traverse = quote_owned_traverse(traverse);
|
|
|
+
|
|
|
+// quote! {
|
|
|
+// StaticPathSeg {
|
|
|
+// ops: &[#(#ops),*],
|
|
|
+// traverse: #traverse,
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// fn quote_owned_traverse(traverse: OwnedTraverse) -> proc_macro2::TokenStream {
|
|
|
+// match traverse {
|
|
|
+// OwnedTraverse::Halt => {
|
|
|
+// quote! {StaticTraverse::Halt}
|
|
|
+// }
|
|
|
+// OwnedTraverse::FirstChild(seg) => {
|
|
|
+// let seg = quote_owned_segment(*seg);
|
|
|
+// quote! {StaticTraverse::FirstChild(&#seg)}
|
|
|
+// }
|
|
|
+// OwnedTraverse::NextSibling(seg) => {
|
|
|
+// let seg = quote_owned_segment(*seg);
|
|
|
+// quote! {StaticTraverse::NextSibling(&#seg)}
|
|
|
+// }
|
|
|
+// OwnedTraverse::Both(b) => {
|
|
|
+// let (child, sibling) = *b;
|
|
|
+// let child = quote_owned_segment(child);
|
|
|
+// let sibling = quote_owned_segment(sibling);
|
|
|
+// quote! {StaticTraverse::Both(&(#child, #sibling))}
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// fn quote_template_node_id(id: TemplateNodeId) -> proc_macro2::TokenStream {
|
|
|
+// let raw = id.0;
|
|
|
+// quote! {
|
|
|
+// TemplateNodeId(#raw)
|
|
|
+// }
|
|
|
+// }
|