element.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. use super::*;
  2. use proc_macro2::TokenStream as TokenStream2;
  3. use quote::{quote, ToTokens, TokenStreamExt};
  4. use syn::{
  5. ext::IdentExt,
  6. parse::{discouraged::Speculative, Parse, ParseBuffer, ParseStream},
  7. token, Error, Expr, ExprClosure, Ident, LitStr, Result, Token,
  8. };
  9. // =======================================
  10. // Parse the VNode::Element type
  11. // =======================================
  12. pub struct Element {
  13. name: Ident,
  14. key: Option<AttrType>,
  15. attributes: Vec<ElementAttr>,
  16. listeners: Vec<ElementAttr>,
  17. children: Vec<BodyNode>,
  18. is_static: bool,
  19. }
  20. impl ToTokens for Element {
  21. fn to_tokens(&self, tokens: &mut TokenStream2) {
  22. let name = &self.name;
  23. let attr = &self.attributes;
  24. let childs = &self.children;
  25. let listeners = &self.listeners;
  26. tokens.append_all(quote! {
  27. __cx.element(
  28. dioxus_elements::#name,
  29. __cx.bump().alloc([ #(#listeners),* ]),
  30. __cx.bump().alloc([ #(#attr),* ]),
  31. __cx.bump().alloc([ #(#childs),* ]),
  32. None,
  33. )
  34. });
  35. }
  36. }
  37. impl Parse for Element {
  38. fn parse(stream: ParseStream) -> Result<Self> {
  39. let name = Ident::parse(stream)?;
  40. // parse the guts
  41. let content: ParseBuffer;
  42. syn::braced!(content in stream);
  43. let mut attributes: Vec<ElementAttr> = vec![];
  44. let mut listeners: Vec<ElementAttr> = vec![];
  45. let mut children: Vec<BodyNode> = vec![];
  46. let mut key = None;
  47. 'parsing: loop {
  48. // [1] Break if empty
  49. if content.is_empty() {
  50. break 'parsing;
  51. }
  52. if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  53. parse_element_body(
  54. &content,
  55. &mut attributes,
  56. &mut listeners,
  57. &mut key,
  58. name.clone(),
  59. )?;
  60. } else {
  61. children.push(content.parse::<BodyNode>()?);
  62. }
  63. // consume comma if it exists
  64. // we don't actually care if there *are* commas after elements/text
  65. if content.peek(Token![,]) {
  66. let _ = content.parse::<Token![,]>();
  67. }
  68. }
  69. Ok(Self {
  70. key,
  71. name,
  72. attributes,
  73. children,
  74. listeners,
  75. is_static: false,
  76. })
  77. }
  78. }
  79. /// =======================================
  80. /// Parse a VElement's Attributes
  81. /// =======================================
  82. struct ElementAttr {
  83. element_name: Ident,
  84. name: Ident,
  85. value: AttrType,
  86. namespace: Option<String>,
  87. }
  88. enum AttrType {
  89. BumpText(LitStr),
  90. FieldTokens(Expr),
  91. EventTokens(Expr),
  92. Event(ExprClosure),
  93. }
  94. // We parse attributes and dump them into the attribute vec
  95. // This is because some tags might be namespaced (IE style)
  96. // These dedicated tags produce multiple name-spaced attributes
  97. fn parse_element_body(
  98. stream: ParseStream,
  99. attrs: &mut Vec<ElementAttr>,
  100. listeners: &mut Vec<ElementAttr>,
  101. key: &mut Option<AttrType>,
  102. element_name: Ident,
  103. ) -> Result<()> {
  104. let mut name = Ident::parse_any(stream)?;
  105. let name_str = name.to_string();
  106. stream.parse::<Token![:]>()?;
  107. // Return early if the field is a listener
  108. if name_str.starts_with("on") {
  109. // remove the "on" bit
  110. // name = Ident::new(&name_str.trim_start_matches("on"), name.span());
  111. let ty = if stream.peek(token::Brace) {
  112. let content;
  113. syn::braced!(content in stream);
  114. // Try to parse directly as a closure
  115. let fork = content.fork();
  116. if let Ok(event) = fork.parse::<ExprClosure>() {
  117. content.advance_to(&fork);
  118. AttrType::Event(event)
  119. } else {
  120. AttrType::EventTokens(content.parse()?)
  121. }
  122. } else {
  123. AttrType::Event(stream.parse()?)
  124. };
  125. listeners.push(ElementAttr {
  126. name,
  127. value: ty,
  128. namespace: None,
  129. element_name: element_name.clone(),
  130. });
  131. return Ok(());
  132. }
  133. let ty: AttrType = match name_str.as_str() {
  134. // short circuit early if style is using the special syntax
  135. "style" if stream.peek(token::Brace) => {
  136. let inner;
  137. syn::braced!(inner in stream);
  138. while !inner.is_empty() {
  139. let name = Ident::parse_any(&inner)?;
  140. inner.parse::<Token![:]>()?;
  141. let ty = if inner.peek(LitStr) {
  142. let rawtext = inner.parse::<LitStr>().unwrap();
  143. AttrType::BumpText(rawtext)
  144. } else {
  145. let toks = inner.parse::<Expr>()?;
  146. AttrType::FieldTokens(toks)
  147. };
  148. if inner.peek(Token![,]) {
  149. let _ = inner.parse::<Token![,]>();
  150. }
  151. attrs.push(ElementAttr {
  152. name,
  153. value: ty,
  154. namespace: Some("style".to_string()),
  155. element_name: element_name.clone(),
  156. });
  157. }
  158. return Ok(());
  159. }
  160. "key" => {
  161. *key = Some(AttrType::BumpText(stream.parse::<LitStr>()?));
  162. return Ok(());
  163. }
  164. "classes" => {
  165. todo!("custom class lsit not supported")
  166. }
  167. "namespace" => {
  168. todo!("custom namespace not supported")
  169. }
  170. "ref" => {
  171. todo!("NodeRefs are currently not supported! This is currently a reserved keyword.")
  172. }
  173. // Fall through
  174. _ => {
  175. if stream.peek(LitStr) {
  176. let rawtext = stream.parse::<LitStr>().unwrap();
  177. AttrType::BumpText(rawtext)
  178. } else {
  179. let toks = stream.parse::<Expr>()?;
  180. AttrType::FieldTokens(toks)
  181. }
  182. }
  183. };
  184. // consume comma if it exists
  185. // we don't actually care if there *are* commas between attrs
  186. if stream.peek(Token![,]) {
  187. let _ = stream.parse::<Token![,]>();
  188. }
  189. attrs.push(ElementAttr {
  190. name,
  191. value: ty,
  192. namespace: None,
  193. element_name,
  194. });
  195. Ok(())
  196. }
  197. impl ToTokens for ElementAttr {
  198. fn to_tokens(&self, tokens: &mut TokenStream2) {
  199. let el_name = &self.element_name;
  200. let name_str = self.name.to_string();
  201. let nameident = &self.name;
  202. let namespace = match &self.namespace {
  203. Some(t) => quote! { Some(#t) },
  204. None => quote! { None },
  205. };
  206. match &self.value {
  207. AttrType::BumpText(value) => tokens.append_all(quote! {
  208. dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value))
  209. }),
  210. AttrType::FieldTokens(exp) => tokens.append_all(quote! {
  211. dioxus_elements::#el_name.#nameident(__cx, #exp)
  212. }),
  213. // todo: move event handlers on to the elements or onto the nodefactory
  214. AttrType::Event(event) => tokens.append_all(quote! {
  215. dioxus::events::on::#nameident(__cx, #event)
  216. }),
  217. AttrType::EventTokens(event) => tokens.append_all(quote! {
  218. dioxus::events::on::#nameident(__cx, #event)
  219. }),
  220. }
  221. }
  222. }
  223. // __cx.attr(#name, format_args_f!(#value), #namespace, false)
  224. //
  225. // AttrType::BumpText(value) => tokens.append_all(quote! {
  226. // __cx.attr(#name, format_args_f!(#value), #namespace, false)
  227. // }),
  228. // __cx.attr(#name_str, #exp, #namespace, false)
  229. // AttrType::FieldTokens(exp) => tokens.append_all(quote! {
  230. // dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value))
  231. // __cx.attr(#name_str, #exp, #namespace, false)
  232. // }),