123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262 |
- use super::*;
- use proc_macro2::TokenStream as TokenStream2;
- use quote::{quote, ToTokens, TokenStreamExt};
- use syn::{
- ext::IdentExt,
- parse::{discouraged::Speculative, Parse, ParseBuffer, ParseStream},
- token, Error, Expr, ExprClosure, Ident, LitStr, Result, Token,
- };
- // =======================================
- // Parse the VNode::Element type
- // =======================================
- pub struct Element {
- name: Ident,
- key: Option<AttrType>,
- attributes: Vec<ElementAttr>,
- listeners: Vec<ElementAttr>,
- children: Vec<BodyNode>,
- is_static: bool,
- }
- impl ToTokens for Element {
- fn to_tokens(&self, tokens: &mut TokenStream2) {
- let name = &self.name;
- let attr = &self.attributes;
- let childs = &self.children;
- let listeners = &self.listeners;
- tokens.append_all(quote! {
- __cx.element(
- dioxus_elements::#name,
- __cx.bump().alloc([ #(#listeners),* ]),
- __cx.bump().alloc([ #(#attr),* ]),
- __cx.bump().alloc([ #(#childs),* ]),
- None,
- )
- });
- }
- }
- impl Parse for Element {
- fn parse(stream: ParseStream) -> Result<Self> {
- let name = Ident::parse(stream)?;
- // parse the guts
- let content: ParseBuffer;
- syn::braced!(content in stream);
- let mut attributes: Vec<ElementAttr> = vec![];
- let mut listeners: Vec<ElementAttr> = vec![];
- let mut children: Vec<BodyNode> = vec![];
- let mut key = None;
- 'parsing: loop {
- // [1] Break if empty
- if content.is_empty() {
- break 'parsing;
- }
- if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) {
- parse_element_body(
- &content,
- &mut attributes,
- &mut listeners,
- &mut key,
- name.clone(),
- )?;
- } else {
- children.push(content.parse::<BodyNode>()?);
- }
- // consume comma if it exists
- // we don't actually care if there *are* commas after elements/text
- if content.peek(Token![,]) {
- let _ = content.parse::<Token![,]>();
- }
- }
- Ok(Self {
- key,
- name,
- attributes,
- children,
- listeners,
- is_static: false,
- })
- }
- }
- /// =======================================
- /// Parse a VElement's Attributes
- /// =======================================
- struct ElementAttr {
- element_name: Ident,
- name: Ident,
- value: AttrType,
- namespace: Option<String>,
- }
- enum AttrType {
- BumpText(LitStr),
- FieldTokens(Expr),
- EventTokens(Expr),
- Event(ExprClosure),
- }
- // We parse attributes and dump them into the attribute vec
- // This is because some tags might be namespaced (IE style)
- // These dedicated tags produce multiple name-spaced attributes
- fn parse_element_body(
- stream: ParseStream,
- attrs: &mut Vec<ElementAttr>,
- listeners: &mut Vec<ElementAttr>,
- key: &mut Option<AttrType>,
- element_name: Ident,
- ) -> Result<()> {
- let mut name = Ident::parse_any(stream)?;
- let name_str = name.to_string();
- stream.parse::<Token![:]>()?;
- // Return early if the field is a listener
- if name_str.starts_with("on") {
- // remove the "on" bit
- // name = Ident::new(&name_str.trim_start_matches("on"), name.span());
- let ty = if stream.peek(token::Brace) {
- let content;
- syn::braced!(content in stream);
- // Try to parse directly as a closure
- let fork = content.fork();
- if let Ok(event) = fork.parse::<ExprClosure>() {
- content.advance_to(&fork);
- AttrType::Event(event)
- } else {
- AttrType::EventTokens(content.parse()?)
- }
- } else {
- AttrType::Event(stream.parse()?)
- };
- listeners.push(ElementAttr {
- name,
- value: ty,
- namespace: None,
- element_name: element_name.clone(),
- });
- return Ok(());
- }
- let ty: AttrType = match name_str.as_str() {
- // short circuit early if style is using the special syntax
- "style" if stream.peek(token::Brace) => {
- let inner;
- syn::braced!(inner in stream);
- while !inner.is_empty() {
- let name = Ident::parse_any(&inner)?;
- inner.parse::<Token![:]>()?;
- let ty = if inner.peek(LitStr) {
- let rawtext = inner.parse::<LitStr>().unwrap();
- AttrType::BumpText(rawtext)
- } else {
- let toks = inner.parse::<Expr>()?;
- AttrType::FieldTokens(toks)
- };
- if inner.peek(Token![,]) {
- let _ = inner.parse::<Token![,]>();
- }
- attrs.push(ElementAttr {
- name,
- value: ty,
- namespace: Some("style".to_string()),
- element_name: element_name.clone(),
- });
- }
- return Ok(());
- }
- "key" => {
- *key = Some(AttrType::BumpText(stream.parse::<LitStr>()?));
- return Ok(());
- }
- "classes" => {
- todo!("custom class lsit not supported")
- }
- "namespace" => {
- todo!("custom namespace not supported")
- }
- "ref" => {
- todo!("NodeRefs are currently not supported! This is currently a reserved keyword.")
- }
- // Fall through
- _ => {
- if stream.peek(LitStr) {
- let rawtext = stream.parse::<LitStr>().unwrap();
- AttrType::BumpText(rawtext)
- } else {
- let toks = stream.parse::<Expr>()?;
- AttrType::FieldTokens(toks)
- }
- }
- };
- // consume comma if it exists
- // we don't actually care if there *are* commas between attrs
- if stream.peek(Token![,]) {
- let _ = stream.parse::<Token![,]>();
- }
- attrs.push(ElementAttr {
- name,
- value: ty,
- namespace: None,
- element_name,
- });
- Ok(())
- }
- impl ToTokens for ElementAttr {
- fn to_tokens(&self, tokens: &mut TokenStream2) {
- let el_name = &self.element_name;
- let name_str = self.name.to_string();
- let nameident = &self.name;
- let namespace = match &self.namespace {
- Some(t) => quote! { Some(#t) },
- None => quote! { None },
- };
- match &self.value {
- AttrType::BumpText(value) => tokens.append_all(quote! {
- dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value))
- }),
- AttrType::FieldTokens(exp) => tokens.append_all(quote! {
- dioxus_elements::#el_name.#nameident(__cx, #exp)
- }),
- // todo: move event handlers on to the elements or onto the nodefactory
- AttrType::Event(event) => tokens.append_all(quote! {
- dioxus::events::on::#nameident(__cx, #event)
- }),
- AttrType::EventTokens(event) => tokens.append_all(quote! {
- dioxus::events::on::#nameident(__cx, #event)
- }),
- }
- }
- }
- // __cx.attr(#name, format_args_f!(#value), #namespace, false)
- //
- // AttrType::BumpText(value) => tokens.append_all(quote! {
- // __cx.attr(#name, format_args_f!(#value), #namespace, false)
- // }),
- // __cx.attr(#name_str, #exp, #namespace, false)
- // AttrType::FieldTokens(exp) => tokens.append_all(quote! {
- // dioxus_elements::#el_name.#nameident(__cx, format_args_f!(#value))
- // __cx.attr(#name_str, #exp, #namespace, false)
- // }),
|