element.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. use std::fmt::{Display, Formatter};
  2. use super::*;
  3. use proc_macro2::{Span, TokenStream as TokenStream2};
  4. use quote::{quote, ToTokens, TokenStreamExt};
  5. use syn::{
  6. parse::{Parse, ParseBuffer, ParseStream},
  7. punctuated::Punctuated,
  8. spanned::Spanned,
  9. Ident, LitStr, Result, Token,
  10. };
  11. // =======================================
  12. // Parse the VNode::Element type
  13. // =======================================
  14. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  15. pub struct Element {
  16. pub name: ElementName,
  17. pub key: Option<IfmtInput>,
  18. pub attributes: Vec<ElementAttrNamed>,
  19. pub merged_attributes: Vec<ElementAttrNamed>,
  20. pub children: Vec<BodyNode>,
  21. pub brace: syn::token::Brace,
  22. }
  23. impl Parse for Element {
  24. fn parse(stream: ParseStream) -> Result<Self> {
  25. let el_name = ElementName::parse(stream)?;
  26. // parse the guts
  27. let content: ParseBuffer;
  28. let brace = syn::braced!(content in stream);
  29. let mut attributes: Vec<ElementAttrNamed> = vec![];
  30. let mut children: Vec<BodyNode> = vec![];
  31. let mut key = None;
  32. // parse fields with commas
  33. // break when we don't get this pattern anymore
  34. // start parsing bodynodes
  35. // "def": 456,
  36. // abc: 123,
  37. loop {
  38. // Parse the raw literal fields
  39. if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  40. let name = content.parse::<LitStr>()?;
  41. let ident = name.clone();
  42. content.parse::<Token![:]>()?;
  43. let value = content.parse::<ElementAttrValue>()?;
  44. attributes.push(ElementAttrNamed {
  45. el_name: el_name.clone(),
  46. attr: ElementAttr {
  47. name: ElementAttrName::Custom(name),
  48. value,
  49. },
  50. });
  51. if content.is_empty() {
  52. break;
  53. }
  54. if content.parse::<Token![,]>().is_err() {
  55. missing_trailing_comma!(ident.span());
  56. }
  57. continue;
  58. }
  59. if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
  60. let name = content.parse::<Ident>()?;
  61. let name_str = name.to_string();
  62. content.parse::<Token![:]>()?;
  63. // The span of the content to be parsed,
  64. // for example the `hi` part of `class: "hi"`.
  65. let span = content.span();
  66. if name_str.starts_with("on") {
  67. attributes.push(ElementAttrNamed {
  68. el_name: el_name.clone(),
  69. attr: ElementAttr {
  70. name: ElementAttrName::BuiltIn(name),
  71. value: ElementAttrValue::EventTokens(content.parse()?),
  72. },
  73. });
  74. } else {
  75. match name_str.as_str() {
  76. "key" => {
  77. key = Some(content.parse()?);
  78. }
  79. _ => {
  80. let value = content.parse::<ElementAttrValue>()?;
  81. attributes.push(ElementAttrNamed {
  82. el_name: el_name.clone(),
  83. attr: ElementAttr {
  84. name: ElementAttrName::BuiltIn(name),
  85. value,
  86. },
  87. });
  88. }
  89. }
  90. }
  91. if content.is_empty() {
  92. break;
  93. }
  94. // todo: add a message saying you need to include commas between fields
  95. if content.parse::<Token![,]>().is_err() {
  96. missing_trailing_comma!(span);
  97. }
  98. continue;
  99. }
  100. break;
  101. }
  102. // Deduplicate any attributes that can be combined
  103. // For example, if there are two `class` attributes, combine them into one
  104. let mut merged_attributes: Vec<ElementAttrNamed> = Vec::new();
  105. for attr in &attributes {
  106. if let Some(old_attr_index) = merged_attributes
  107. .iter()
  108. .position(|a| a.attr.name == attr.attr.name)
  109. {
  110. let old_attr = &mut merged_attributes[old_attr_index];
  111. if let Some(combined) = old_attr.try_combine(attr) {
  112. *old_attr = combined;
  113. }
  114. } else {
  115. merged_attributes.push(attr.clone());
  116. }
  117. }
  118. while !content.is_empty() {
  119. if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
  120. attr_after_element!(content.span());
  121. }
  122. if (content.peek(Ident) && content.peek2(Token![:])) && !content.peek3(Token![:]) {
  123. attr_after_element!(content.span());
  124. }
  125. children.push(content.parse::<BodyNode>()?);
  126. // consume comma if it exists
  127. // we don't actually care if there *are* commas after elements/text
  128. if content.peek(Token![,]) {
  129. let _ = content.parse::<Token![,]>();
  130. }
  131. }
  132. Ok(Self {
  133. key,
  134. name: el_name,
  135. attributes,
  136. merged_attributes,
  137. children,
  138. brace,
  139. })
  140. }
  141. }
  142. impl ToTokens for Element {
  143. fn to_tokens(&self, tokens: &mut TokenStream2) {
  144. let name = &self.name;
  145. let children = &self.children;
  146. let key = match &self.key {
  147. Some(ty) => quote! { Some(#ty) },
  148. None => quote! { None },
  149. };
  150. let listeners = self
  151. .merged_attributes
  152. .iter()
  153. .filter(|f| matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
  154. let attr = self
  155. .merged_attributes
  156. .iter()
  157. .filter(|f| !matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
  158. tokens.append_all(quote! {
  159. __cx.element(
  160. #name,
  161. __cx.bump().alloc([ #(#listeners),* ]),
  162. __cx.bump().alloc([ #(#attr),* ]),
  163. __cx.bump().alloc([ #(#children),* ]),
  164. #key,
  165. )
  166. });
  167. }
  168. }
  169. #[derive(PartialEq, Eq, Clone, Debug, Hash)]
  170. pub enum ElementName {
  171. Ident(Ident),
  172. Custom(LitStr),
  173. }
  174. impl ElementName {
  175. pub(crate) fn tag_name(&self) -> TokenStream2 {
  176. match self {
  177. ElementName::Ident(i) => quote! { dioxus_elements::#i::TAG_NAME },
  178. ElementName::Custom(s) => quote! { #s },
  179. }
  180. }
  181. }
  182. impl ElementName {
  183. pub fn span(&self) -> Span {
  184. match self {
  185. ElementName::Ident(i) => i.span(),
  186. ElementName::Custom(s) => s.span(),
  187. }
  188. }
  189. }
  190. impl PartialEq<&str> for ElementName {
  191. fn eq(&self, other: &&str) -> bool {
  192. match self {
  193. ElementName::Ident(i) => i == *other,
  194. ElementName::Custom(s) => s.value() == *other,
  195. }
  196. }
  197. }
  198. impl Display for ElementName {
  199. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  200. match self {
  201. ElementName::Ident(i) => write!(f, "{}", i),
  202. ElementName::Custom(s) => write!(f, "{}", s.value()),
  203. }
  204. }
  205. }
  206. impl Parse for ElementName {
  207. fn parse(stream: ParseStream) -> Result<Self> {
  208. let raw = Punctuated::<Ident, Token![-]>::parse_separated_nonempty(stream)?;
  209. if raw.len() == 1 {
  210. Ok(ElementName::Ident(raw.into_iter().next().unwrap()))
  211. } else {
  212. let span = raw.span();
  213. let tag = raw
  214. .into_iter()
  215. .map(|ident| ident.to_string())
  216. .collect::<Vec<_>>()
  217. .join("-");
  218. let tag = LitStr::new(&tag, span);
  219. Ok(ElementName::Custom(tag))
  220. }
  221. }
  222. }
  223. impl ToTokens for ElementName {
  224. fn to_tokens(&self, tokens: &mut TokenStream2) {
  225. match self {
  226. ElementName::Ident(i) => tokens.append_all(quote! { dioxus_elements::#i }),
  227. ElementName::Custom(s) => tokens.append_all(quote! { #s }),
  228. }
  229. }
  230. }