2022-04-24 02:35:52 -04:00
//! Parse the root tokens in the rsx!{} macro
//! =========================================
//!
//! This parsing path emerges directly from the macro call, with `RsxRender` being the primary entrance into parsing.
//! This feature must support:
//! - [x] Optionally rendering if the `in XYZ` pattern is present
//! - [x] Fragments as top-level element (through ambiguous)
//! - [x] Components as top-level element (through ambiguous)
//! - [x] Tags as top-level elements (through ambiguous)
//! - [x] Good errors if parsing fails
//!
//! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
#[ macro_use ]
mod errors ;
mod component ;
mod element ;
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-23 15:42:34 -06:00
pub mod hot_reload ;
2022-05-25 08:58:59 -05:00
mod ifmt ;
2022-04-24 02:35:52 -04:00
mod node ;
2023-07-19 10:19:23 -07:00
use std ::{ fmt ::Debug , hash ::Hash } ;
2022-12-10 16:21:31 -06:00
2022-04-24 02:35:52 -04:00
// Re-export the namespaces into each other
pub use component ::* ;
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 12:29:15 -06:00
use dioxus_core ::{ Template , TemplateAttribute , TemplateNode } ;
2022-04-24 02:35:52 -04:00
pub use element ::* ;
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-23 15:42:34 -06:00
pub use hot_reload ::HotReloadingContext ;
2022-05-25 08:58:59 -05:00
pub use ifmt ::* ;
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 16:21:31 -06:00
use internment ::Intern ;
2022-04-24 02:35:52 -04:00
pub use node ::* ;
// imports
use proc_macro2 ::TokenStream as TokenStream2 ;
use quote ::{ quote , ToTokens , TokenStreamExt } ;
use syn ::{
parse ::{ Parse , ParseStream } ,
2022-09-25 01:05:16 -07:00
Result , Token ,
2022-04-24 02:35:52 -04:00
} ;
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 16:21:31 -06:00
// interns a object into a static object, resusing the value if it already exists
2022-12-10 21:18:44 -06:00
fn intern < T : Eq + Hash + Send + Sync + ? Sized + 'static > ( s : impl Into < Intern < T > > ) -> & 'static T {
2022-12-10 16:21:31 -06:00
s . into ( ) . as_ref ( )
}
2022-10-20 09:56:09 -07:00
/// Fundametnally, every CallBody is a template
2022-12-11 09:38:38 -06:00
#[ derive(Default, Debug) ]
2022-04-24 02:35:52 -04:00
pub struct CallBody {
pub roots : Vec < BodyNode > ,
}
2022-12-23 18:06:47 -06:00
impl CallBody {
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 16:21:31 -06:00
/// This will try to create a new template from the current body and the previous body. This will return None if the rsx has some dynamic part that has changed.
2022-12-10 12:29:15 -06:00
/// This function intentionally leaks memory to create a static template.
/// Keeping the template static allows us to simplify the core of dioxus and leaking memory in dev mode is less of an issue.
/// the previous_location is the location of the previous template at the time the template was originally compiled.
2022-12-23 18:06:47 -06:00
pub fn update_template < Ctx : HotReloadingContext > (
2022-12-10 16:21:31 -06:00
& self ,
2022-12-23 18:06:47 -06:00
template : Option < CallBody > ,
2022-12-10 16:21:31 -06:00
location : & 'static str ,
2022-12-19 09:37:15 -06:00
) -> Option < Template < 'static > > {
2023-06-29 12:52:27 -07:00
let mut renderer : TemplateRenderer = TemplateRenderer {
roots : & self . roots ,
location : None ,
} ;
2022-12-23 18:06:47 -06:00
renderer . update_template ::< Ctx > ( template , location )
2022-12-10 12:29:15 -06:00
}
2023-06-29 12:52:27 -07:00
/// Render the template with a manually set file location. This should be used when multiple rsx! calls are used in the same macro
pub fn render_with_location ( & self , location : String ) -> TokenStream2 {
let body = TemplateRenderer {
roots : & self . roots ,
location : Some ( location ) ,
} ;
quote! {
::dioxus ::core ::LazyNodes ::new ( move | __cx : & ::dioxus ::core ::ScopeState | -> ::dioxus ::core ::VNode {
#body
} )
}
}
2022-12-10 12:29:15 -06:00
}
2022-04-24 02:35:52 -04:00
impl Parse for CallBody {
fn parse ( input : ParseStream ) -> Result < Self > {
let mut roots = Vec ::new ( ) ;
while ! input . is_empty ( ) {
let node = input . parse ::< BodyNode > ( ) ? ;
if input . peek ( Token! [ , ] ) {
let _ = input . parse ::< Token! [ , ] > ( ) ;
}
roots . push ( node ) ;
}
2022-12-23 17:47:57 -05:00
Ok ( Self { roots } )
2022-04-24 02:35:52 -04:00
}
}
/// Serialize the same way, regardless of flavor
impl ToTokens for CallBody {
fn to_tokens ( & self , out_tokens : & mut TokenStream2 ) {
2023-06-29 12:52:27 -07:00
let body = TemplateRenderer {
roots : & self . roots ,
location : None ,
} ;
2022-11-02 01:00:37 -07:00
2022-12-23 17:47:57 -05:00
out_tokens . append_all ( quote! {
::dioxus ::core ::LazyNodes ::new ( move | __cx : & ::dioxus ::core ::ScopeState | -> ::dioxus ::core ::VNode {
#body
2022-11-02 17:29:18 -07:00
} )
2022-12-23 17:47:57 -05:00
} )
}
}
#[ derive(Default, Debug) ]
pub struct RenderCallBody ( pub CallBody ) ;
impl ToTokens for RenderCallBody {
fn to_tokens ( & self , out_tokens : & mut TokenStream2 ) {
2022-12-23 18:06:47 -06:00
let body : TemplateRenderer = TemplateRenderer {
2022-12-23 17:47:57 -05:00
roots : & self . 0. roots ,
2023-06-29 12:52:27 -07:00
location : None ,
2022-12-23 17:47:57 -05:00
} ;
out_tokens . append_all ( quote! {
Some ( {
let __cx = cx ;
#body
2022-11-02 17:29:18 -07:00
} )
2022-12-23 17:47:57 -05:00
} )
2022-11-02 17:29:18 -07:00
}
}
2022-10-20 09:56:09 -07:00
2022-11-02 17:29:18 -07:00
pub struct TemplateRenderer < ' a > {
pub roots : & ' a [ BodyNode ] ,
2023-06-29 12:52:27 -07:00
pub location : Option < String > ,
2022-11-02 17:29:18 -07:00
}
2022-12-23 18:06:47 -06:00
impl < ' a > TemplateRenderer < ' a > {
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-23 18:06:47 -06:00
fn update_template < Ctx : HotReloadingContext > (
2022-12-10 16:21:31 -06:00
& mut self ,
2022-12-23 18:06:47 -06:00
previous_call : Option < CallBody > ,
2022-12-10 16:21:31 -06:00
location : & 'static str ,
) -> Option < Template < 'static > > {
2022-12-11 09:38:38 -06:00
let mut mapping = previous_call . map ( | call | DynamicMapping ::from ( call . roots ) ) ;
2022-12-10 21:18:44 -06:00
2022-12-23 18:06:47 -06:00
let mut context = DynamicContext ::default ( ) ;
2022-12-10 12:29:15 -06:00
2022-12-10 21:18:44 -06:00
let mut roots = Vec ::new ( ) ;
for ( idx , root ) in self . roots . iter ( ) . enumerate ( ) {
context . current_path . push ( idx as u8 ) ;
2022-12-23 18:06:47 -06:00
roots . push ( context . update_node ::< Ctx > ( root , & mut mapping ) ? ) ;
2022-12-10 21:18:44 -06:00
context . current_path . pop ( ) ;
}
2022-12-10 12:29:15 -06:00
2022-12-10 16:21:31 -06:00
Some ( Template {
name : location ,
roots : intern ( roots . as_slice ( ) ) ,
node_paths : intern (
2022-12-10 12:29:15 -06:00
context
. node_paths
. into_iter ( )
2022-12-10 16:21:31 -06:00
. map ( | path | intern ( path . as_slice ( ) ) )
2022-12-10 12:29:15 -06:00
. collect ::< Vec < _ > > ( )
2022-12-10 16:21:31 -06:00
. as_slice ( ) ,
2022-12-10 12:29:15 -06:00
) ,
2022-12-10 16:21:31 -06:00
attr_paths : intern (
2022-12-10 12:29:15 -06:00
context
. attr_paths
. into_iter ( )
2022-12-10 16:21:31 -06:00
. map ( | path | intern ( path . as_slice ( ) ) )
2022-12-10 12:29:15 -06:00
. collect ::< Vec < _ > > ( )
2022-12-10 16:21:31 -06:00
. as_slice ( ) ,
2022-12-10 12:29:15 -06:00
) ,
2022-12-10 16:21:31 -06:00
} )
2022-12-10 12:29:15 -06:00
}
}
2022-11-02 17:29:18 -07:00
impl < ' a > ToTokens for TemplateRenderer < ' a > {
fn to_tokens ( & self , out_tokens : & mut TokenStream2 ) {
2022-12-23 18:06:47 -06:00
let mut context = DynamicContext ::default ( ) ;
2022-11-03 01:24:20 -07:00
let key = match self . roots . get ( 0 ) {
Some ( BodyNode ::Element ( el ) ) if self . roots . len ( ) = = 1 = > el . key . clone ( ) ,
Some ( BodyNode ::Component ( comp ) ) if self . roots . len ( ) = = 1 = > comp . key ( ) . cloned ( ) ,
_ = > None ,
} ;
2022-11-03 01:38:18 -07:00
2022-11-03 01:24:20 -07:00
let key_tokens = match key {
2022-11-30 23:54:30 -05:00
Some ( tok ) = > quote! { Some ( __cx . raw_text ( #tok ) ) } ,
2022-11-03 01:24:20 -07:00
None = > quote! { None } ,
2022-10-20 09:56:09 -07:00
} ;
2022-11-03 20:56:31 -07:00
let spndbg = format! ( " {:?} " , self . roots [ 0 ] . span ( ) ) ;
2022-12-23 18:27:53 -05:00
let root_col = spndbg
. rsplit_once ( " .. " )
. and_then ( | ( _ , after ) | after . split_once ( ')' ) . map ( | ( before , _ ) | before ) )
. unwrap_or_default ( ) ;
2022-11-03 20:56:31 -07:00
2022-11-02 01:00:37 -07:00
let root_printer = self . roots . iter ( ) . enumerate ( ) . map ( | ( idx , root ) | {
context . current_path . push ( idx as u8 ) ;
2022-11-02 17:29:18 -07:00
let out = context . render_static_node ( root ) ;
2022-11-02 01:00:37 -07:00
context . current_path . pop ( ) ;
out
} ) ;
2022-10-20 09:56:09 -07:00
2023-06-29 12:52:27 -07:00
let name = match self . location {
Some ( ref loc ) = > quote! { #loc } ,
None = > quote! {
concat! (
file! ( ) ,
" : " ,
line! ( ) ,
" : " ,
column! ( ) ,
" : " ,
#root_col
)
} ,
} ;
2022-10-20 09:56:09 -07:00
// Render and release the mutable borrow on context
let roots = quote! { #( #root_printer ) , * } ;
2023-08-15 12:19:05 -05:00
let root_count = self . roots . len ( ) ;
2022-10-26 18:04:47 -07:00
let node_printer = & context . dynamic_nodes ;
2022-11-02 17:29:18 -07:00
let dyn_attr_printer = & context . dynamic_attributes ;
2022-11-03 01:38:18 -07:00
let node_paths = context . node_paths . iter ( ) . map ( | it | quote! ( & [ #( #it ) , * ] ) ) ;
let attr_paths = context . attr_paths . iter ( ) . map ( | it | quote! ( & [ #( #it ) , * ] ) ) ;
2022-11-02 01:00:37 -07:00
2022-11-02 17:29:18 -07:00
out_tokens . append_all ( quote! {
2022-11-02 01:00:37 -07:00
static TEMPLATE : ::dioxus ::core ::Template = ::dioxus ::core ::Template {
2023-06-29 12:52:27 -07:00
name : #name ,
2022-11-03 01:24:20 -07:00
roots : & [ #roots ] ,
node_paths : & [ #( #node_paths ) , * ] ,
attr_paths : & [ #( #attr_paths ) , * ] ,
2022-11-02 01:00:37 -07:00
} ;
::dioxus ::core ::VNode {
parent : None ,
2022-11-03 01:24:20 -07:00
key : #key_tokens ,
2022-12-19 19:28:44 -06:00
template : std ::cell ::Cell ::new ( TEMPLATE ) ,
2023-08-15 12:19:05 -05:00
root_ids : dioxus ::core ::exports ::bumpalo ::collections ::Vec ::with_capacity_in ( #root_count , __cx . bump ( ) ) . into ( ) ,
2022-11-02 01:00:37 -07:00
dynamic_nodes : __cx . bump ( ) . alloc ( [ #( #node_printer ) , * ] ) ,
2022-11-02 17:29:18 -07:00
dynamic_attrs : __cx . bump ( ) . alloc ( [ #( #dyn_attr_printer ) , * ] ) ,
2022-11-02 01:00:37 -07:00
}
2022-11-02 17:29:18 -07:00
} ) ;
2022-09-30 14:03:06 -05:00
}
}
2022-12-10 16:21:31 -06:00
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-19 11:48:28 -06:00
#[ derive(Default, Debug) ]
2022-12-10 21:18:44 -06:00
struct DynamicMapping {
2023-07-19 10:19:23 -07:00
attribute_to_idx : std ::collections ::HashMap < ElementAttr , Vec < usize > > ,
2022-12-10 21:18:44 -06:00
last_attribute_idx : usize ,
2023-07-19 10:19:23 -07:00
node_to_idx : std ::collections ::HashMap < BodyNode , Vec < usize > > ,
2022-12-10 21:18:44 -06:00
last_element_idx : usize ,
}
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 21:18:44 -06:00
impl DynamicMapping {
fn from ( nodes : Vec < BodyNode > ) -> Self {
let mut new = Self ::default ( ) ;
for node in nodes {
new . add_node ( node ) ;
}
new
}
2022-12-11 09:38:38 -06:00
fn get_attribute_idx ( & mut self , attr : & ElementAttr ) -> Option < usize > {
2022-12-10 21:18:44 -06:00
self . attribute_to_idx
2022-12-11 09:38:38 -06:00
. get_mut ( attr )
. and_then ( | idxs | idxs . pop ( ) )
2022-12-10 21:18:44 -06:00
}
2022-12-11 09:38:38 -06:00
fn get_node_idx ( & mut self , node : & BodyNode ) -> Option < usize > {
self . node_to_idx . get_mut ( node ) . and_then ( | idxs | idxs . pop ( ) )
2022-12-10 21:18:44 -06:00
}
2022-12-11 09:38:38 -06:00
fn insert_attribute ( & mut self , attr : ElementAttr ) -> usize {
2022-12-10 21:18:44 -06:00
let idx = self . last_attribute_idx ;
self . last_attribute_idx + = 1 ;
2023-08-08 13:43:57 -07:00
self . attribute_to_idx . entry ( attr ) . or_default ( ) . push ( idx ) ;
2022-12-10 21:18:44 -06:00
idx
}
fn insert_node ( & mut self , node : BodyNode ) -> usize {
let idx = self . last_element_idx ;
self . last_element_idx + = 1 ;
2023-08-08 13:43:57 -07:00
self . node_to_idx . entry ( node ) . or_default ( ) . push ( idx ) ;
2022-12-10 21:18:44 -06:00
idx
}
fn add_node ( & mut self , node : BodyNode ) {
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 { .. } = > {
2022-12-11 09:38:38 -06:00
self . insert_attribute ( attr . attr ) ;
2022-12-10 21:18:44 -06:00
}
}
}
for child in el . children {
self . add_node ( child ) ;
}
}
BodyNode ::Text ( text ) if text . is_static ( ) = > { }
BodyNode ::RawExpr ( _ )
| BodyNode ::Text ( _ )
| BodyNode ::ForLoop ( _ )
| BodyNode ::IfChain ( _ )
| BodyNode ::Component ( _ ) = > {
self . insert_node ( node ) ;
}
}
}
}
2022-12-10 16:21:31 -06:00
// As we create the dynamic nodes, we want to keep track of them in a linear fashion
// We'll use the size of the vecs to determine the index of the dynamic node in the final output
2022-12-23 18:06:47 -06:00
#[ derive(Default, Debug) ]
2022-11-02 17:29:18 -07:00
pub struct DynamicContext < ' a > {
dynamic_nodes : Vec < & ' a BodyNode > ,
dynamic_attributes : Vec < & ' a ElementAttrNamed > ,
current_path : Vec < u8 > ,
2022-11-03 01:24:20 -07:00
node_paths : Vec < Vec < u8 > > ,
attr_paths : Vec < Vec < u8 > > ,
2022-12-10 14:09:59 -06:00
}
2022-11-02 17:29:18 -07:00
impl < ' a > DynamicContext < ' a > {
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-23 18:06:47 -06:00
fn update_node < Ctx : HotReloadingContext > (
2022-12-10 21:18:44 -06:00
& mut self ,
root : & ' a BodyNode ,
2022-12-11 09:38:38 -06:00
mapping : & mut Option < DynamicMapping > ,
2022-12-10 21:18:44 -06:00
) -> Option < TemplateNode < 'static > > {
2022-12-10 12:29:15 -06:00
match root {
BodyNode ::Element ( el ) = > {
2022-12-10 14:09:59 -06:00
let element_name_rust = el . name . to_string ( ) ;
2022-12-10 21:18:44 -06:00
let mut static_attrs = Vec ::new ( ) ;
for attr in & el . attributes {
match & attr . attr {
2022-12-10 14:09:59 -06:00
ElementAttr ::AttrText { name , value } if value . is_static ( ) = > {
2022-12-10 12:29:15 -06:00
let value = value . source . as_ref ( ) . unwrap ( ) ;
2022-12-10 14:09:59 -06:00
let attribute_name_rust = name . to_string ( ) ;
let ( name , namespace ) =
Ctx ::map_attribute ( & element_name_rust , & attribute_name_rust )
2022-12-10 16:21:31 -06:00
. unwrap_or ( ( intern ( attribute_name_rust . as_str ( ) ) , None ) ) ;
2022-12-10 21:18:44 -06:00
static_attrs . push ( TemplateAttribute ::Static {
2022-12-10 14:09:59 -06:00
name ,
namespace ,
2022-12-10 16:21:31 -06:00
value : intern ( value . value ( ) . as_str ( ) ) ,
2022-12-10 21:18:44 -06:00
} )
2022-12-10 12:29:15 -06:00
}
ElementAttr ::CustomAttrText { name , value } if value . is_static ( ) = > {
let value = value . source . as_ref ( ) . unwrap ( ) ;
2022-12-10 21:18:44 -06:00
static_attrs . push ( TemplateAttribute ::Static {
2022-12-10 16:21:31 -06:00
name : intern ( name . value ( ) . as_str ( ) ) ,
2022-12-10 12:29:15 -06:00
namespace : None ,
2022-12-10 16:21:31 -06:00
value : intern ( value . value ( ) . as_str ( ) ) ,
2022-12-10 21:18:44 -06:00
} )
2022-12-10 12:29:15 -06:00
}
ElementAttr ::AttrExpression { .. }
| ElementAttr ::AttrText { .. }
| ElementAttr ::CustomAttrText { .. }
| ElementAttr ::CustomAttrExpression { .. }
| ElementAttr ::EventTokens { .. } = > {
2022-12-22 11:36:38 -06:00
let idx = match mapping {
2022-12-11 09:38:38 -06:00
Some ( mapping ) = > mapping . get_attribute_idx ( & attr . attr ) ? ,
2022-12-10 21:18:44 -06:00
None = > self . dynamic_attributes . len ( ) ,
} ;
2022-12-10 12:29:15 -06:00
self . dynamic_attributes . push ( attr ) ;
2022-12-22 11:36:38 -06:00
if self . attr_paths . len ( ) < = idx {
self . attr_paths . resize_with ( idx + 1 , Vec ::new ) ;
}
self . attr_paths [ idx ] = self . current_path . clone ( ) ;
static_attrs . push ( TemplateAttribute ::Dynamic { id : idx } )
2022-12-10 12:29:15 -06:00
}
2022-12-10 21:18:44 -06:00
}
}
let mut children = Vec ::new ( ) ;
for ( idx , root ) in el . children . iter ( ) . enumerate ( ) {
self . current_path . push ( idx as u8 ) ;
2022-12-23 18:06:47 -06:00
children . push ( self . update_node ::< Ctx > ( root , mapping ) ? ) ;
2022-12-10 21:18:44 -06:00
self . current_path . pop ( ) ;
}
2022-12-10 12:29:15 -06:00
2022-12-10 14:09:59 -06:00
let ( tag , namespace ) = Ctx ::map_element ( & element_name_rust )
2022-12-10 16:21:31 -06:00
. unwrap_or ( ( intern ( element_name_rust . as_str ( ) ) , None ) ) ;
2022-12-10 21:18:44 -06:00
Some ( TemplateNode ::Element {
2022-12-10 14:09:59 -06:00
tag ,
namespace ,
2022-12-10 16:21:31 -06:00
attrs : intern ( static_attrs . into_boxed_slice ( ) ) ,
children : intern ( children . as_slice ( ) ) ,
2022-12-10 21:18:44 -06:00
} )
2022-12-10 12:29:15 -06:00
}
BodyNode ::Text ( text ) if text . is_static ( ) = > {
let text = text . source . as_ref ( ) . unwrap ( ) ;
2022-12-10 21:18:44 -06:00
Some ( TemplateNode ::Text {
2022-12-10 16:21:31 -06:00
text : intern ( text . value ( ) . as_str ( ) ) ,
2022-12-10 21:18:44 -06:00
} )
2022-12-10 12:29:15 -06:00
}
BodyNode ::RawExpr ( _ )
| BodyNode ::Text ( _ )
| BodyNode ::ForLoop ( _ )
| BodyNode ::IfChain ( _ )
| BodyNode ::Component ( _ ) = > {
2022-12-22 11:36:38 -06:00
let idx = match mapping {
2022-12-10 21:18:44 -06:00
Some ( mapping ) = > mapping . get_node_idx ( root ) ? ,
None = > self . dynamic_nodes . len ( ) ,
} ;
2022-12-10 12:29:15 -06:00
self . dynamic_nodes . push ( root ) ;
2022-12-22 11:36:38 -06:00
if self . node_paths . len ( ) < = idx {
self . node_paths . resize_with ( idx + 1 , Vec ::new ) ;
}
self . node_paths [ idx ] = self . current_path . clone ( ) ;
2022-12-10 12:29:15 -06:00
2022-12-10 21:18:44 -06:00
Some ( match root {
2022-12-22 11:36:38 -06:00
BodyNode ::Text ( _ ) = > TemplateNode ::DynamicText { id : idx } ,
_ = > TemplateNode ::Dynamic { id : idx } ,
2022-12-10 21:18:44 -06:00
} )
2022-12-10 12:29:15 -06:00
}
}
}
2022-11-02 17:29:18 -07:00
fn render_static_node ( & mut self , root : & ' a BodyNode ) -> TokenStream2 {
match root {
BodyNode ::Element ( el ) = > {
let el_name = & el . name ;
2023-03-19 20:37:40 -05:00
let ns = | name | match el_name {
ElementName ::Ident ( i ) = > quote! { dioxus_elements ::#i ::#name } ,
ElementName ::Custom ( _ ) = > quote! { None } ,
} ;
2022-12-06 17:50:25 -08:00
let static_attrs = el . attributes . iter ( ) . map ( | attr | match & attr . attr {
2022-11-02 17:29:18 -07:00
ElementAttr ::AttrText { name , value } if value . is_static ( ) = > {
2022-12-10 15:05:41 -06:00
let value = value . to_static ( ) . unwrap ( ) ;
2023-03-19 20:37:40 -05:00
let ns = ns ( quote! ( #name . 1 ) ) ;
2023-03-19 20:47:16 -05:00
let name = match el_name {
2023-03-19 20:37:40 -05:00
ElementName ::Ident ( _ ) = > quote! { #el_name ::#name . 0 } ,
ElementName ::Custom ( _ ) = > {
2023-03-19 20:47:16 -05:00
let as_string = name . to_string ( ) ;
2023-03-19 20:37:40 -05:00
quote! { #as_string }
2023-03-19 20:47:16 -05:00
}
2023-03-19 20:37:40 -05:00
} ;
2022-12-06 17:50:25 -08:00
quote! {
2022-11-02 17:29:18 -07:00
::dioxus ::core ::TemplateAttribute ::Static {
2023-03-19 20:37:40 -05:00
name : #name ,
namespace : #ns ,
2022-11-02 17:29:18 -07:00
value : #value ,
2022-12-02 16:24:49 -08:00
// todo: we don't diff these so we never apply the volatile flag
// volatile: dioxus_elements::#el_name::#name.2,
2022-11-02 17:29:18 -07:00
}
2022-12-06 17:50:25 -08:00
}
2022-11-02 17:29:18 -07:00
}
2022-04-24 02:35:52 -04:00
2022-11-02 17:29:18 -07:00
ElementAttr ::CustomAttrText { name , value } if value . is_static ( ) = > {
2022-12-10 15:05:41 -06:00
let value = value . to_static ( ) . unwrap ( ) ;
2022-12-06 17:50:25 -08:00
quote! {
2022-11-02 17:29:18 -07:00
::dioxus ::core ::TemplateAttribute ::Static {
2022-12-07 15:11:51 -08:00
name : #name ,
namespace : None ,
2022-11-02 17:29:18 -07:00
value : #value ,
2022-12-02 16:24:49 -08:00
// todo: we don't diff these so we never apply the volatile flag
// volatile: dioxus_elements::#el_name::#name.2,
2022-11-02 17:29:18 -07:00
}
2022-12-06 17:50:25 -08:00
}
2022-11-02 17:29:18 -07:00
}
2022-10-20 09:56:09 -07:00
2022-11-02 17:29:18 -07:00
ElementAttr ::AttrExpression { .. }
| ElementAttr ::AttrText { .. }
| ElementAttr ::CustomAttrText { .. }
2022-11-03 01:24:20 -07:00
| ElementAttr ::CustomAttrExpression { .. }
| ElementAttr ::EventTokens { .. } = > {
2022-11-02 17:29:18 -07:00
let ct = self . dynamic_attributes . len ( ) ;
self . dynamic_attributes . push ( attr ) ;
2022-11-03 01:24:20 -07:00
self . attr_paths . push ( self . current_path . clone ( ) ) ;
2022-12-06 17:50:25 -08:00
quote! { ::dioxus ::core ::TemplateAttribute ::Dynamic { id : #ct } }
2022-11-02 17:29:18 -07:00
}
} ) ;
let attrs = quote! { #( #static_attrs ) , * } ;
let children = el . children . iter ( ) . enumerate ( ) . map ( | ( idx , root ) | {
self . current_path . push ( idx as u8 ) ;
let out = self . render_static_node ( root ) ;
self . current_path . pop ( ) ;
out
} ) ;
2022-12-02 16:24:49 -08:00
let _opt = el . children . len ( ) = = 1 ;
2022-11-02 17:29:18 -07:00
let children = quote! { #( #children ) , * } ;
2022-10-20 09:56:09 -07:00
2023-03-19 20:37:40 -05:00
let ns = ns ( quote! ( NAME_SPACE ) ) ;
let el_name = el_name . tag_name ( ) ;
2022-11-02 17:29:18 -07:00
quote! {
::dioxus ::core ::TemplateNode ::Element {
2023-03-19 20:37:40 -05:00
tag : #el_name ,
namespace : #ns ,
2022-11-02 17:29:18 -07:00
attrs : & [ #attrs ] ,
children : & [ #children ] ,
}
2022-11-01 18:42:29 -07:00
}
2022-11-02 17:29:18 -07:00
}
BodyNode ::Text ( text ) if text . is_static ( ) = > {
2022-12-10 15:05:41 -06:00
let text = text . to_static ( ) . unwrap ( ) ;
2022-12-05 16:08:41 -08:00
quote! { ::dioxus ::core ::TemplateNode ::Text { text : #text } }
2022-11-02 17:29:18 -07:00
}
2022-11-16 22:10:13 -08:00
BodyNode ::RawExpr ( _ )
| BodyNode ::Text ( _ )
| BodyNode ::ForLoop ( _ )
| BodyNode ::IfChain ( _ )
| BodyNode ::Component ( _ ) = > {
2022-11-02 17:29:18 -07:00
let ct = self . dynamic_nodes . len ( ) ;
self . dynamic_nodes . push ( root ) ;
2022-11-03 01:24:20 -07:00
self . node_paths . push ( self . current_path . clone ( ) ) ;
2022-11-15 23:22:41 -08:00
2022-11-24 09:11:27 -05:00
match root {
2022-12-05 16:08:41 -08:00
BodyNode ::Text ( _ ) = > {
quote! { ::dioxus ::core ::TemplateNode ::DynamicText { id : #ct } }
}
_ = > quote! { ::dioxus ::core ::TemplateNode ::Dynamic { id : #ct } } ,
2022-11-15 23:22:41 -08:00
}
2022-11-02 17:29:18 -07:00
}
}
2022-10-09 01:24:41 -05:00
}
2022-04-24 02:35:52 -04:00
}
2022-12-10 12:29:15 -06:00
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 12:29:15 -06:00
#[ test ]
2022-12-10 21:18:44 -06:00
fn create_template ( ) {
2022-12-10 12:29:15 -06:00
let input = quote! {
2022-12-10 14:09:59 -06:00
svg {
2022-12-10 12:29:15 -06:00
width : 100 ,
height : " 100px " ,
" width2 " : 100 ,
" height2 " : " 100px " ,
p {
" hello world "
}
( 0 .. 10 ) . map ( | i | rsx! { " {i} " } )
}
} ;
2022-12-10 14:09:59 -06:00
struct Mock ;
impl HotReloadingContext for Mock {
fn map_attribute (
element_name_rust : & str ,
attribute_name_rust : & str ,
) -> Option < ( & 'static str , Option < & 'static str > ) > {
match element_name_rust {
" svg " = > match attribute_name_rust {
" width " = > Some ( ( " width " , Some ( " style " ) ) ) ,
" height " = > Some ( ( " height " , Some ( " style " ) ) ) ,
_ = > None ,
} ,
_ = > None ,
}
}
fn map_element ( element_name_rust : & str ) -> Option < ( & 'static str , Option < & 'static str > ) > {
match element_name_rust {
" svg " = > Some ( ( " svg " , Some ( " svg " ) ) ) ,
_ = > None ,
}
}
}
2022-12-23 18:06:47 -06:00
let call_body : CallBody = syn ::parse2 ( input ) . unwrap ( ) ;
2022-12-10 12:29:15 -06:00
2022-12-23 18:06:47 -06:00
let template = call_body . update_template ::< Mock > ( None , " testing " ) . unwrap ( ) ;
2022-12-10 12:29:15 -06:00
dbg! ( template ) ;
assert_eq! (
template ,
Template {
name : " testing " ,
roots : & [ TemplateNode ::Element {
2022-12-10 14:09:59 -06:00
tag : " svg " ,
namespace : Some ( " svg " ) ,
2022-12-10 12:29:15 -06:00
attrs : & [
TemplateAttribute ::Dynamic { id : 0 } ,
TemplateAttribute ::Static {
name : " height " ,
2022-12-10 14:09:59 -06:00
namespace : Some ( " style " ) ,
2022-12-10 12:29:15 -06:00
value : " 100px " ,
} ,
TemplateAttribute ::Dynamic { id : 1 } ,
TemplateAttribute ::Static {
name : " height2 " ,
namespace : None ,
value : " 100px " ,
} ,
] ,
children : & [
TemplateNode ::Element {
tag : " p " ,
namespace : None ,
attrs : & [ ] ,
children : & [ TemplateNode ::Text {
text : " hello world " ,
} ] ,
} ,
TemplateNode ::Dynamic { id : 0 }
] ,
} ] ,
node_paths : & [ & [ 0 , 1 , ] , ] ,
attr_paths : & [ & [ 0 , ] , & [ 0 , ] , ] ,
} ,
)
}
2022-12-10 21:18:44 -06:00
2023-07-19 10:19:23 -07:00
#[ cfg(feature = " hot_reload " ) ]
2022-12-10 21:18:44 -06:00
#[ test ]
fn diff_template ( ) {
2022-12-20 20:48:28 -06:00
use dioxus_core ::Scope ;
#[ allow(unused, non_snake_case) ]
fn Comp ( _ : Scope ) -> dioxus_core ::Element {
None
}
2022-12-10 21:18:44 -06:00
let input = quote! {
svg {
width : 100 ,
height : " 100px " ,
" width2 " : 100 ,
" height2 " : " 100px " ,
p {
" hello world "
}
( 0 .. 10 ) . map ( | i | rsx! { " {i} " } ) ,
2022-12-11 09:38:38 -06:00
( 0 .. 10 ) . map ( | i | rsx! { " {i} " } ) ,
( 0 .. 11 ) . map ( | i | rsx! { " {i} " } ) ,
2022-12-20 20:48:28 -06:00
Comp { }
2022-12-10 21:18:44 -06:00
}
} ;
2022-12-11 09:38:38 -06:00
#[ derive(Debug) ]
2022-12-10 21:18:44 -06:00
struct Mock ;
impl HotReloadingContext for Mock {
fn map_attribute (
element_name_rust : & str ,
attribute_name_rust : & str ,
) -> Option < ( & 'static str , Option < & 'static str > ) > {
match element_name_rust {
" svg " = > match attribute_name_rust {
" width " = > Some ( ( " width " , Some ( " style " ) ) ) ,
" height " = > Some ( ( " height " , Some ( " style " ) ) ) ,
_ = > None ,
} ,
_ = > None ,
}
}
fn map_element ( element_name_rust : & str ) -> Option < ( & 'static str , Option < & 'static str > ) > {
match element_name_rust {
" svg " = > Some ( ( " svg " , Some ( " svg " ) ) ) ,
_ = > None ,
}
}
}
2022-12-23 18:06:47 -06:00
let call_body1 : CallBody = syn ::parse2 ( input ) . unwrap ( ) ;
2022-12-10 21:18:44 -06:00
2022-12-23 18:06:47 -06:00
let template = call_body1 . update_template ::< Mock > ( None , " testing " ) . unwrap ( ) ;
2022-12-10 21:18:44 -06:00
dbg! ( template ) ;
// scrambling the attributes should not cause a full rebuild
let input = quote! {
div {
" width2 " : 100 ,
height : " 100px " ,
" height2 " : " 100px " ,
2022-12-11 09:38:38 -06:00
width : 100 ,
2022-12-20 20:48:28 -06:00
Comp { }
2022-12-11 09:38:38 -06:00
( 0 .. 11 ) . map ( | i | rsx! { " {i} " } ) ,
( 0 .. 10 ) . map ( | i | rsx! { " {i} " } ) ,
2022-12-10 21:18:44 -06:00
( 0 .. 10 ) . map ( | i | rsx! { " {i} " } ) ,
p {
" hello world "
}
}
} ;
2022-12-23 18:06:47 -06:00
let call_body2 : CallBody = syn ::parse2 ( input ) . unwrap ( ) ;
2022-12-10 21:18:44 -06:00
let template = call_body2
2022-12-23 18:06:47 -06:00
. update_template ::< Mock > ( Some ( call_body1 ) , " testing " )
2022-12-10 21:18:44 -06:00
. unwrap ( ) ;
2022-12-20 20:48:28 -06:00
dbg! ( template ) ;
2022-12-10 21:18:44 -06:00
2022-12-11 09:38:38 -06:00
assert_eq! (
template ,
Template {
name : " testing " ,
roots : & [ TemplateNode ::Element {
tag : " div " ,
namespace : None ,
attrs : & [
TemplateAttribute ::Dynamic { id : 1 } ,
TemplateAttribute ::Static {
name : " height " ,
namespace : None ,
value : " 100px " ,
} ,
TemplateAttribute ::Static {
name : " height2 " ,
namespace : None ,
value : " 100px " ,
} ,
TemplateAttribute ::Dynamic { id : 0 } ,
] ,
children : & [
2022-12-20 20:48:28 -06:00
TemplateNode ::Dynamic { id : 3 } ,
2022-12-11 09:38:38 -06:00
TemplateNode ::Dynamic { id : 2 } ,
TemplateNode ::Dynamic { id : 1 } ,
TemplateNode ::Dynamic { id : 0 } ,
TemplateNode ::Element {
tag : " p " ,
namespace : None ,
attrs : & [ ] ,
children : & [ TemplateNode ::Text {
text : " hello world " ,
} ] ,
} ,
] ,
} ] ,
2022-12-22 19:32:21 -06:00
node_paths : & [ & [ 0 , 3 ] , & [ 0 , 2 ] , & [ 0 , 1 ] , & [ 0 , 0 ] ] ,
2022-12-11 09:38:38 -06:00
attr_paths : & [ & [ 0 ] , & [ 0 ] ]
} ,
)
2022-12-10 21:18:44 -06:00
}