htm.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. //!
  2. //! TODO:
  3. //! - [ ] Support for VComponents
  4. //! - [ ] Support for inline format in text
  5. //! - [ ] Support for expressions in attribute positions
  6. //! - [ ] Support for iterators
  7. //! - [ ] support for inline html!
  8. //!
  9. //!
  10. //!
  11. //!
  12. //!
  13. //!
  14. //!
  15. use {
  16. proc_macro2::TokenStream as TokenStream2,
  17. quote::{quote, ToTokens, TokenStreamExt},
  18. syn::{
  19. ext::IdentExt,
  20. parse::{Parse, ParseStream},
  21. token, Error, Expr, ExprClosure, Ident, LitStr, Result, Token,
  22. },
  23. };
  24. // ==============================================
  25. // Parse any stream coming from the html! macro
  26. // ==============================================
  27. pub struct HtmlRender {
  28. kind: NodeOrList,
  29. }
  30. impl Parse for HtmlRender {
  31. fn parse(input: ParseStream) -> Result<Self> {
  32. if input.peek(LitStr) {
  33. return input.parse::<LitStr>()?.parse::<HtmlRender>();
  34. }
  35. // let __cx: Ident = s.parse()?;
  36. // s.parse::<Token![,]>()?;
  37. // if elements are in an array, return a bumpalo::collections::Vec rather than a Node.
  38. let kind = if input.peek(token::Bracket) {
  39. let nodes_toks;
  40. syn::bracketed!(nodes_toks in input);
  41. let mut nodes: Vec<MaybeExpr<Node>> = vec![nodes_toks.parse()?];
  42. while nodes_toks.peek(Token![,]) {
  43. nodes_toks.parse::<Token![,]>()?;
  44. nodes.push(nodes_toks.parse()?);
  45. }
  46. NodeOrList::List(NodeList(nodes))
  47. } else {
  48. NodeOrList::Node(input.parse()?)
  49. };
  50. Ok(HtmlRender { kind })
  51. }
  52. }
  53. impl ToTokens for HtmlRender {
  54. fn to_tokens(&self, out_tokens: &mut TokenStream2) {
  55. let new_toks = ToToksCtx::new(&self.kind).to_token_stream();
  56. // create a lazy tree that accepts a bump allocator
  57. let final_tokens = quote! {
  58. dioxus::prelude::LazyNodes::new(move |__cx| {
  59. let bump = __cx.bump();
  60. #new_toks
  61. })
  62. };
  63. final_tokens.to_tokens(out_tokens);
  64. }
  65. }
  66. /// =============================================
  67. /// Parse any child as a node or list of nodes
  68. /// =============================================
  69. /// - [ ] Allow iterators
  70. ///
  71. ///
  72. enum NodeOrList {
  73. Node(Node),
  74. List(NodeList),
  75. }
  76. impl ToTokens for ToToksCtx<&NodeOrList> {
  77. fn to_tokens(&self, tokens: &mut TokenStream2) {
  78. match self.inner {
  79. NodeOrList::Node(node) => self.recurse(node).to_tokens(tokens),
  80. NodeOrList::List(list) => self.recurse(list).to_tokens(tokens),
  81. }
  82. }
  83. }
  84. struct NodeList(Vec<MaybeExpr<Node>>);
  85. impl ToTokens for ToToksCtx<&NodeList> {
  86. fn to_tokens(&self, tokens: &mut TokenStream2) {
  87. let nodes = self.inner.0.iter().map(|node| self.recurse(node));
  88. tokens.append_all(quote! {
  89. dioxus::bumpalo::vec![in bump;
  90. #(#nodes),*
  91. ]
  92. });
  93. }
  94. }
  95. enum Node {
  96. Element(Element),
  97. Text(TextNode),
  98. }
  99. impl ToTokens for ToToksCtx<&Node> {
  100. fn to_tokens(&self, tokens: &mut TokenStream2) {
  101. match &self.inner {
  102. Node::Element(el) => self.recurse(el).to_tokens(tokens),
  103. Node::Text(txt) => self.recurse(txt).to_tokens(tokens),
  104. }
  105. }
  106. }
  107. impl Node {
  108. fn _peek(s: ParseStream) -> bool {
  109. (s.peek(Token![<]) && !s.peek2(Token![/])) || s.peek(token::Brace) || s.peek(LitStr)
  110. }
  111. }
  112. impl Parse for Node {
  113. fn parse(s: ParseStream) -> Result<Self> {
  114. Ok(if s.peek(Token![<]) {
  115. Node::Element(s.parse()?)
  116. } else {
  117. Node::Text(s.parse()?)
  118. })
  119. }
  120. }
  121. /// =======================================
  122. /// Parse the VNode::Element type
  123. /// =======================================
  124. /// - [ ] Allow VComponent
  125. ///
  126. ///
  127. struct Element {
  128. name: Ident,
  129. attrs: Vec<Attr>,
  130. children: MaybeExpr<Vec<Node>>,
  131. }
  132. impl ToTokens for ToToksCtx<&Element> {
  133. fn to_tokens(&self, tokens: &mut TokenStream2) {
  134. // let __cx = self.__cx;
  135. let name = &self.inner.name;
  136. // let name = &self.inner.name.to_string();
  137. tokens.append_all(quote! {
  138. __cx.element(dioxus_elements::#name)
  139. // dioxus::builder::ElementBuilder::new( #name)
  140. });
  141. for attr in self.inner.attrs.iter() {
  142. self.recurse(attr).to_tokens(tokens);
  143. }
  144. // if is_valid_svg_tag(&name.to_string()) {
  145. // tokens.append_all(quote! {
  146. // .namespace(Some("http://www.w3.org/2000/svg"))
  147. // });
  148. // }
  149. match &self.inner.children {
  150. MaybeExpr::Expr(expr) => tokens.append_all(quote! {
  151. .children(#expr)
  152. }),
  153. MaybeExpr::Literal(nodes) => {
  154. let mut children = nodes.iter();
  155. if let Some(child) = children.next() {
  156. let mut inner_toks = TokenStream2::new();
  157. self.recurse(child).to_tokens(&mut inner_toks);
  158. for child in children {
  159. quote!(,).to_tokens(&mut inner_toks);
  160. self.recurse(child).to_tokens(&mut inner_toks);
  161. }
  162. tokens.append_all(quote! {
  163. .children([#inner_toks])
  164. });
  165. }
  166. }
  167. }
  168. tokens.append_all(quote! {
  169. .finish()
  170. });
  171. }
  172. }
  173. impl Parse for Element {
  174. fn parse(s: ParseStream) -> Result<Self> {
  175. s.parse::<Token![<]>()?;
  176. let name = Ident::parse_any(s)?;
  177. let mut attrs = vec![];
  178. let _children: Vec<Node> = vec![];
  179. // keep looking for attributes
  180. while !s.peek(Token![>]) {
  181. // self-closing
  182. if s.peek(Token![/]) {
  183. s.parse::<Token![/]>()?;
  184. s.parse::<Token![>]>()?;
  185. return Ok(Self {
  186. name,
  187. attrs,
  188. children: MaybeExpr::Literal(vec![]),
  189. });
  190. }
  191. attrs.push(s.parse()?);
  192. }
  193. s.parse::<Token![>]>()?;
  194. // Contents of an element can either be a brace (in which case we just copy verbatim), or a
  195. // sequence of nodes.
  196. let children = if s.peek(token::Brace) {
  197. // expr
  198. let content;
  199. syn::braced!(content in s);
  200. MaybeExpr::Expr(content.parse()?)
  201. } else {
  202. // nodes
  203. let mut children = vec![];
  204. while !(s.peek(Token![<]) && s.peek2(Token![/])) {
  205. children.push(s.parse()?);
  206. }
  207. MaybeExpr::Literal(children)
  208. };
  209. // closing element
  210. s.parse::<Token![<]>()?;
  211. s.parse::<Token![/]>()?;
  212. let close = Ident::parse_any(s)?;
  213. if close != name {
  214. return Err(Error::new_spanned(
  215. close,
  216. "closing element does not match opening",
  217. ));
  218. }
  219. s.parse::<Token![>]>()?;
  220. Ok(Self {
  221. name,
  222. attrs,
  223. children,
  224. })
  225. }
  226. }
  227. /// =======================================
  228. /// Parse a VElement's Attributes
  229. /// =======================================
  230. /// - [ ] Allow expressions as attribute
  231. ///
  232. ///
  233. struct Attr {
  234. name: Ident,
  235. ty: AttrType,
  236. }
  237. impl Parse for Attr {
  238. fn parse(s: ParseStream) -> Result<Self> {
  239. let mut name = Ident::parse_any(s)?;
  240. let name_str = name.to_string();
  241. s.parse::<Token![=]>()?;
  242. // Check if this is an event handler
  243. // If so, parse into literal tokens
  244. let ty = if name_str.starts_with("on") {
  245. // remove the "on" bit
  246. name = Ident::new(name_str.trim_start_matches("on"), name.span());
  247. let content;
  248. syn::braced!(content in s);
  249. // AttrType::Value(content.parse()?)
  250. AttrType::Event(content.parse()?)
  251. // AttrType::Event(content.parse()?)
  252. } else {
  253. let lit_str = if name_str == "style" && s.peek(token::Brace) {
  254. // special-case to deal with literal styles.
  255. let outer;
  256. syn::braced!(outer in s);
  257. // double brace for inline style.
  258. // todo!("Style support not ready yet");
  259. // if outer.peek(token::Brace) {
  260. // let inner;
  261. // syn::braced!(inner in outer);
  262. // let styles: Styles = inner.parse()?;
  263. // MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site()))
  264. // } else {
  265. // just parse as an expression
  266. MaybeExpr::Expr(outer.parse()?)
  267. // }
  268. } else {
  269. s.parse()?
  270. };
  271. AttrType::Value(lit_str)
  272. };
  273. Ok(Attr { name, ty })
  274. }
  275. }
  276. impl ToTokens for ToToksCtx<&Attr> {
  277. fn to_tokens(&self, tokens: &mut TokenStream2) {
  278. let name = self.inner.name.to_string();
  279. let _attr_stream = TokenStream2::new();
  280. match &self.inner.ty {
  281. AttrType::Value(value) => {
  282. let value = self.recurse(value);
  283. if name == "xmlns" {
  284. tokens.append_all(quote! {
  285. .namespace(Some(#value))
  286. });
  287. } else {
  288. tokens.append_all(quote! {
  289. .attr(#name, format_args_f!(#value))
  290. });
  291. }
  292. }
  293. AttrType::Event(event) => {
  294. tokens.append_all(quote! {
  295. .on(#name, #event)
  296. });
  297. }
  298. }
  299. }
  300. }
  301. enum AttrType {
  302. Value(MaybeExpr<LitStr>),
  303. Event(ExprClosure),
  304. // todo Bool(MaybeExpr<LitBool>)
  305. }
  306. /// =======================================
  307. /// Parse just plain text
  308. /// =======================================
  309. /// - [ ] Perform formatting automatically
  310. ///
  311. ///
  312. struct TextNode(MaybeExpr<LitStr>);
  313. impl Parse for TextNode {
  314. fn parse(s: ParseStream) -> Result<Self> {
  315. Ok(Self(s.parse()?))
  316. }
  317. }
  318. impl ToTokens for ToToksCtx<&TextNode> {
  319. fn to_tokens(&self, tokens: &mut TokenStream2) {
  320. let mut token_stream = TokenStream2::new();
  321. self.recurse(&self.inner.0).to_tokens(&mut token_stream);
  322. tokens.append_all(quote! {
  323. __cx.text(format_args_f!(#token_stream))
  324. });
  325. }
  326. }
  327. #[allow(clippy::large_enum_variant)]
  328. enum MaybeExpr<T> {
  329. Literal(T),
  330. Expr(Expr),
  331. }
  332. impl<T: Parse> Parse for MaybeExpr<T> {
  333. fn parse(s: ParseStream) -> Result<Self> {
  334. if s.peek(token::Brace) {
  335. let content;
  336. syn::braced!(content in s);
  337. Ok(MaybeExpr::Expr(content.parse()?))
  338. } else {
  339. Ok(MaybeExpr::Literal(s.parse()?))
  340. }
  341. }
  342. }
  343. impl<'a, T> ToTokens for ToToksCtx<&'a MaybeExpr<T>>
  344. where
  345. T: 'a,
  346. ToToksCtx<&'a T>: ToTokens,
  347. {
  348. fn to_tokens(&self, tokens: &mut TokenStream2) {
  349. match &self.inner {
  350. MaybeExpr::Literal(v) => self.recurse(v).to_tokens(tokens),
  351. MaybeExpr::Expr(expr) => expr.to_tokens(tokens),
  352. }
  353. }
  354. }
  355. /// ToTokens context
  356. struct ToToksCtx<T> {
  357. inner: T,
  358. }
  359. impl<'a, T> ToToksCtx<T> {
  360. fn new(inner: T) -> Self {
  361. ToToksCtx { inner }
  362. }
  363. fn recurse<U>(&self, inner: U) -> ToToksCtx<U> {
  364. ToToksCtx { inner }
  365. }
  366. }
  367. impl ToTokens for ToToksCtx<&LitStr> {
  368. fn to_tokens(&self, tokens: &mut TokenStream2) {
  369. self.inner.to_tokens(tokens)
  370. }
  371. }
  372. #[cfg(test)]
  373. mod test {
  374. fn parse(input: &str) -> super::Result<super::HtmlRender> {
  375. syn::parse_str(input)
  376. }
  377. #[test]
  378. fn div() {
  379. parse("bump, <div class=\"test\"/>").unwrap();
  380. }
  381. #[test]
  382. fn nested() {
  383. parse("bump, <div class=\"test\"><div />\"text\"</div>").unwrap();
  384. }
  385. #[test]
  386. fn complex() {
  387. parse(
  388. "bump,
  389. <section style={{
  390. display: flex;
  391. flex-direction: column;
  392. max-width: 95%;
  393. }} class=\"map-panel\">{contact_details}</section>
  394. ",
  395. )
  396. .unwrap();
  397. }
  398. }