فهرست منبع

chore: fixes to autoformat to prevent double rendering of expr line

Jonathan Kelley 2 سال پیش
والد
کامیت
a37458b9fa

+ 3 - 0
packages/autofmt/README.md

@@ -15,3 +15,6 @@ Sorted roughly in order of what's possible
 - [ ] Format regular exprs
 - [ ] Fix prettyplease around chaining
 - [ ] Don't eat comments in prettyplease
+
+
+# Technique

+ 18 - 8
packages/autofmt/src/buffer.rs

@@ -124,16 +124,26 @@ impl Buffer {
     }
 
     pub fn write_body_no_indent(&mut self, children: &[BodyNode]) -> Result {
-        for child in children {
-            // Exprs handle their own indenting/line breaks
-            if !matches!(child, BodyNode::RawExpr(_)) {
-                if self.current_span_is_primary(child.span()) {
-                    self.write_comments(child.span())?;
+        let last_child = children.len();
+
+        for (idx, child) in children.iter().enumerate() {
+            match child {
+                // check if the expr is a short
+                BodyNode::RawExpr(_) => {
+                    self.tabbed_line()?;
+                    self.write_ident(child)?;
+                    if idx != last_child - 1 {
+                        write!(self.buf, ",")?;
+                    }
+                }
+                _ => {
+                    if self.current_span_is_primary(child.span()) {
+                        self.write_comments(child.span())?;
+                    }
+                    self.tabbed_line()?;
+                    self.write_ident(child)?;
                 }
-                self.tabbed_line()?;
             }
-
-            self.write_ident(child)?;
         }
 
         Ok(())

+ 46 - 16
packages/autofmt/src/element.rs

@@ -2,6 +2,7 @@ use crate::Buffer;
 use dioxus_rsx::*;
 use proc_macro2::Span;
 use std::{fmt::Result, fmt::Write};
+use syn::{spanned::Spanned, Expr};
 
 #[derive(Debug)]
 enum ShortOptimization {
@@ -87,8 +88,11 @@ impl Buffer {
                     write!(self.buf, ", ")?;
                 }
 
-                for child in children {
+                for (id, child) in children.iter().enumerate() {
                     self.write_ident(child)?;
+                    if id != children.len() - 1 && children.len() > 1 {
+                        write!(self.buf, ", ")?;
+                    }
                 }
 
                 write!(self.buf, " ")?;
@@ -283,28 +287,54 @@ impl Buffer {
                         .map(|child_len| child_len + attr_len)
                 }
             }
-            [BodyNode::RawExpr(ref _expr)] => {
+            [BodyNode::RawExpr(ref expr)] => {
                 // TODO: let rawexprs to be inlined
-                // let span = syn::spanned::Spanned::span(&text);
-                // let (start, end) = (span.start(), span.end());
-                // if start.line == end.line {
-                //     Some(end.column - start.column)
-                // } else {
-                //     None
-                // }
-                None
+                get_expr_length(expr)
             }
             [BodyNode::Element(ref el)] => {
                 let attr_len = self.is_short_attrs(&el.attributes);
 
-                if attr_len > 80 {
-                    None
-                } else {
-                    self.is_short_children(&el.children)
-                        .map(|child_len| child_len + attr_len)
+                if el.children.is_empty() && attr_len < 80 {
+                    return Some(el.name.to_string().len());
+                }
+
+                if el.children.len() == 1 {
+                    if let BodyNode::Text(ref text) = el.children[0] {
+                        if text.value().len() + el.name.to_string().len() + attr_len < 80 {
+                            return Some(text.value().len() + el.name.to_string().len() + attr_len);
+                        }
+                    }
                 }
+
+                None
+            }
+            // todo, allow non-elements to be on the same line
+            items => {
+                let mut total_count = 0;
+
+                for item in items {
+                    match item {
+                        BodyNode::Component(_) | BodyNode::Element(_) => return None,
+                        BodyNode::Text(text) => total_count += text.value().len(),
+                        BodyNode::RawExpr(expr) => match get_expr_length(expr) {
+                            Some(len) => total_count += len,
+                            None => return None,
+                        },
+                    }
+                }
+
+                Some(total_count)
             }
-            _ => None,
         }
     }
 }
+
+fn get_expr_length(expr: &Expr) -> Option<usize> {
+    let span = expr.span();
+    let (start, end) = (span.start(), span.end());
+    if start.line == end.line {
+        Some(end.column - start.column)
+    } else {
+        None
+    }
+}

+ 61 - 5
packages/autofmt/src/expr.rs

@@ -16,16 +16,46 @@ impl Buffer {
         let placement = exp.span();
         let start = placement.start();
         let end = placement.end();
-        let num_spaces_desired = (self.indent * 4) as isize;
+        // let num_spaces_desired = (self.indent * 4) as isize;
 
-        let first = &self.src[start.line - 1];
-        let num_spaces_real = first.chars().take_while(|c| c.is_whitespace()).count() as isize;
+        // if the expr is on one line, just write it directly
+        if start.line == end.line {
+            write!(
+                self.buf,
+                "{}",
+                &self.src[start.line - 1][start.column - 1..end.column].trim()
+            )?;
+            return Ok(());
+        }
+
+        // If the expr is multiline, we want to collect all of its lines together and write them out properly
+        // This involves unshifting the first line if it's aligned
 
-        let offset = num_spaces_real - num_spaces_desired;
+        let first_line = &self.src[start.line - 1];
+        write!(
+            self.buf,
+            "{}",
+            &first_line[start.column - 1..first_line.len()].trim()
+        )?;
 
-        for line in &self.src[start.line - 1..end.line] {
+        let first_prefix = &self.src[start.line - 1][..start.column];
+        let offset = match first_prefix.trim() {
+            "" => 0,
+            _ => first_prefix
+                .chars()
+                .rev()
+                .take_while(|c| c.is_whitespace())
+                .count() as isize,
+        };
+
+        for (id, line) in self.src[start.line..end.line].iter().enumerate() {
             writeln!(self.buf)?;
             // trim the leading whitespace
+            let line = match id {
+                x if x == (end.line - start.line) - 1 => &line[..end.column],
+                _ => line,
+            };
+
             if offset < 0 {
                 for _ in 0..-offset {
                     write!(self.buf, " ")?;
@@ -39,6 +69,32 @@ impl Buffer {
             }
         }
 
+        // let first = &self.src[start.line - 1];
+        // let num_spaces_real = first.chars().take_while(|c| c.is_whitespace()).count() as isize;
+        // let offset = num_spaces_real - num_spaces_desired;
+
+        // for (row, line) in self.src[start.line - 1..end.line].iter().enumerate() {
+        //     let line = match row {
+        //         0 => &line[start.column - 1..],
+        //         a if a == (end.line - start.line) => &line[..end.column - 1],
+        //         _ => line,
+        //     };
+
+        //     writeln!(self.buf)?;
+        //     // trim the leading whitespace
+        //     if offset < 0 {
+        //         for _ in 0..-offset {
+        //             write!(self.buf, " ")?;
+        //         }
+
+        //         write!(self.buf, "{}", line)?;
+        //     } else {
+        //         let offset = offset as usize;
+        //         let right = &line[offset..];
+        //         write!(self.buf, "{}", right)?;
+        //     }
+        // }
+
         Ok(())
     }
 }

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

@@ -124,7 +124,7 @@ pub fn apply_format(input: &str, block: FormattedBlock) -> String {
     let (left, _) = input.split_at(start);
     let (_, right) = input.split_at(end);
 
-    dbg!(&block.formatted);
+    // dbg!(&block.formatted);
 
     format!("{}{}{}", left, block.formatted, right)
 }

+ 2 - 0
packages/autofmt/tests/samples.rs

@@ -23,3 +23,5 @@ twoway! ("complex" => complex);
 twoway! ("tiny" => tiny);
 
 twoway! ("tinynoopt" => tinynoopt);
+
+twoway! ("long" => long);

+ 3 - 3
packages/autofmt/tests/samples/complex.rsx

@@ -27,9 +27,7 @@ rsx! {
                 }
             })
         }
-        div { class: "px-4",
-            is_current.then(|| rsx!{ children })
-        }
+        div { class: "px-4", is_current.then(|| rsx!{ children }) }
     }
 
     // No nesting
@@ -47,4 +45,6 @@ rsx! {
             let blah = 120;
         }
     }
+
+    div { asdbascasdbasd, asbdasbdabsd, asbdabsdbasdbas }
 }

