lib.rs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. //! Parse the root tokens in the rsx!{} macro
  2. //! =========================================
  3. //!
  4. //! This parsing path emerges directly from the macro call, with `RsxRender` being the primary entrance into parsing.
  5. //! This feature must support:
  6. //! - [x] Optionally rendering if the `in XYZ` pattern is present
  7. //! - [x] Fragments as top-level element (through ambiguous)
  8. //! - [x] Components as top-level element (through ambiguous)
  9. //! - [x] Tags as top-level elements (through ambiguous)
  10. //! - [x] Good errors if parsing fails
  11. //!
  12. //! Any errors in using rsx! will likely occur when people start using it, so the first errors must be really helpful.
  13. #[macro_use]
  14. mod errors;
  15. mod component;
  16. mod element;
  17. mod ifmt;
  18. mod node;
  19. mod template;
  20. // Re-export the namespaces into each other
  21. pub use component::*;
  22. pub use element::*;
  23. pub use ifmt::*;
  24. pub use node::*;
  25. // imports
  26. use proc_macro2::TokenStream as TokenStream2;
  27. use quote::{quote, ToTokens, TokenStreamExt};
  28. use syn::{
  29. parse::{Parse, ParseStream},
  30. Result, Token,
  31. };
  32. /// Fundametnally, every CallBody is a template
  33. #[derive(Default, Debug)]
  34. pub struct CallBody {
  35. pub roots: Vec<BodyNode>,
  36. // set this after
  37. pub inline_cx: bool,
  38. }
  39. impl Parse for CallBody {
  40. fn parse(input: ParseStream) -> Result<Self> {
  41. let mut roots = Vec::new();
  42. while !input.is_empty() {
  43. let node = input.parse::<BodyNode>()?;
  44. if input.peek(Token![,]) {
  45. let _ = input.parse::<Token![,]>();
  46. }
  47. roots.push(node);
  48. }
  49. Ok(Self {
  50. roots,
  51. inline_cx: false,
  52. })
  53. }
  54. }
  55. /// Serialize the same way, regardless of flavor
  56. impl ToTokens for CallBody {
  57. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  58. let body = TemplateRenderer { roots: &self.roots };
  59. if self.inline_cx {
  60. out_tokens.append_all(quote! {
  61. Ok({
  62. let __cx = cx;
  63. #body
  64. })
  65. })
  66. } else {
  67. out_tokens.append_all(quote! {
  68. ::dioxus::core::LazyNodes::new( move | __cx: &::dioxus::core::ScopeState| -> ::dioxus::core::VNode {
  69. #body
  70. })
  71. })
  72. }
  73. }
  74. }
  75. pub struct TemplateRenderer<'a> {
  76. pub roots: &'a [BodyNode],
  77. }
  78. impl<'a> ToTokens for TemplateRenderer<'a> {
  79. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  80. let mut context = DynamicContext {
  81. dynamic_nodes: vec![],
  82. dynamic_attributes: vec![],
  83. current_path: vec![],
  84. attr_paths: vec![],
  85. node_paths: vec![],
  86. };
  87. let key = match self.roots.get(0) {
  88. Some(BodyNode::Element(el)) if self.roots.len() == 1 => el.key.clone(),
  89. Some(BodyNode::Component(comp)) if self.roots.len() == 1 => comp.key().cloned(),
  90. _ => None,
  91. };
  92. let key_tokens = match key {
  93. Some(tok) => quote! { Some( __cx.raw_text(#tok) ) },
  94. None => quote! { None },
  95. };
  96. let spndbg = format!("{:?}", self.roots[0].span());
  97. let root_col = spndbg[9..].split("..").next().unwrap();
  98. let root_printer = self.roots.iter().enumerate().map(|(idx, root)| {
  99. context.current_path.push(idx as u8);
  100. let out = context.render_static_node(root);
  101. context.current_path.pop();
  102. out
  103. });
  104. // Render and release the mutable borrow on context
  105. let num_roots = self.roots.len();
  106. let roots = quote! { #( #root_printer ),* };
  107. let node_printer = &context.dynamic_nodes;
  108. let dyn_attr_printer = &context.dynamic_attributes;
  109. let node_paths = context.node_paths.iter().map(|it| quote!(&[#(#it),*]));
  110. let attr_paths = context.attr_paths.iter().map(|it| quote!(&[#(#it),*]));
  111. out_tokens.append_all(quote! {
  112. static TEMPLATE: ::dioxus::core::Template = ::dioxus::core::Template {
  113. name: concat!(
  114. file!(),
  115. ":",
  116. line!(),
  117. ":",
  118. column!(),
  119. ":",
  120. #root_col
  121. ),
  122. roots: &[ #roots ],
  123. node_paths: &[ #(#node_paths),* ],
  124. attr_paths: &[ #(#attr_paths),* ],
  125. };
  126. ::dioxus::core::VNode {
  127. parent: None,
  128. key: #key_tokens,
  129. template: TEMPLATE,
  130. root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut _).as_slice_of_cells(),
  131. // root_ids: std::cell::Cell::from_mut( __cx.bump().alloc([None; #num_roots]) as &mut [::dioxus::core::ElementId]).as_slice_of_cells(),
  132. dynamic_nodes: __cx.bump().alloc([ #( #node_printer ),* ]),
  133. dynamic_attrs: __cx.bump().alloc([ #( #dyn_attr_printer ),* ]),
  134. }
  135. });
  136. }
  137. }
  138. // As we print out the dynamic nodes, we want to keep track of them in a linear fashion
  139. // We'll use the size of the vecs to determine the index of the dynamic node in the final
  140. pub struct DynamicContext<'a> {
  141. dynamic_nodes: Vec<&'a BodyNode>,
  142. dynamic_attributes: Vec<&'a ElementAttrNamed>,
  143. current_path: Vec<u8>,
  144. node_paths: Vec<Vec<u8>>,
  145. attr_paths: Vec<Vec<u8>>,
  146. }
  147. impl<'a> DynamicContext<'a> {
  148. fn render_static_node(&mut self, root: &'a BodyNode) -> TokenStream2 {
  149. match root {
  150. BodyNode::Element(el) => {
  151. let el_name = &el.name;
  152. // dynamic attributes
  153. // [0]
  154. // [0, 1]
  155. // [0, 1]
  156. // [0, 1]
  157. // [0, 1, 2]
  158. // [0, 2]
  159. // [0, 2, 1]
  160. let static_attrs = el.attributes.iter().map(|attr| match &attr.attr {
  161. ElementAttr::AttrText { name, value } if value.is_static() => {
  162. let value = value.to_static().unwrap();
  163. quote! {
  164. ::dioxus::core::TemplateAttribute::Static {
  165. name: dioxus_elements::#el_name::#name.0,
  166. namespace: dioxus_elements::#el_name::#name.1,
  167. value: #value,
  168. // todo: we don't diff these so we never apply the volatile flag
  169. // volatile: dioxus_elements::#el_name::#name.2,
  170. }
  171. }
  172. }
  173. ElementAttr::CustomAttrText { name, value } if value.is_static() => {
  174. let value = value.to_static().unwrap();
  175. quote! {
  176. ::dioxus::core::TemplateAttribute::Static {
  177. name: #name,
  178. namespace: None,
  179. value: #value,
  180. // todo: we don't diff these so we never apply the volatile flag
  181. // volatile: dioxus_elements::#el_name::#name.2,
  182. }
  183. }
  184. }
  185. ElementAttr::AttrExpression { .. }
  186. | ElementAttr::AttrText { .. }
  187. | ElementAttr::CustomAttrText { .. }
  188. | ElementAttr::CustomAttrExpression { .. }
  189. | ElementAttr::EventTokens { .. } => {
  190. let ct = self.dynamic_attributes.len();
  191. self.dynamic_attributes.push(attr);
  192. self.attr_paths.push(self.current_path.clone());
  193. quote! { ::dioxus::core::TemplateAttribute::Dynamic { id: #ct } }
  194. }
  195. });
  196. let attrs = quote! { #(#static_attrs),*};
  197. let children = el.children.iter().enumerate().map(|(idx, root)| {
  198. self.current_path.push(idx as u8);
  199. let out = self.render_static_node(root);
  200. self.current_path.pop();
  201. out
  202. });
  203. let _opt = el.children.len() == 1;
  204. let children = quote! { #(#children),* };
  205. quote! {
  206. ::dioxus::core::TemplateNode::Element {
  207. tag: dioxus_elements::#el_name::TAG_NAME,
  208. namespace: dioxus_elements::#el_name::NAME_SPACE,
  209. attrs: &[ #attrs ],
  210. children: &[ #children ],
  211. }
  212. }
  213. }
  214. BodyNode::Text(text) if text.is_static() => {
  215. let text = text.to_static().unwrap();
  216. quote! { ::dioxus::core::TemplateNode::Text{ text: #text } }
  217. }
  218. BodyNode::RawExpr(_)
  219. | BodyNode::Text(_)
  220. | BodyNode::ForLoop(_)
  221. | BodyNode::IfChain(_)
  222. | BodyNode::Component(_) => {
  223. let ct = self.dynamic_nodes.len();
  224. self.dynamic_nodes.push(root);
  225. self.node_paths.push(self.current_path.clone());
  226. match root {
  227. BodyNode::Text(_) => {
  228. quote! { ::dioxus::core::TemplateNode::DynamicText { id: #ct } }
  229. }
  230. _ => quote! { ::dioxus::core::TemplateNode::Dynamic { id: #ct } },
  231. }
  232. }
  233. }
  234. }
  235. }