diff --git a/packages/rsx/src/attribute.rs b/packages/rsx/src/attribute.rs index ea9d79263..a5daafc27 100644 --- a/packages/rsx/src/attribute.rs +++ b/packages/rsx/src/attribute.rs @@ -13,14 +13,14 @@ pub struct ElementAttrNamed { } impl ElementAttrNamed { - pub(crate) fn try_combine(&self, other: Self) -> Option { + pub(crate) fn try_combine(&self, other: &Self) -> Option { if self.el_name == other.el_name && self.attr.name == other.attr.name { - if let Some(separator) = todo!() { + if let Some(separator) = self.attr.name.multi_attribute_separator() { return Some(ElementAttrNamed { el_name: self.el_name.clone(), attr: ElementAttr { name: self.attr.name.clone(), - value: self.attr.value.combine(separator, other.attr.value), + value: self.attr.value.combine(separator, &other.attr.value), }, }); } @@ -109,8 +109,33 @@ pub enum ElementAttrValue { } impl ElementAttrValue { - fn combine(&self, separator: &str, other: Self) -> Self { - todo!() + fn combine(&self, separator: &str, other: &Self) -> Self { + match (self, other) { + (Self::AttrLiteral(lit1), Self::AttrLiteral(lit2)) => { + let fmt = lit1.clone().join(lit2.clone(), separator); + Self::AttrLiteral(fmt) + } + (Self::AttrLiteral(expr1), Self::AttrExpr(expr2)) => { + let mut ifmt = expr1.clone(); + ifmt.push_str(separator); + ifmt.push_expr(expr2.clone()); + Self::AttrLiteral(ifmt) + } + (Self::AttrExpr(expr1), Self::AttrLiteral(expr2)) => { + let mut ifmt = expr2.clone(); + ifmt.push_str(separator); + ifmt.push_expr(expr1.clone()); + Self::AttrLiteral(ifmt) + } + (Self::AttrExpr(expr1), Self::AttrExpr(expr2)) => { + let mut ifmt = IfmtInput::default(); + ifmt.push_expr(expr1.clone()); + ifmt.push_str(separator); + ifmt.push_expr(expr2.clone()); + Self::AttrLiteral(ifmt) + } + _ => todo!(), + } } } @@ -121,6 +146,17 @@ pub enum ElementAttrName { } impl ElementAttrName { + fn multi_attribute_separator(&self) -> Option<&'static str> { + match self { + ElementAttrName::BuiltIn(i) => match i.to_string().as_str() { + "class" => Some(" "), + "style" => Some(";"), + _ => None, + }, + ElementAttrName::Custom(_) => None, + } + } + pub fn start(&self) -> Span { match self { ElementAttrName::BuiltIn(i) => i.span(), diff --git a/packages/rsx/src/element.rs b/packages/rsx/src/element.rs index 19557edd2..a02669fc7 100644 --- a/packages/rsx/src/element.rs +++ b/packages/rsx/src/element.rs @@ -1,4 +1,7 @@ -use std::fmt::{Display, Formatter}; +use std::{ + collections::HashMap, + fmt::{Display, Formatter}, +}; use super::*; @@ -134,6 +137,17 @@ impl Parse for Element { // Deduplicate any attributes that can be combined // For example, if there are two `class` attributes, combine them into one + let mut combined_attrs: HashMap = HashMap::new(); + for attr in attributes { + if let Some(old_attr) = combined_attrs.get_mut(&attr.attr.name) { + if let Some(combined) = old_attr.try_combine(&attr) { + *old_attr = combined; + } + } else { + combined_attrs.insert(attr.attr.name.clone(), attr); + } + } + let attributes: Vec<_> = combined_attrs.into_iter().map(|(_, v)| v).collect(); while !content.is_empty() { if (content.peek(LitStr) && content.peek2(Token![:])) && !content.peek3(Token![:]) { diff --git a/packages/rsx/src/ifmt.rs b/packages/rsx/src/ifmt.rs index 6c72f5025..981b111ce 100644 --- a/packages/rsx/src/ifmt.rs +++ b/packages/rsx/src/ifmt.rs @@ -13,7 +13,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result { } #[allow(dead_code)] // dumb compiler does not see the struct being used... -#[derive(Debug, PartialEq, Eq, Clone, Hash)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)] pub struct IfmtInput { pub source: Option, pub segments: Vec, @@ -27,6 +27,25 @@ impl IfmtInput { } } + pub fn join(mut self, other: Self, separator: &str) -> Self { + if !self.segments.is_empty() { + self.segments.push(Segment::Literal(separator.to_string())); + } + self.segments.extend(other.segments); + self + } + + pub fn push_expr(&mut self, expr: Expr) { + self.segments.push(Segment::Formatted(FormattedSegment { + format_args: String::new(), + segment: FormattedSegmentType::Expr(Box::new(expr)), + })); + } + + pub fn push_str(&mut self, s: &str) { + self.segments.push(Segment::Literal(s.to_string())); + } + pub fn is_static(&self) -> bool { matches!(self.segments.as_slice(), &[Segment::Literal(_)] | &[]) }