1
0
Эх сурвалжийг харах

wip: make element printer more comprehensive

Jonathan Kelley 3 жил өмнө
parent
commit
79b475798d

+ 41 - 0
packages/autofmt/src/block.rs

@@ -84,10 +84,51 @@ fn format_block_basic() {
                 h1 { "thing" }
                 h1 { "thing" }
                 h1 { "thing" }
+                h1 { "thing" "is whack" "but you're wacker" }
+                h1 { "thing" div {"special cases?"     } }
+                div {
+                    a: 123,
+                    b: 456,
+                    c: 789,
+                    d: "hello",
+                }
+                div {
+                    a: 123,
+                    b: 456,
+                    c: 789,
+                    d: "hello",
+                    p {}
+                    c {}
+                }
+
+                h3 { class: "asdasd", "asdasd" }
+                h3 { class: "mx-large bg-gray-900 tall-md-400", div {
+                    "classy"
+                }}
+
+
+                // Some comments explaining my genius
+                div {
+                    "comment compression"
+                }
+
+                // Some comments explaining my genius
+                div {
+                    // Some comments explaining my genius
+                    a: 123,
+
+                    // comment compression
+                    b: 456,
+
+                    // comments on attributes
+                    c: 789,
+                }
             }
         }
     "#;
 
+    // div { class: "asdasd", p { "hello!" } }
+
     let edits = get_format_blocks(block);
 
     println!("{}", edits[0].formatted);

+ 176 - 104
packages/autofmt/src/element.rs

@@ -2,6 +2,14 @@ use crate::{util::*, write_ident};
 use dioxus_rsx::*;
 use std::{fmt, fmt::Write};
 
