|
@@ -8,7 +8,7 @@ use syn::{
|
|
parse::{Parse, ParseBuffer, ParseStream},
|
|
parse::{Parse, ParseBuffer, ParseStream},
|
|
punctuated::Punctuated,
|
|
punctuated::Punctuated,
|
|
spanned::Spanned,
|
|
spanned::Spanned,
|
|
- Ident, LitStr, Result, Token,
|
|
|
|
|
|
+ Expr, Ident, LitStr, Result, Token,
|
|
};
|
|
};
|
|
|
|
|
|
// =======================================
|
|
// =======================================
|
|
@@ -18,11 +18,10 @@ use syn::{
|
|
pub struct Element {
|
|
pub struct Element {
|
|
pub name: ElementName,
|
|
pub name: ElementName,
|
|
pub key: Option<IfmtInput>,
|
|
pub key: Option<IfmtInput>,
|
|
- pub attributes: Vec<ElementAttrNamed>,
|
|
|
|
- pub merged_attributes: Vec<ElementAttrNamed>,
|
|
|
|
|
|
+ pub attributes: Vec<AttributeType>,
|
|
|
|
+ pub merged_attributes: Vec<AttributeType>,
|
|
pub children: Vec<BodyNode>,
|
|
pub children: Vec<BodyNode>,
|
|
pub brace: syn::token::Brace,
|
|
pub brace: syn::token::Brace,
|
|
- pub extra_attributes: Option<Expr>,
|
|
|
|
}
|
|
}
|
|
|
|
|
|
impl Parse for Element {
|
|
impl Parse for Element {
|
|
@@ -33,7 +32,7 @@ impl Parse for Element {
|
|
let content: ParseBuffer;
|
|
let content: ParseBuffer;
|
|
let brace = syn::braced!(content in stream);
|
|
let brace = syn::braced!(content in stream);
|
|
|
|
|
|
- let mut attributes: Vec<ElementAttrNamed> = vec![];
|
|
|
|
|
|
+ let mut attributes: Vec<AttributeType> = vec![];
|
|
let mut children: Vec<BodyNode> = vec![];
|
|
let mut children: Vec<BodyNode> = vec![];
|
|
let mut key = None;
|
|
let mut key = None;
|
|
|
|
|
|
@@ -43,9 +42,20 @@ impl Parse for Element {
|
|
// "def": 456,
|
|
// "def": 456,
|
|
// abc: 123,
|
|
// abc: 123,
|
|
loop {
|
|
loop {
|
|
- if content.peek(Token![...]) {
|
|
|
|
- content.parse::<Token![...]>()?;
|
|
|
|
- extra_attributes = Some(content.parse::<Expr>()?);
|
|
|
|
|
|
+ if content.peek(Token![..]) {
|
|
|
|
+ content.parse::<Token![..]>()?;
|
|
|
|
+ let expr = content.parse::<Expr>()?;
|
|
|
|
+ let span = expr.span();
|
|
|
|
+ attributes.push(attribute::AttributeType::Spread(expr));
|
|
|
|
+
|
|
|
|
+ if content.is_empty() {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if content.parse::<Token![,]>().is_err() {
|
|
|
|
+ missing_trailing_comma!(span);
|
|
|
|
+ }
|
|
|
|
+ continue;
|
|
}
|
|
}
|
|
|
|
|
|
// Parse the raw literal fields
|
|
// Parse the raw literal fields
|
|
@@ -56,13 +66,13 @@ impl Parse for Element {
|
|
content.parse::<Token![:]>()?;
|
|
content.parse::<Token![:]>()?;
|
|
|
|
|
|
let value = content.parse::<ElementAttrValue>()?;
|
|
let value = content.parse::<ElementAttrValue>()?;
|
|
- attributes.push(ElementAttrNamed {
|
|
|
|
|
|
+ attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
|
|
el_name: el_name.clone(),
|
|
el_name: el_name.clone(),
|
|
attr: ElementAttr {
|
|
attr: ElementAttr {
|
|
name: ElementAttrName::Custom(name),
|
|
name: ElementAttrName::Custom(name),
|
|
value,
|
|
value,
|
|
},
|
|
},
|
|
- });
|
|
|
|
|
|
+ }));
|
|
|
|
|
|
if content.is_empty() {
|
|
if content.is_empty() {
|
|
break;
|
|
break;
|
|
@@ -85,13 +95,13 @@ impl Parse for Element {
|
|
let span = content.span();
|
|
let span = content.span();
|
|
|
|
|
|
if name_str.starts_with("on") {
|
|
if name_str.starts_with("on") {
|
|
- attributes.push(ElementAttrNamed {
|
|
|
|
|
|
+ attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
|
|
el_name: el_name.clone(),
|
|
el_name: el_name.clone(),
|
|
attr: ElementAttr {
|
|
attr: ElementAttr {
|
|
name: ElementAttrName::BuiltIn(name),
|
|
name: ElementAttrName::BuiltIn(name),
|
|
value: ElementAttrValue::EventTokens(content.parse()?),
|
|
value: ElementAttrValue::EventTokens(content.parse()?),
|
|
},
|
|
},
|
|
- });
|
|
|
|
|
|
+ }));
|
|
} else {
|
|
} else {
|
|
match name_str.as_str() {
|
|
match name_str.as_str() {
|
|
"key" => {
|
|
"key" => {
|
|
@@ -99,13 +109,13 @@ impl Parse for Element {
|
|
}
|
|
}
|
|
_ => {
|
|
_ => {
|
|
let value = content.parse::<ElementAttrValue>()?;
|
|
let value = content.parse::<ElementAttrValue>()?;
|
|
- attributes.push(ElementAttrNamed {
|
|
|
|
|
|
+ attributes.push(attribute::AttributeType::Named(ElementAttrNamed {
|
|
el_name: el_name.clone(),
|
|
el_name: el_name.clone(),
|
|
attr: ElementAttr {
|
|
attr: ElementAttr {
|
|
name: ElementAttrName::BuiltIn(name),
|
|
name: ElementAttrName::BuiltIn(name),
|
|
value,
|
|
value,
|
|
},
|
|
},
|
|
- });
|
|
|
|
|
|
+ }));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -114,7 +124,6 @@ impl Parse for Element {
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- // todo: add a message saying you need to include commas between fields
|
|
|
|
if content.parse::<Token![,]>().is_err() {
|
|
if content.parse::<Token![,]>().is_err() {
|
|
missing_trailing_comma!(span);
|
|
missing_trailing_comma!(span);
|
|
}
|
|
}
|
|
@@ -126,12 +135,26 @@ impl Parse for Element {
|
|
|
|
|
|
// Deduplicate any attributes that can be combined
|
|
// Deduplicate any attributes that can be combined
|
|
// For example, if there are two `class` attributes, combine them into one
|
|
// For example, if there are two `class` attributes, combine them into one
|
|
- let mut merged_attributes: Vec<ElementAttrNamed> = Vec::new();
|
|
|
|
|
|
+ let mut merged_attributes: Vec<AttributeType> = Vec::new();
|
|
for attr in &attributes {
|
|
for attr in &attributes {
|
|
- if let Some(old_attr_index) = merged_attributes
|
|
|
|
- .iter()
|
|
|
|
- .position(|a| a.attr.name == attr.attr.name)
|
|
|
|
- {
|
|
|
|
|
|
+ if let Some(old_attr_index) = merged_attributes.iter().position(|a| {
|
|
|
|
+ matches!((a, attr), (
|
|
|
|
+ AttributeType::Named(ElementAttrNamed {
|
|
|
|
+ attr: ElementAttr {
|
|
|
|
+ name: ElementAttrName::BuiltIn(old_name),
|
|
|
|
+ ..
|
|
|
|
+ },
|
|
|
|
+ ..
|
|
|
|
+ }),
|
|
|
|
+ AttributeType::Named(ElementAttrNamed {
|
|
|
|
+ attr: ElementAttr {
|
|
|
|
+ name: ElementAttrName::BuiltIn(new_name),
|
|
|
|
+ ..
|
|
|
|
+ },
|
|
|
|
+ ..
|
|
|
|
+ }),
|
|
|
|
+ ) if old_name == new_name)
|
|
|
|
+ }) {
|
|
let old_attr = &mut merged_attributes[old_attr_index];
|
|
let old_attr = &mut merged_attributes[old_attr_index];
|
|
if let Some(combined) = old_attr.try_combine(attr) {
|
|
if let Some(combined) = old_attr.try_combine(attr) {
|
|
*old_attr = combined;
|
|
*old_attr = combined;
|
|
@@ -165,7 +188,6 @@ impl Parse for Element {
|
|
merged_attributes,
|
|
merged_attributes,
|
|
children,
|
|
children,
|
|
brace,
|
|
brace,
|
|
- extra_attributes,
|
|
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -180,15 +202,31 @@ impl ToTokens for Element {
|
|
None => quote! { None },
|
|
None => quote! { None },
|
|
};
|
|
};
|
|
|
|
|
|
- let listeners = self
|
|
|
|
- .merged_attributes
|
|
|
|
- .iter()
|
|
|
|
- .filter(|f| matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
|
|
|
|
|
|
+ let listeners = self.merged_attributes.iter().filter(|f| {
|
|
|
|
+ matches!(
|
|
|
|
+ f,
|
|
|
|
+ AttributeType::Named(ElementAttrNamed {
|
|
|
|
+ attr: ElementAttr {
|
|
|
|
+ value: ElementAttrValue::EventTokens { .. },
|
|
|
|
+ ..
|
|
|
|
+ },
|
|
|
|
+ ..
|
|
|
|
+ })
|
|
|
|
+ )
|
|
|
|
+ });
|
|
|
|
|
|
- let attr = self
|
|
|
|
- .merged_attributes
|
|
|
|
- .iter()
|
|
|
|
- .filter(|f| !matches!(f.attr.value, ElementAttrValue::EventTokens { .. }));
|
|
|
|
|
|
+ let attr = self.merged_attributes.iter().filter(|f| {
|
|
|
|
+ !matches!(
|
|
|
|
+ f,
|
|
|
|
+ AttributeType::Named(ElementAttrNamed {
|
|
|
|
+ attr: ElementAttr {
|
|
|
|
+ value: ElementAttrValue::EventTokens { .. },
|
|
|
|
+ ..
|
|
|
|
+ },
|
|
|
|
+ ..
|
|
|
|
+ })
|
|
|
|
+ )
|
|
|
|
+ });
|
|
|
|
|
|
tokens.append_all(quote! {
|
|
tokens.append_all(quote! {
|
|
__cx.element(
|
|
__cx.element(
|