Ver Fonte

combine style and class attributes when there are multiple

Evan Almloff há 1 ano atrás
pai
commit
9f1c735cf1
3 ficheiros alterados com 76 adições e 7 exclusões
  1. 41 5
      packages/rsx/src/attribute.rs
  2. 15 1
      packages/rsx/src/element.rs
  3. 20 1
      packages/rsx/src/ifmt.rs

+ 41 - 5
packages/rsx/src/attribute.rs

@@ -13,14 +13,14 @@ pub struct ElementAttrNamed {
 }
 
 impl ElementAttrNamed {
-    pub(crate) fn try_combine(&self, other: Self) -> Option<Self> {
+    pub(crate) fn try_combine(&self, other: &Self) -> Option<Self> {
         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(),

+ 15 - 1
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<ElementAttrName, ElementAttrNamed> = 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![:]) {

+ 20 - 1
packages/rsx/src/ifmt.rs

@@ -13,7 +13,7 @@ pub fn format_args_f_impl(input: IfmtInput) -> Result<TokenStream> {
 }
 
 #[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<LitStr>,
     pub segments: Vec<Segment>,
@@ -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(_)] | &[])
     }