element.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  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. attrs: Vec<ElementAttr>,
  15. children: Vec<Node>,
  16. }
  17. impl Parse for Element {
  18. fn parse(stream: ParseStream) -> Result<Self> {
  19. //
  20. let name = Ident::parse(stream)?;
  21. if !crate::util::is_valid_tag(&name.to_string()) {
  22. return Err(Error::new(name.span(), "Not a valid Html tag"));
  23. }
  24. // parse the guts
  25. let content: ParseBuffer;
  26. syn::braced!(content in stream);
  27. let mut attrs: Vec<ElementAttr> = vec![];
  28. let mut children: Vec<Node> = vec![];
  29. 'parsing: loop {
  30. // [1] Break if empty
  31. if content.is_empty() {
  32. break 'parsing;
  33. }
  34. let forked = content.fork();
  35. if forked.call(Ident::parse_any).is_ok()
  36. && forked.parse::<Token![:]>().is_ok()
  37. && forked.parse::<Token![:]>().is_err()
  38. {
  39. attrs.push(content.parse::<ElementAttr>()?);
  40. } else {
  41. children.push(content.parse::<Node>()?);
  42. }
  43. // consume comma if it exists
  44. // we don't actually care if there *are* commas after elements/text
  45. if content.peek(Token![,]) {
  46. let _ = content.parse::<Token![,]>();
  47. }
  48. }
  49. Ok(Self {
  50. name,
  51. attrs,
  52. children,
  53. })
  54. }
  55. }
  56. impl ToTokens for Element {
  57. fn to_tokens(&self, tokens: &mut TokenStream2) {
  58. let name = &self.name.to_string();
  59. tokens.append_all(quote! {
  60. dioxus::builder::ElementBuilder::new(__cx, #name)
  61. });
  62. for attr in self.attrs.iter() {
  63. attr.to_tokens(tokens);
  64. }
  65. let mut children = self.children.iter();
  66. while let Some(child) = children.next() {
  67. let inner_toks = child.to_token_stream();
  68. tokens.append_all(quote! {
  69. .iter_child(#inner_toks)
  70. })
  71. }
  72. tokens.append_all(quote! {
  73. .finish()
  74. });
  75. }
  76. }
  77. /// =======================================
  78. /// Parse a VElement's Attributes
  79. /// =======================================
  80. struct ElementAttr {
  81. name: Ident,
  82. ty: AttrType,
  83. }
  84. enum AttrType {
  85. BumpText(LitStr),
  86. FieldTokens(Expr),
  87. EventTokens(Expr),
  88. Event(ExprClosure),
  89. }
  90. impl Parse for ElementAttr {
  91. fn parse(s: ParseStream) -> Result<Self> {
  92. let mut name = Ident::parse_any(s)?;
  93. let name_str = name.to_string();
  94. s.parse::<Token![:]>()?;
  95. // Check if this is an event handler
  96. // If so, parse into literal tokens
  97. let ty = if name_str.starts_with("on") {
  98. // remove the "on" bit
  99. name = Ident::new(&name_str.trim_start_matches("on"), name.span());
  100. if s.peek(token::Brace) {
  101. let content;
  102. syn::braced!(content in s);
  103. // Try to parse directly as a closure
  104. let fork = content.fork();
  105. if let Ok(event) = fork.parse::<ExprClosure>() {
  106. content.advance_to(&fork);
  107. AttrType::Event(event)
  108. } else {
  109. AttrType::EventTokens(content.parse()?)
  110. }
  111. } else {
  112. AttrType::Event(s.parse()?)
  113. }
  114. } else {
  115. match name_str.as_str() {
  116. "key" => {
  117. // todo: better error here
  118. AttrType::BumpText(s.parse::<LitStr>()?)
  119. }
  120. "style" => {
  121. //
  122. todo!("inline style not yet supported")
  123. }
  124. "classes" => {
  125. //
  126. todo!("custom class lsit not supported")
  127. }
  128. "namespace" => {
  129. //
  130. todo!("custom namespace not supported")
  131. }
  132. "ref" => {
  133. //
  134. todo!("custom ref not supported")
  135. }
  136. _ => {
  137. if s.peek(LitStr) {
  138. let rawtext = s.parse::<LitStr>().unwrap();
  139. AttrType::BumpText(rawtext)
  140. } else {
  141. let toks = s.parse::<Expr>()?;
  142. AttrType::FieldTokens(toks)
  143. }
  144. }
  145. }
  146. // let lit_str = if name_str == "style" && s.peek(token::Brace) {
  147. // // special-case to deal with literal styles.
  148. // let outer;
  149. // syn::braced!(outer in s);
  150. // // double brace for inline style.
  151. // // todo!("Style support not ready yet");
  152. // // if outer.peek(token::Brace) {
  153. // // let inner;
  154. // // syn::braced!(inner in outer);
  155. // // let styles: Styles = inner.parse()?;
  156. // // MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site()))
  157. // // } else {
  158. // // just parse as an expression
  159. // outer.parse()?
  160. // // }
  161. // } else {
  162. // s.parse()?
  163. // };
  164. };
  165. // consume comma if it exists
  166. // we don't actually care if there *are* commas between attrs
  167. if s.peek(Token![,]) {
  168. let _ = s.parse::<Token![,]>();
  169. }
  170. Ok(ElementAttr { name, ty })
  171. }
  172. }
  173. impl ToTokens for ElementAttr {
  174. fn to_tokens(&self, tokens: &mut TokenStream2) {
  175. let name = self.name.to_string();
  176. let nameident = &self.name;
  177. let _attr_stream = TokenStream2::new();
  178. match &self.ty {
  179. AttrType::BumpText(value) => match name.as_str() {
  180. "key" => {
  181. tokens.append_all(quote! {
  182. .key2(format_args_f!(#value))
  183. });
  184. }
  185. _ => {
  186. tokens.append_all(quote! {
  187. .attr(#name, format_args_f!(#value))
  188. });
  189. }
  190. },
  191. AttrType::FieldTokens(exp) => {
  192. tokens.append_all(quote! {
  193. .attr(#name, #exp)
  194. });
  195. }
  196. AttrType::Event(event) => {
  197. tokens.append_all(quote! {
  198. .add_listener(dioxus::events::on::#nameident(__cx, #event))
  199. });
  200. }
  201. AttrType::EventTokens(event) => {
  202. //
  203. tokens.append_all(quote! {
  204. .add_listener(dioxus::events::on::#nameident(__cx, #event))
  205. });
  206. }
  207. }
  208. }
  209. }