+enum ShortOptimization {
+    // Special because we want to print the closing bracket immediately
+    Empty,
+    Oneliner,
+    PropsOnTop,
+    NoOpt,
+}
+
 pub fn write_element(
     Element {
         name,
@@ -16,145 +24,209 @@ pub fn write_element(
     indent: usize,
 ) -> fmt::Result {
     /*
-    Write the tag
-    Write the key
-    Write the attributes
-    Write the children
+        1. Write the tag
+        2. Write the key
+        3. Write the attributes
+        4. Write the children
     */
 
     write!(buf, "{name} {{")?;
 
-    let total_attr_len = extract_attr_len(attributes);
-    let is_long_attr_list = total_attr_len > 80;
+    // decide if we have any special optimizations
+    // Default with none, opt the cases in one-by-one
+    let mut opt_level = ShortOptimization::NoOpt;
 
-    if let Some(key) = key.as_ref().map(|f| f.value()) {
-        if is_long_attr_list {
-            writeln!(buf)?;
-            write_tabs(buf, indent + 1)?;
-        } else {
-            write!(buf, " ")?;
-        }
-        write!(buf, "key: \"{key}\"")?;
+    // check if we have a lot of attributes
+    let is_short_attr_list = is_short_attrs(attributes);
+    let is_small_children = is_short_children(children);
 
-        if !attributes.is_empty() {
-            write!(buf, ",")?;
-        }
+    // if we have few attributes and a lot of children, place the attrs on top
+    if is_short_attr_list && !is_small_children {
+        opt_level = ShortOptimization::PropsOnTop;
     }
 
-    let mut attr_iter = attributes.iter().peekable();
+    // if we have few children and few attributes, make it a one-liner
+    if is_short_attr_list && is_small_children {
+        opt_level = ShortOptimization::Oneliner;
+    }
 
-    while let Some(attr) = attr_iter.next() {
-        if is_long_attr_list {
-            writeln!(buf)?;
-            write_tabs(buf, indent + 1)?;
-        } else {
+    // If there's nothing at all, empty optimization
+    if attributes.is_empty() && children.is_empty() && key.is_none() {
+        opt_level = ShortOptimization::Empty;
+    }
+
+    match opt_level {
+        ShortOptimization::Empty => write!(buf, "}}")?,
+        ShortOptimization::Oneliner => {
             write!(buf, " ")?;
-        }
+            write_attributes(buf, attributes, true, indent)?;
 
-        match &attr.attr {
-            ElementAttr::AttrText { name, value } => {
-                write!(buf, "{name}: \"{value}\"", value = value.value())?;
+            if !children.is_empty() && !attributes.is_empty() {
+                write!(buf, ", ")?;
             }
-            ElementAttr::AttrExpression { name, value } => {
-                let out = prettyplease::unparse_expr(value);
-                write!(buf, "{}: {}", name, out)?;
+
+            // write the children
+            for child in children {
+                write_ident(buf, lines, child, indent + 1)?;
             }
 
-            ElementAttr::CustomAttrText { name, value } => {
-                write!(
-                    buf,
-                    "\"{name}\": \"{value}\"",
-                    name = name.value(),
-                    value = value.value()
-                )?;
+            write!(buf, " }}")?;
+        }
+
+        ShortOptimization::PropsOnTop => {
+            write!(buf, " ")?;
+            write_attributes(buf, attributes, true, indent)?;
+
+            if !children.is_empty() && !attributes.is_empty() {
+                write!(buf, ",")?;
             }
 
-            ElementAttr::CustomAttrExpression { name, value } => {
-                let out = prettyplease::unparse_expr(value);
-                write!(buf, "\"{}\": {}", name.value(), out)?;
+            // write the children
+            for child in children {
+                writeln!(buf)?;
+                write_tabs(buf, indent + 1)?;
+                write_ident(buf, lines, child, indent + 1)?;
             }
 
-            ElementAttr::EventTokens { name, tokens } => {
-                let out = prettyplease::unparse_expr(tokens);
-
-                let mut lines = out.split('\n').peekable();
-                let first = lines.next().unwrap();
-
-                // a one-liner for whatever reason
-                // Does not need a new line
-                if lines.peek().is_none() {
-                    write!(buf, "{}: {}", name, first)?;
-                } else {
-                    writeln!(buf, "{}: {}", name, first)?;
-
-                    while let Some(line) = lines.next() {
-                        write_tabs(buf, indent + 1)?;
-                        write!(buf, "{}", line)?;
-                        if lines.peek().is_none() {
-                            write!(buf, "")?;
-                        } else {
-                            writeln!(buf)?;
-                        }
-                    }
-                }
+            writeln!(buf)?;
+            write_tabs(buf, indent)?;
+            write!(buf, "}}")?;
+        }
+
+        ShortOptimization::NoOpt => {
+            // write the key
+
+            // write the attributes
+            write_attributes(buf, attributes, false, indent)?;
+
+            // write the children
+            for child in children {
+                writeln!(buf)?;
+                write_tabs(buf, indent + 1)?;
+                write_ident(buf, lines, child, indent + 1)?;
             }
+
+            writeln!(buf)?;
+            write_tabs(buf, indent)?;
+            write!(buf, "}}")?;
         }
+    }
 
-        if attr_iter.peek().is_some() || !children.is_empty() {
-            write!(buf, ",")?;
+    Ok(())
+}
+
+fn is_short_attrs(attrs: &[ElementAttrNamed]) -> bool {
+    let total_attr_len = extract_attr_len(attrs);
+    total_attr_len < 80
+}
+
+// check if the children are short enough to be on the same line
+// We don't have the notion of current line depth - each line tries to be < 80 total
+fn is_short_children(children: &[BodyNode]) -> bool {
+    if children.is_empty() {
+        return true;
+    }
+
+    if children.len() == 1 {
+        if let BodyNode::Text(ref text) = &children[0] {
+            return text.value().len() < 80;
         }
     }
 
-    // If the attr list is short, then we want to optimize for some cases
-    let is_child_optimized = match children.as_slice() {
-        // No children, just close the tag
-        [] => true,
+    false
+}
+
+fn write_key() {
+    // if let Some(key) = key.as_ref().map(|f| f.value()) {
+    //     if is_long_attr_list {
+    //         writeln!(buf)?;
+    //         write_tabs(buf, indent + 1)?;
+    //     } else {
+    //         write!(buf, " ")?;
+    //     }
+    //     write!(buf, "key: \"{key}\"")?;
+
+    //     if !attributes.is_empty() {
+    //         write!(buf, ",")?;
+    //     }
+    // }
+}
 
-        // Only a text node, just write it out
-        [BodyNode::Text(_)] => true,
+fn write_attributes(
+    buf: &mut String,
+    attributes: &[ElementAttrNamed],
+    sameline: bool,
+    indent: usize,
+) -> fmt::Result {
+    let mut attr_iter = attributes.iter().peekable();
 
-        // If these have zero children and attributes, then we can just write out the tag
-        [BodyNode::Component(ref comp)] => comp.body.is_empty() && comp.children.is_empty(),
-        [BodyNode::Element(ref el)] => el.attributes.is_empty() && el.children.is_empty(),
+    while let Some(attr) = attr_iter.next() {
+        write_attribute(buf, attr, indent)?;
 
-        // Nothing else is optimized
-        _ => false,
-    };
+        if attr_iter.peek().is_some() {
+            write!(buf, ",")?;
 
-    if !is_long_attr_list && is_child_optimized {
-        write_ident(buf, lines, &children[0], indent)?;
-    } else {
-        for child in children {
-            writeln!(buf)?;
-            write_ident(buf, lines, child, indent + 1)?;
+            if sameline {
+                write!(buf, " ")?;
+            } else {
+                writeln!(buf)?;
+                write_tabs(buf, indent + 1)?;
+            }
         }
     }
 
-    // let text_val = text.value();
-    // if total_attr_len + text_val.len() > 80 {
-    //     writeln!(buf)?;
-    //     write_tabs(buf, indent + 1)?;
-    //     writeln!(buf, "\"{}\"", text.value())?;
-    //     write_tabs(buf, indent)?;
-    // } else {
-    //     write!(buf, " \"{}\" ", text.value())?;
-    // }
+    Ok(())
+}
 
-    // if is_long_attr_list {
-    //     writeln!(buf)?;
-    // }
+fn write_attribute(buf: &mut String, attr: &ElementAttrNamed, indent: usize) -> fmt::Result {
+    match &attr.attr {
+        ElementAttr::AttrText { name, value } => {
+            write!(buf, "{name}: \"{value}\"", value = value.value())?;
+        }
+        ElementAttr::AttrExpression { name, value } => {
+            let out = prettyplease::unparse_expr(value);
+            write!(buf, "{}: {}", name, out)?;
+        }
 
-    // for child in children {
-    //     write_ident(buf, lines, child, indent + 1)?;
-    // }
+        ElementAttr::CustomAttrText { name, value } => {
+            write!(
+                buf,
+                "\"{name}\": \"{value}\"",
+                name = name.value(),
+                value = value.value()
+            )?;
+        }
 
-    // if is_long_attr_list {
-    //     write_tabs(buf, indent)?;
-    // } else {
-    //     write!(buf, " ")?;
-    // }
+        ElementAttr::CustomAttrExpression { name, value } => {
+            let out = prettyplease::unparse_expr(value);
+            write!(buf, "\"{}\": {}", name.value(), out)?;
+        }
 
-    writeln!(buf, "}}")?;
+        ElementAttr::EventTokens { name, tokens } => {
+            let out = prettyplease::unparse_expr(tokens);
+
+            let mut lines = out.split('\n').peekable();
+            let first = lines.next().unwrap();
+
+            // a one-liner for whatever reason
+            // Does not need a new line
+            if lines.peek().is_none() {
+                write!(buf, "{}: {}", name, first)?;
+            } else {
+                writeln!(buf, "{}: {}", name, first)?;
+
+                while let Some(line) = lines.next() {
+                    write_tabs(buf, indent + 1)?;
+                    write!(buf, "{}", line)?;
+                    if lines.peek().is_none() {
+                        write!(buf, "")?;
+                    } else {
+                        writeln!(buf)?;
+                    }
+                }
+            }
+        }
+    }
 
     Ok(())
 }

+ 1 - 1
packages/autofmt/src/util.rs

@@ -11,7 +11,7 @@ pub fn extract_attr_len(attributes: &[ElementAttrNamed]) -> usize {
             ElementAttr::CustomAttrExpression { name, value } => 10,
             ElementAttr::EventTokens { name, tokens } => 1000000,
         })
-        .sum::<usize>()
+        .sum()
 }
 
 pub fn write_tabs(f: &mut dyn Write, num: usize) -> std::fmt::Result {