+ 38 - 0
packages/autofmt/tests/samples/long.rsx

@@ -0,0 +1,38 @@
+use dioxus::prelude::*;
+
+#[inline_props]
+pub fn Explainer<'a>(
+    cx: Scope<'a>,
+    invert: bool,
+    title: &'static str,
+    content: Element<'a>,
+    flasher: Element<'a>,
+) -> Element {
+    // pt-5 sm:pt-24 lg:pt-24
+
+    let mut right = rsx! {
+        div { class: "relative w-1/2", flasher }
+    };
+
+    let align = match invert {
+        true => "mr-auto ml-16",
+        false => "ml-auto mr-16",
+    };
+
+    let mut left = rsx! {
+        div { class: "relative w-1/2 {align} max-w-md leading-8",
+            h2 { class: "mb-6 text-3xl leading-tight md:text-4xl md:leading-tight lg:text-3xl lg:leading-tight font-heading font-mono font-bold",
+                "{title}"
+            }
+            content
+        }
+    };
+
+    if *invert {
+        std::mem::swap(&mut left, &mut right);
+    }
+
+    cx.render(rsx! {
+        div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", left, right }
+    })
+}

+ 14 - 6
packages/autofmt/tests/samples/simple.rsx

@@ -1,9 +1,6 @@
 rsx! {
     div { "hello world!" }
-    div {
-        "hello world!"
-        "goodbye world!"
-    }
+    div { "hello world!", "goodbye world!" }
 
     // Simple div
     div { "hello world!" }
@@ -15,7 +12,16 @@ rsx! {
     div { div { "nested" } }
 
     // Nested two level
-    div { div { h1 { "highly nested" } } }
+    div {
+        div { h1 { "highly nested" } }
+    }
+
+    // Anti-Nested two level
+    div {
+        div {
+            div { h1 { "highly nested" } }
+        }
+    }
 
     // Compression
     h3 { class: "mb-2 text-xl font-bold", "Invite Member" }
@@ -30,7 +36,9 @@ rsx! {
     img { class: "mb-6 mx-auto h-24", src: "artemis-assets/images/friends.png", alt: "" }
 
     // One level compression
-    div { a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", "Send invitation" } }
+    div {
+        a { class: "py-2 px-3 bg-indigo-500 hover:bg-indigo-600 rounded text-xs text-white", href: "#", "Send invitation" }
+    }
 
     // Components
     Component { ..Props {} }

+ 2 - 0
packages/autofmt/tests/wrong.rs

@@ -14,3 +14,5 @@ macro_rules! twoway {
 twoway!("comments" => comments);
 
 twoway!("multi" => multi);
+
+twoway!("multiexpr" => multiexpr);

+ 3 - 0
packages/autofmt/tests/wrong/multiexpr.rsx

@@ -0,0 +1,3 @@
+cx.render(rsx! {
+    div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", left, right }
+})

+ 3 - 0
packages/autofmt/tests/wrong/multiexpr.wrong.rsx

@@ -0,0 +1,3 @@
+cx.render(rsx! {
+    div { class: "flex flex-wrap items-center dark:text-white py-16 border-t font-light", left, right }
+})

+ 3 - 3
packages/rsx/src/component.rs

@@ -23,7 +23,7 @@ use syn::{
     Token,
 };
 
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub struct Component {
     pub name: syn::Path,
     pub prop_gen_args: Option<AngleBracketedGenericArguments>,
@@ -193,13 +193,13 @@ impl ToTokens for Component {
 }
 
 // the struct's fields info
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub struct ComponentField {
     pub name: Ident,
     pub content: ContentField,
 }
 
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub enum ContentField {
     ManExpr(Expr),
     Formatted(LitStr),

+ 3 - 3
packages/rsx/src/element.rs

@@ -10,7 +10,7 @@ use syn::{
 // =======================================
 // Parse the VNode::Element type
 // =======================================
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub struct Element {
     pub name: Ident,
     pub key: Option<LitStr>,
@@ -190,7 +190,7 @@ impl ToTokens for Element {
     }
 }
 
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub enum ElementAttr {
     /// attribute: "valuee {}"
     AttrText { name: Ident, value: LitStr },
@@ -231,7 +231,7 @@ impl ElementAttr {
     }
 }
 
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub struct ElementAttrNamed {
     pub el_name: Ident,
     pub attr: ElementAttr,

+ 1 - 0
packages/rsx/src/lib.rs

@@ -33,6 +33,7 @@ use syn::{
     Result, Token,
 };
 
+#[derive(Debug)]
 pub struct CallBody {
     pub roots: Vec<BodyNode>,
 }

+ 1 - 1
packages/rsx/src/node.rs

@@ -16,7 +16,7 @@ Parse
 -> "text {with_args}"
 -> (0..10).map(|f| rsx!("asd")),  // <--- notice the comma - must be a complete expr
 */
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Debug)]
 pub enum BodyNode {
     Element(Element),
     Component(Component),