diff --git a/packages/autofmt/src/element.rs b/packages/autofmt/src/element.rs index 1354666f3..d386f8b72 100644 --- a/packages/autofmt/src/element.rs +++ b/packages/autofmt/src/element.rs @@ -237,6 +237,9 @@ impl Writer<'_> { ElementAttrValue::AttrLiteral(value) => { write!(self.out, "{value}", value = ifmt_to_string(value))?; } + ElementAttrValue::Shorthand(value) => { + write!(self.out, "{value}",)?; + } ElementAttrValue::AttrExpr(value) => { let out = prettyplease::unparse_expr(value); let mut lines = out.split('\n').peekable(); diff --git a/packages/autofmt/src/writer.rs b/packages/autofmt/src/writer.rs index 78736acfe..be375aa9d 100644 --- a/packages/autofmt/src/writer.rs +++ b/packages/autofmt/src/writer.rs @@ -142,6 +142,7 @@ impl<'a> Writer<'a> { } ElementAttrValue::AttrLiteral(lit) => ifmt_to_string(lit).len(), ElementAttrValue::AttrExpr(expr) => expr.span().line_length(), + ElementAttrValue::Shorthand(expr) => expr.span().line_length(), ElementAttrValue::EventTokens(tokens) => { let location = Location::new(tokens.span().start()); diff --git a/packages/core/tests/miri_stress.rs b/packages/core/tests/miri_stress.rs index 5ddc7d817..f571b30ce 100644 --- a/packages/core/tests/miri_stress.rs +++ b/packages/core/tests/miri_stress.rs @@ -15,7 +15,7 @@ fn test_memory_leak() { cx.spawn(async {}); if val == 2 || val == 4 { - return None; + return render!({ () }); } let name = cx.use_hook(|| String::from("numbers: ")); diff --git a/packages/rsx/src/attribute.rs b/packages/rsx/src/attribute.rs index 4c0db5a33..c5d3383b0 100644 --- a/packages/rsx/src/attribute.rs +++ b/packages/rsx/src/attribute.rs @@ -20,6 +20,13 @@ impl AttributeType { } } + pub fn matches_attr_name(&self, other: &Self) -> bool { + match (self, other) { + (Self::Named(a), Self::Named(b)) => a.attr.name == b.attr.name, + _ => false, + } + } + pub(crate) fn try_combine(&self, other: &Self) -> Option { match (self, other) { (Self::Named(a), Self::Named(b)) => a.try_combine(b).map(Self::Named), @@ -105,6 +112,7 @@ impl ToTokens for ElementAttrNamed { match &attr.value { ElementAttrValue::AttrLiteral(_) | ElementAttrValue::AttrExpr(_) + | ElementAttrValue::Shorthand(_) | ElementAttrValue::AttrOptionalExpr { .. } => { let name = &self.attr.name; let ns = ns(name); @@ -144,6 +152,8 @@ pub struct ElementAttr { #[derive(PartialEq, Eq, Clone, Debug, Hash)] pub enum ElementAttrValue { + /// attribute, + Shorthand(Ident), /// attribute: "value" AttrLiteral(IfmtInput), /// attribute: if bool { "value" } @@ -159,7 +169,7 @@ pub enum ElementAttrValue { impl Parse for ElementAttrValue { fn parse(input: ParseStream) -> syn::Result { - Ok(if input.peek(Token![if]) { + let element_attr_value = if input.peek(Token![if]) { let if_expr = input.parse::()?; if is_if_chain_terminated(&if_expr) { ElementAttrValue::AttrExpr(Expr::If(if_expr)) @@ -180,13 +190,16 @@ impl Parse for ElementAttrValue { } else { let value = input.parse::()?; ElementAttrValue::AttrExpr(value) - }) + }; + + Ok(element_attr_value) } } impl ToTokens for ElementAttrValue { fn to_tokens(&self, tokens: &mut TokenStream2) { match self { + ElementAttrValue::Shorthand(i) => tokens.append_all(quote! { #i }), ElementAttrValue::AttrLiteral(lit) => tokens.append_all(quote! { #lit }), ElementAttrValue::AttrOptionalExpr { condition, value } => { tokens.append_all(quote! { if #condition { Some(#value) } else { None } }) diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index e62fc6d6e..45a11125c 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -8,6 +8,7 @@ use syn::{ parse::{Parse, ParseBuffer, ParseStream}, punctuated::Punctuated, spanned::Spanned, + token::Brace, Expr, Ident, LitStr, Result, Token, }; @@ -59,6 +60,7 @@ impl Parse for Element { } // Parse the raw literal fields + // "def": 456, if content.peek(LitStr) && content.peek2(Token![:]) && !content.peek3(Token![:]) { let name = content.parse::()?; let ident = name.clone(); @@ -84,6 +86,8 @@ impl Parse for Element { continue; } + // Parse + // abc: 123, if content.peek(Ident) && content.peek2(Token![:]) && !content.peek3(Token![:]) { let name = content.parse::()?; @@ -102,22 +106,17 @@ impl Parse for Element { value: ElementAttrValue::EventTokens(content.parse()?), }, })); + } else if name_str == "key" { + key = Some(content.parse()?); } else { - match name_str.as_str() { - "key" => { - key = Some(content.parse()?); - } - _ => { - let value = content.parse::()?; - attributes.push(attribute::AttributeType::Named(ElementAttrNamed { - el_name: el_name.clone(), - attr: ElementAttr { - name: ElementAttrName::BuiltIn(name), - value, - }, - })); - } - } + let value = content.parse::()?; + attributes.push(attribute::AttributeType::Named(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr { + name: ElementAttrName::BuiltIn(name), + value, + }, + })); } if content.is_empty() { @@ -130,6 +129,33 @@ impl Parse for Element { continue; } + // Parse shorthand fields + if content.peek(Ident) + && !content.peek2(Brace) + && !content.peek2(Token![:]) + && !content.peek2(Token![-]) + { + let name = content.parse::()?; + let name_ = name.clone(); + let value = ElementAttrValue::Shorthand(name.clone()); + attributes.push(attribute::AttributeType::Named(ElementAttrNamed { + el_name: el_name.clone(), + attr: ElementAttr { + name: ElementAttrName::BuiltIn(name), + value, + }, + })); + + if content.is_empty() { + break; + } + + if content.parse::().is_err() { + missing_trailing_comma!(name_.span()); + } + continue; + } + break; } @@ -137,31 +163,21 @@ impl Parse for Element { // For example, if there are two `class` attributes, combine them into one let mut merged_attributes: Vec = Vec::new(); for attr in &attributes { - 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 attr_index = merged_attributes + .iter() + .position(|a| a.matches_attr_name(attr)); + + if let Some(old_attr_index) = attr_index { let old_attr = &mut merged_attributes[old_attr_index]; + if let Some(combined) = old_attr.try_combine(attr) { *old_attr = combined; } - } else { - merged_attributes.push(attr.clone()); + + continue; } + + merged_attributes.push(attr.clone()); } while !content.is_empty() { diff --git a/packages/rsx/src/lib.rs b/packages/rsx/src/lib.rs index 70d03fc52..a23c6b0ff 100644 --- a/packages/rsx/src/lib.rs +++ b/packages/rsx/src/lib.rs @@ -668,9 +668,9 @@ fn diff_template() { p { "hello world" } - (0..10).map(|i| rsx!{"{i}"}), - (0..10).map(|i| rsx!{"{i}"}), - (0..11).map(|i| rsx!{"{i}"}), + {(0..10).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx!{"{i}"})}, + {(0..11).map(|i| rsx!{"{i}"})}, Comp{} } }; @@ -714,9 +714,9 @@ fn diff_template() { "height2": "100px", width: 100, Comp{} - (0..11).map(|i| rsx!{"{i}"}), - (0..10).map(|i| rsx!{"{i}"}), - (0..10).map(|i| rsx!{"{i}"}), + {(0..11).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx!{"{i}"})}, + {(0..10).map(|i| rsx!{"{i}"})}, p { "hello world" }