From 93a755f000301edf717d857f74577e1f610d8c1b Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Sun, 23 Jun 2024 21:25:27 +0200 Subject: [PATCH 1/8] Add possibility to have non void self closing HTML tags --- formatter/src/formatter/element.rs | 18 ++++++++++++++++-- formatter/src/formatter/mod.rs | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index 84fe88e..1ed85af 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -9,7 +9,7 @@ impl Formatter<'_> { let is_void = is_void_element(&name, !element.children.is_empty()); self.opening_tag(element, is_void); - if !is_void { + if !(is_void || self.settings.allow_non_void_self_closing_tags) { self.children(&element.children, element.attributes().len()); self.flush_comments(element.close_tag.span().end().line - 1); self.closing_tag(element) @@ -22,7 +22,7 @@ impl Formatter<'_> { self.attributes(element.attributes()); - if is_void { + if is_void || self.settings.allow_non_void_self_closing_tags { self.printer.word("/>"); } else { self.printer.word(">") @@ -155,6 +155,14 @@ mod tests { }) }}; } + macro_rules! format_element_with_self_closing_tag { + ($($tt:tt)*) => {{ + let element = element! { $($tt)* }; + format_with(FormatterSettings { max_width: 40, allow_non_void_self_closing_tags: true, ..Default::default() }, |formatter| { + formatter.element(&element) + }) + }}; + } macro_rules! format_element_from_string { ($val:expr) => {{ format_element_from_string( @@ -221,6 +229,12 @@ mod tests { "###); } + #[test] + fn no_children_allow_self_closing() { + let formatted = format_element_with_self_closing_tag! { < div /> }; + insta::assert_snapshot!(formatted, @"
"); + } + #[test] fn child_element() { let formatted = format_element! {
"hello"
}; diff --git a/formatter/src/formatter/mod.rs b/formatter/src/formatter/mod.rs index 3c780c1..9d69ba6 100644 --- a/formatter/src/formatter/mod.rs +++ b/formatter/src/formatter/mod.rs @@ -73,6 +73,9 @@ pub struct FormatterSettings { /// Determines placement of braces around single expression attribute values pub attr_value_brace_style: AttributeValueBraceStyle, + /// Allows non void HTML tags like div to be self closing. If set to true,
will no longer format to
+ pub allow_non_void_self_closing_tags: bool, + /// Determines macros to be formatted. Default: leptos::view, view pub macro_names: Vec, @@ -88,6 +91,7 @@ impl Default for FormatterSettings { attr_value_brace_style: AttributeValueBraceStyle::WhenRequired, indentation_style: IndentationStyle::Auto, newline_style: NewlineStyle::Auto, + allow_non_void_self_closing_tags: false, macro_names: vec!["leptos::view".to_string(), "view".to_string()], attr_values: HashMap::new(), } From 026ace6dd736ff1f11ca90c529015489d5446067 Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Sun, 23 Jun 2024 21:36:48 +0200 Subject: [PATCH 2/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c3185cf..df8266f 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ indentation_style = "Auto" # "Tabs", "Spaces" or "Auto" newline_style = "Auto" # "Unix", "Windows" or "Auto" attr_value_brace_style = "WhenRequired" # "Always", "AlwaysUnlessLit", "WhenRequired" or "Preserve" macro_names = [ "leptos::view", "view" ] # Macro names which will be formatted +allow_non_void_self_closing_tags = true # false by default # Attribute values can be formatted by custom formatters # Every attribute name may only select one formatter (this might change later on) From 68e45b5b9dfc2f1411fcb1a56004ab514f6aab47 Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Sun, 23 Jun 2024 22:29:56 +0200 Subject: [PATCH 3/8] Fix forgotten usecase & add more tests --- formatter/src/formatter/element.rs | 277 ++++++++++++++++++++++++++++- 1 file changed, 269 insertions(+), 8 deletions(-) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index 1ed85af..dcf00e9 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -9,7 +9,9 @@ impl Formatter<'_> { let is_void = is_void_element(&name, !element.children.is_empty()); self.opening_tag(element, is_void); - if !(is_void || self.settings.allow_non_void_self_closing_tags) { + if !(is_void + || (self.settings.allow_non_void_self_closing_tags && element.children.is_empty())) + { self.children(&element.children, element.attributes().len()); self.flush_comments(element.close_tag.span().end().line - 1); self.closing_tag(element) @@ -22,7 +24,9 @@ impl Formatter<'_> { self.attributes(element.attributes()); - if is_void || self.settings.allow_non_void_self_closing_tags { + if is_void + || (self.settings.allow_non_void_self_closing_tags && element.children.is_empty()) + { self.printer.word("/>"); } else { self.printer.word(">") @@ -174,6 +178,18 @@ mod tests { ) }}; } + macro_rules! format_element_from_string_with_self_closing_tag { + ($val:expr) => {{ + format_element_from_string( + FormatterSettings { + max_width: 40, + allow_non_void_self_closing_tags: true, + ..Default::default() + }, + $val, + ) + }}; + } #[test] fn no_children() { @@ -229,12 +245,6 @@ mod tests { "###); } - #[test] - fn no_children_allow_self_closing() { - let formatted = format_element_with_self_closing_tag! { < div /> }; - insta::assert_snapshot!(formatted, @"
"); - } - #[test] fn child_element() { let formatted = format_element! {
"hello"
}; @@ -423,4 +433,255 @@ mod tests {
"#); } + + // Self Closing Tag + + #[test] + fn self_closing_no_children() { + let formatted = format_element_with_self_closing_tag! { < div /> }; + insta::assert_snapshot!(formatted, @"
"); + } + + #[test] + fn self_closing_no_children_single_attr() { + let formatted = format_element_with_self_closing_tag! { < div width=12 > < / div > }; + insta::assert_snapshot!(formatted, @"
"); + } + + #[test] + fn self_closing_no_children_multi_attr() { + let formatted = format_element_with_self_closing_tag! {
}; + insta::assert_snapshot!(formatted, @"
"); + } + + #[test] + fn self_closing_no_children_single_long_attr() { + let formatted = format_element_with_self_closing_tag! {
}; + + insta::assert_snapshot!(formatted, @"
"); + } + + #[test] + fn self_closing_no_children_multi_long_attr() { + let formatted = format_element_with_self_closing_tag! {
}; + insta::assert_snapshot!(formatted, @r###" +
+ "###); + } + + #[test] + fn self_closing_no_children_multi_attr_with_comment() { + println!("test"); + let formatted = format_element_from_string_with_self_closing_tag!(indoc! {" +
+ "}); + + insta::assert_snapshot!(formatted, @r###" +
+ "###); + } + + #[test] + fn self_closing_child_element() { + let formatted = format_element_with_self_closing_tag! {
"hello"
}; + insta::assert_snapshot!(formatted, @r#" +
+ "hello" +
+ "#); + } + + #[test] + fn self_closing_child_element_single_textual() { + let formatted = format_element_with_self_closing_tag! {
"hello"
}; + insta::assert_snapshot!(formatted, @r#"
"hello"
"#); + } + + #[test] + fn self_closing_child_element_single_textual_unquoted() { + let formatted = format_element_from_string_with_self_closing_tag!("
hello
"); + insta::assert_snapshot!(formatted, @r###"
hello
"###); + } + + #[test] + fn self_closing_child_element_single_textual_single_attr() { + let formatted = format_element_with_self_closing_tag! {
"hello"
}; + insta::assert_snapshot!(formatted, @r#"
"hello"
"#); + } + + #[test] + fn self_closing_child_element_single_textual_multi_attr() { + let formatted = + format_element_with_self_closing_tag! {
"hello"
}; + insta::assert_snapshot!(formatted, @r#" +
+ "hello" +
+ "#); + } + + #[test] + fn self_closing_child_element_two_textual() { + let formatted = + format_element_with_self_closing_tag! {
"The count is " {count}
}; + insta::assert_snapshot!(formatted, @r#"
"The count is " {count}
"#); + } + + #[test] + fn self_closing_child_element_many_textual() { + let formatted = format_element_with_self_closing_tag! {
"The current count is: " {count} ". Increment by one is this: " {count + 1}
}; + insta::assert_snapshot!(formatted, @r#" +
+ "The current count is: " {count} + ". Increment by one is this: " {count + 1} +
+ "#); + } + + #[test] + fn self_closing_child_element_two_textual_unquoted() { + let formatted = format_element_from_string_with_self_closing_tag! { "
The count is {count}.
" }; + insta::assert_snapshot!(formatted, @r#"
The count is {count}.
"#); + } + + #[test] + fn self_closing_child_element_two_textual_unquoted_no_trailingspace() { + let formatted = + format_element_from_string_with_self_closing_tag! { "
The count is{count}
" }; + insta::assert_snapshot!(formatted, @r#"
The count is{count}
"#); + } + + #[test] + fn self_closing_child_element_many_textual_unquoted() { + let formatted = format_element_from_string_with_self_closing_tag! { "
The current count is: {count}. Increment by one is this: {count + 1}
" }; + insta::assert_snapshot!(formatted, @r###" +
+ The current count is: {count}. Increment by one is this: + {count + 1} +
+ "###); + } + // view! {

Something: {something} .

} + + #[test] + fn self_closing_html_unquoted_text() { + let formatted = + format_element_from_string_with_self_closing_tag!(r##"
Unquoted text
"##); + insta::assert_snapshot!(formatted, @"
Unquoted text
"); + } + + #[test] + fn self_closing_html_unquoted_text_with_surrounding_spaces() { + let formatted = format_element_from_string_with_self_closing_tag!( + r##"
Unquoted text with spaces
"## + ); + insta::assert_snapshot!(formatted, @"
Unquoted text with spaces
"); + } + + #[test] + fn self_closing_html_unquoted_text_multiline() { + let formatted = format_element_from_string_with_self_closing_tag!(indoc! {" +
+ Unquoted text + with spaces +
+ "}); + + insta::assert_snapshot!(formatted, @r###" +
+ Unquoted text + with spaces +
"###); + } + + #[test] + fn self_closing_single_empty_line() { + let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" +
+
+ "#}); + + insta::assert_snapshot!(formatted, @r###" +
+
+ "###); + } + + #[test] + fn self_closing_multiple_empty_lines() { + let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" +
+
+ "#}); + + insta::assert_snapshot!(formatted, @r###" +
+
+ "###); + } + + #[test] + fn self_closing_surrounded_by_empty_lines() { + let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" + +
+
+ + "#}); + + insta::assert_snapshot!(formatted, @r###" +
+
+ "###); + } + + #[test] + fn self_closing_other_test() { + let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" +
+
+ + "Sign in with google" +
+
+ "#}); + + insta::assert_snapshot!(formatted, @r#" +
+
+ + "Sign in with google" +
+
+ "#); + } } From 5eec4f439e1c44c6ec9703db44cb76b427032f80 Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Tue, 9 Jul 2024 22:29:22 +0200 Subject: [PATCH 4/8] Use an enum to set the formatting style & prune the tests --- README.md | 2 +- formatter/src/formatter/element.rs | 317 ++++++----------------------- formatter/src/formatter/mod.rs | 16 +- 3 files changed, 76 insertions(+), 259 deletions(-) diff --git a/README.md b/README.md index df8266f..180920a 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ indentation_style = "Auto" # "Tabs", "Spaces" or "Auto" newline_style = "Auto" # "Unix", "Windows" or "Auto" attr_value_brace_style = "WhenRequired" # "Always", "AlwaysUnlessLit", "WhenRequired" or "Preserve" macro_names = [ "leptos::view", "view" ] # Macro names which will be formatted -allow_non_void_self_closing_tags = true # false by default +closing_tag_style = "Preserve" # "Preserve", "SelfClosing" or "NonSelfClosing" # Attribute values can be formatted by custom formatters # Every attribute name may only select one formatter (this might change later on) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index dcf00e9..2ad20a0 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -1,4 +1,4 @@ -use crate::formatter::Formatter; +use crate::{formatter::Formatter, ClosingTagStyle}; use rstml::node::{Node, NodeAttribute, NodeElement}; use syn::spanned::Spanned; @@ -7,26 +7,25 @@ impl Formatter<'_> { pub fn element(&mut self, element: &NodeElement) { let name = element.name().to_string(); let is_void = is_void_element(&name, !element.children.is_empty()); - self.opening_tag(element, is_void); + let is_self_closing = is_self_closing(is_void, self.settings.closing_tag_style); + let is_empty = element.children.is_empty(); - if !(is_void - || (self.settings.allow_non_void_self_closing_tags && element.children.is_empty())) - { + self.opening_tag(element, is_self_closing, is_empty); + + if !(is_self_closing && is_empty) { self.children(&element.children, element.attributes().len()); self.flush_comments(element.close_tag.span().end().line - 1); self.closing_tag(element) } } - fn opening_tag(&mut self, element: &NodeElement, is_void: bool) { + fn opening_tag(&mut self, element: &NodeElement, is_self_closing: bool, is_empty: bool) { self.printer.word("<"); self.node_name(&element.open_tag.name); self.attributes(element.attributes()); - if is_void - || (self.settings.allow_non_void_self_closing_tags && element.children.is_empty()) - { + if is_self_closing && is_empty { self.printer.word("/>"); } else { self.printer.word(">") @@ -142,11 +141,17 @@ fn is_void_element(name: &str, has_children: bool) -> bool { } } +fn is_self_closing(is_void: bool, closing_tag_style: ClosingTagStyle) -> bool { + closing_tag_style == ClosingTagStyle::SelfClosing + || closing_tag_style == ClosingTagStyle::Preserve && is_void +} + #[cfg(test)] mod tests { use indoc::indoc; use crate::{ + formatter::ClosingTagStyle, formatter::FormatterSettings, test_helpers::{element, format_element_from_string, format_with}, }; @@ -162,7 +167,7 @@ mod tests { macro_rules! format_element_with_self_closing_tag { ($($tt:tt)*) => {{ let element = element! { $($tt)* }; - format_with(FormatterSettings { max_width: 40, allow_non_void_self_closing_tags: true, ..Default::default() }, |formatter| { + format_with(FormatterSettings { max_width: 40, closing_tag_style: ClosingTagStyle::SelfClosing, ..Default::default() }, |formatter| { formatter.element(&element) }) }}; @@ -178,18 +183,6 @@ mod tests { ) }}; } - macro_rules! format_element_from_string_with_self_closing_tag { - ($val:expr) => {{ - format_element_from_string( - FormatterSettings { - max_width: 40, - allow_non_void_self_closing_tags: true, - ..Default::default() - }, - $val, - ) - }}; - } #[test] fn no_children() { @@ -437,251 +430,65 @@ mod tests { // Self Closing Tag #[test] - fn self_closing_no_children() { - let formatted = format_element_with_self_closing_tag! { < div /> }; - insta::assert_snapshot!(formatted, @"
"); - } - - #[test] - fn self_closing_no_children_single_attr() { - let formatted = format_element_with_self_closing_tag! { < div width=12 > < / div > }; - insta::assert_snapshot!(formatted, @"
"); - } - - #[test] - fn self_closing_no_children_multi_attr() { - let formatted = format_element_with_self_closing_tag! {
}; - insta::assert_snapshot!(formatted, @"
"); - } - - #[test] - fn self_closing_no_children_single_long_attr() { - let formatted = format_element_with_self_closing_tag! {
}; - - insta::assert_snapshot!(formatted, @"
"); - } - - #[test] - fn self_closing_no_children_multi_long_attr() { - let formatted = format_element_with_self_closing_tag! {
}; - insta::assert_snapshot!(formatted, @r###" -
- "###); - } - - #[test] - fn self_closing_no_children_multi_attr_with_comment() { - println!("test"); - let formatted = format_element_from_string_with_self_closing_tag!(indoc! {" -
- "}); - - insta::assert_snapshot!(formatted, @r###" -
- "###); - } - - #[test] - fn self_closing_child_element() { - let formatted = format_element_with_self_closing_tag! {
"hello"
}; - insta::assert_snapshot!(formatted, @r#" -
- "hello" -
- "#); - } - - #[test] - fn self_closing_child_element_single_textual() { - let formatted = format_element_with_self_closing_tag! {
"hello"
}; - insta::assert_snapshot!(formatted, @r#"
"hello"
"#); - } - - #[test] - fn self_closing_child_element_single_textual_unquoted() { - let formatted = format_element_from_string_with_self_closing_tag!("
hello
"); - insta::assert_snapshot!(formatted, @r###"
hello
"###); - } - - #[test] - fn self_closing_child_element_single_textual_single_attr() { - let formatted = format_element_with_self_closing_tag! {
"hello"
}; - insta::assert_snapshot!(formatted, @r#"
"hello"
"#); - } - - #[test] - fn self_closing_child_element_single_textual_multi_attr() { - let formatted = - format_element_with_self_closing_tag! {
"hello"
}; - insta::assert_snapshot!(formatted, @r#" -
- "hello" -
- "#); - } - - #[test] - fn self_closing_child_element_two_textual() { - let formatted = - format_element_with_self_closing_tag! {
"The count is " {count}
}; - insta::assert_snapshot!(formatted, @r#"
"The count is " {count}
"#); - } - - #[test] - fn self_closing_child_element_many_textual() { - let formatted = format_element_with_self_closing_tag! {
"The current count is: " {count} ". Increment by one is this: " {count + 1}
}; - insta::assert_snapshot!(formatted, @r#" -
- "The current count is: " {count} - ". Increment by one is this: " {count + 1} -
- "#); - } - - #[test] - fn self_closing_child_element_two_textual_unquoted() { - let formatted = format_element_from_string_with_self_closing_tag! { "
The count is {count}.
" }; - insta::assert_snapshot!(formatted, @r#"
The count is {count}.
"#); - } - - #[test] - fn self_closing_child_element_two_textual_unquoted_no_trailingspace() { - let formatted = - format_element_from_string_with_self_closing_tag! { "
The count is{count}
" }; - insta::assert_snapshot!(formatted, @r#"
The count is{count}
"#); - } - - #[test] - fn self_closing_child_element_many_textual_unquoted() { - let formatted = format_element_from_string_with_self_closing_tag! { "
The current count is: {count}. Increment by one is this: {count + 1}
" }; - insta::assert_snapshot!(formatted, @r###" -
- The current count is: {count}. Increment by one is this: - {count + 1} -
- "###); - } - // view! {

Something: {something} .

} - - #[test] - fn self_closing_html_unquoted_text() { - let formatted = - format_element_from_string_with_self_closing_tag!(r##"
Unquoted text
"##); - insta::assert_snapshot!(formatted, @"
Unquoted text
"); - } - - #[test] - fn self_closing_html_unquoted_text_with_surrounding_spaces() { - let formatted = format_element_from_string_with_self_closing_tag!( - r##"
Unquoted text with spaces
"## - ); - insta::assert_snapshot!(formatted, @"
Unquoted text with spaces
"); + fn self_closing_void_element_no_children_separate_closing_tag() { + let formatted = format_element_with_self_closing_tag! { < input >< / input > }; + insta::assert_snapshot!(formatted, @""); } #[test] - fn self_closing_html_unquoted_text_multiline() { - let formatted = format_element_from_string_with_self_closing_tag!(indoc! {" -
- Unquoted text - with spaces -
- "}); - - insta::assert_snapshot!(formatted, @r###" -
- Unquoted text - with spaces -
"###); + fn self_closing_void_element_no_children_self_closing_tag() { + let formatted = format_element_with_self_closing_tag! { < input / > }; + insta::assert_snapshot!(formatted, @""); } #[test] - fn self_closing_single_empty_line() { - let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" -
-
- "#}); - - insta::assert_snapshot!(formatted, @r###" -
-
- "###); + fn self_closing_non_void_element_with_child() { + let formatted = format_element_with_self_closing_tag! { < div > "Child" < / div > }; + insta::assert_snapshot!(formatted, @r#"
"Child"
"#); } #[test] - fn self_closing_multiple_empty_lines() { - let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" -
-
- "#}); - - insta::assert_snapshot!(formatted, @r###" -
-
- "###); + fn self_closing_non_void_element_no_children_separate_closing_tag() { + let formatted = format_element_with_self_closing_tag! { < div >< / div > }; + insta::assert_snapshot!(formatted, @"
"); } #[test] - fn self_closing_surrounded_by_empty_lines() { - let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" - -
-
- - "#}); - - insta::assert_snapshot!(formatted, @r###" -
-
- "###); + fn self_closing_non_void_element_no_children_self_closing_tag() { + let formatted = format_element_with_self_closing_tag! { < div / > }; + insta::assert_snapshot!(formatted, @"
"); } - #[test] - fn self_closing_other_test() { - let formatted = format_element_from_string_with_self_closing_tag!(indoc! {r#" -
-
- - "Sign in with google" -
-
- "#}); - - insta::assert_snapshot!(formatted, @r#" -
-
- - "Sign in with google" -
-
- "#); - } + // Non Self Closing Tag + // TODO uncomment when macro is properly working + + // #[test] + // fn non_self_closing_void_element_no_children_separate_closing_tag() { + // let formatted = format_element_with_self_closing_tag! { < input >< / input > }; + // insta::assert_snapshot!(formatted, @""); + // } + + // #[test] + // fn non_self_closing_void_element_no_children_self_closing_tag() { + // let formatted = format_element_with_self_closing_tag! { < input / > }; + // insta::assert_snapshot!(formatted, @""); + // } + + // #[test] + // fn non_self_closing_non_void_element_with_child() { + // let formatted = format_element_with_self_closing_tag! { < div > "Child" < / div > }; + // insta::assert_snapshot!(formatted, @r#"
"Hello"
"#); + // } + + // #[test] + // fn non_self_closing_non_void_element_no_children_separate_closing_tag() { + // let formatted = format_element_with_self_closing_tag! { < div >< / div > }; + // insta::assert_snapshot!(formatted, @"
"); + // } + + // #[test] + // fn non_self_closing_non_void_element_no_children_self_closing_tag() { + // let formatted = format_element_with_self_closing_tag! { < div / > }; + // insta::assert_snapshot!(formatted, @"
"); + // } } diff --git a/formatter/src/formatter/mod.rs b/formatter/src/formatter/mod.rs index 9d69ba6..ca5385a 100644 --- a/formatter/src/formatter/mod.rs +++ b/formatter/src/formatter/mod.rs @@ -19,6 +19,16 @@ pub use mac::{ParentIndent, ViewMacro}; use serde::Deserialize; use serde::Serialize; +#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] +pub enum ClosingTagStyle { + /// Preserve the original closing tag style (self-closing or a separate closing tag) + Preserve, + /// Self closing tag for elements with no children: `
` formats to `
` + SelfClosing, + /// Separate closing tag for elements with no children: `
` formats to `
` + NonSelfClosing, +} + #[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] pub enum AttributeValueBraceStyle { Always, @@ -73,8 +83,8 @@ pub struct FormatterSettings { /// Determines placement of braces around single expression attribute values pub attr_value_brace_style: AttributeValueBraceStyle, - /// Allows non void HTML tags like div to be self closing. If set to true,
will no longer format to
- pub allow_non_void_self_closing_tags: bool, + /// Preferred style for closing tags (self-closing or not) when a non-void element has no children + pub closing_tag_style: ClosingTagStyle, /// Determines macros to be formatted. Default: leptos::view, view pub macro_names: Vec, @@ -91,7 +101,7 @@ impl Default for FormatterSettings { attr_value_brace_style: AttributeValueBraceStyle::WhenRequired, indentation_style: IndentationStyle::Auto, newline_style: NewlineStyle::Auto, - allow_non_void_self_closing_tags: false, + closing_tag_style: ClosingTagStyle::Preserve, macro_names: vec!["leptos::view".to_string(), "view".to_string()], attr_values: HashMap::new(), } From bccf3696b72bb9df456385326dbf2a7bb3e5059c Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:34:09 +0200 Subject: [PATCH 5/8] Add space before "/>" & Update test macros and is_self_closing behaviour --- formatter/src/formatter/element.rs | 244 +++++++++++++++++------------ 1 file changed, 143 insertions(+), 101 deletions(-) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index 2ad20a0..719855d 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -6,26 +6,25 @@ use syn::spanned::Spanned; impl Formatter<'_> { pub fn element(&mut self, element: &NodeElement) { let name = element.name().to_string(); - let is_void = is_void_element(&name, !element.children.is_empty()); - let is_self_closing = is_self_closing(is_void, self.settings.closing_tag_style); - let is_empty = element.children.is_empty(); + let is_self_closing = is_self_closing(element, &name, self.settings.closing_tag_style); - self.opening_tag(element, is_self_closing, is_empty); + self.opening_tag(element, is_self_closing); - if !(is_self_closing && is_empty) { + if !is_self_closing { self.children(&element.children, element.attributes().len()); self.flush_comments(element.close_tag.span().end().line - 1); self.closing_tag(element) } } - fn opening_tag(&mut self, element: &NodeElement, is_self_closing: bool, is_empty: bool) { + fn opening_tag(&mut self, element: &NodeElement, is_self_closing: bool) { self.printer.word("<"); self.node_name(&element.open_tag.name); self.attributes(element.attributes()); - if is_self_closing && is_empty { + if is_self_closing { + self.printer.nbsp(); self.printer.word("/>"); } else { self.printer.word(">") @@ -117,33 +116,41 @@ impl Formatter<'_> { } } -fn is_void_element(name: &str, has_children: bool) -> bool { - if name.chars().next().unwrap().is_uppercase() { - !has_children - } else { - matches!( - name, - "area" - | "base" - | "br" - | "col" - | "embed" - | "hr" - | "img" - | "input" - | "link" - | "meta" - | "param" - | "source" - | "track" - | "wbr" - ) - } +fn is_void_element(name: &str) -> bool { + matches!( + name, + "area" + | "base" + | "br" + | "col" + | "embed" + | "hr" + | "img" + | "input" + | "link" + | "meta" + | "param" + | "source" + | "track" + | "wbr" + ) } -fn is_self_closing(is_void: bool, closing_tag_style: ClosingTagStyle) -> bool { - closing_tag_style == ClosingTagStyle::SelfClosing - || closing_tag_style == ClosingTagStyle::Preserve && is_void +fn is_self_closing(element: &NodeElement, name: &str, closing_tag_style: ClosingTagStyle) -> bool { + if !element.children.is_empty() { + return false; + } + + if is_void_element(name) { + return true; + }; + + // At this point, it must be a non-void element that has no children + match closing_tag_style { + ClosingTagStyle::Preserve => element.close_tag.is_none(), + ClosingTagStyle::SelfClosing => true, + ClosingTagStyle::NonSelfClosing => false, + } } #[cfg(test)] @@ -158,20 +165,28 @@ mod tests { macro_rules! format_element { ($($tt:tt)*) => {{ - let element = element! { $($tt)* }; - format_with(FormatterSettings { max_width: 40, ..Default::default() }, |formatter| { - formatter.element(&element) - }) + format_element_with!(Default::default(), $($tt)*) }}; } - macro_rules! format_element_with_self_closing_tag { - ($($tt:tt)*) => {{ + + macro_rules! format_element_with_closing_style { + ($style:expr, $($tt:tt)*) => {{ + format_element_with!(FormatterSettings { + closing_tag_style: $style, + ..Default::default() + }, $($tt)*) + }}; + } + + macro_rules! format_element_with { + ($settings:expr, $($tt:tt)*) => {{ let element = element! { $($tt)* }; - format_with(FormatterSettings { max_width: 40, closing_tag_style: ClosingTagStyle::SelfClosing, ..Default::default() }, |formatter| { + format_with(FormatterSettings { max_width: 40, ..$settings }, |formatter| { formatter.element(&element) }) }}; } + macro_rules! format_element_from_string { ($val:expr) => {{ format_element_from_string( @@ -349,17 +364,17 @@ mod tests { fn single_empty_line() { let formatted = format_element_from_string!(indoc! {r#"
-
"#}); insta::assert_snapshot!(formatted, @r###"
-
"###); } @@ -368,19 +383,19 @@ mod tests { fn multiple_empty_lines() { let formatted = format_element_from_string!(indoc! {r#"
-
"#}); insta::assert_snapshot!(formatted, @r###"
-
"###); } @@ -390,16 +405,16 @@ mod tests { let formatted = format_element_from_string!(indoc! {r#"
-
"#}); insta::assert_snapshot!(formatted, @r###"
-
"###); } @@ -427,68 +442,95 @@ mod tests { "#); } - // Self Closing Tag + // Closing Tags Behaviour #[test] - fn self_closing_void_element_no_children_separate_closing_tag() { - let formatted = format_element_with_self_closing_tag! { < input >< / input > }; - insta::assert_snapshot!(formatted, @""); + fn void_element_no_children_separate_closing_tag() { + let preserve_formatted = + format_element_with_closing_style! { ClosingTagStyle::Preserve, < input >< / input > }; + let self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < input >< / input > }; + let non_self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < input >< / input > }; + + insta::assert_snapshot!(preserve_formatted, @""); + insta::assert_snapshot!(self_closing_formatted, @""); + insta::assert_snapshot!(non_self_closing_formatted, @""); } #[test] - fn self_closing_void_element_no_children_self_closing_tag() { - let formatted = format_element_with_self_closing_tag! { < input / > }; - insta::assert_snapshot!(formatted, @""); + fn void_element_no_children_self_closing_tag_one_line() { + let preserve_formatted = + format_element_with_closing_style! { ClosingTagStyle::Preserve, < input / > }; + let self_closing_formatted = + format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < input / > }; + let non_self_closing_formatted = + format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < input / > }; + + insta::assert_snapshot!(preserve_formatted, @""); + insta::assert_snapshot!(self_closing_formatted, @""); + insta::assert_snapshot!(non_self_closing_formatted, @""); } #[test] - fn self_closing_non_void_element_with_child() { - let formatted = format_element_with_self_closing_tag! { < div > "Child" < / div > }; - insta::assert_snapshot!(formatted, @r#"
"Child"
"#); + fn void_element_no_children_self_closing_tag_multi_line() { + let preserve_formatted = format_element_with_closing_style! { ClosingTagStyle::Preserve, < input key=1 class="veryveryvery longlonglong attributesattributesattributes listlistlist" / > }; + let self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < input key=1 class="veryveryvery longlonglong attributesattributesattributes listlistlist" / > }; + let non_self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < input key=1 class="veryveryvery longlonglong attributesattributesattributes listlistlist" / > }; + + insta::assert_snapshot!(preserve_formatted, @r#" + + "#); + insta::assert_snapshot!(self_closing_formatted, @r#" + + "#); + insta::assert_snapshot!(non_self_closing_formatted, @r#" + + "#); } #[test] - fn self_closing_non_void_element_no_children_separate_closing_tag() { - let formatted = format_element_with_self_closing_tag! { < div >< / div > }; - insta::assert_snapshot!(formatted, @"
"); + fn non_void_element_with_child() { + let preserve_formatted = format_element_with_closing_style! { ClosingTagStyle::Preserve, < div > "Child" < / div > }; + let self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < div > "Child" < / div > }; + let non_self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < div > "Child" < / div > }; + + insta::assert_snapshot!(preserve_formatted, @r#"
"Child"
"#); + insta::assert_snapshot!(self_closing_formatted, @r#"
"Child"
"#); + insta::assert_snapshot!(non_self_closing_formatted, @r#"
"Child"
"#); + } + + #[test] + fn non_void_element_no_children_separate_closing_tag() { + let preserve_formatted = + format_element_with_closing_style! { ClosingTagStyle::Preserve, < div >< / div > }; + let self_closing_formatted = + format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < div >< / div > }; + let non_self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < div >< / div > }; + + insta::assert_snapshot!(preserve_formatted, @"
"); + insta::assert_snapshot!(self_closing_formatted, @"
"); + insta::assert_snapshot!(non_self_closing_formatted, @"
"); } #[test] - fn self_closing_non_void_element_no_children_self_closing_tag() { - let formatted = format_element_with_self_closing_tag! { < div / > }; - insta::assert_snapshot!(formatted, @"
"); - } - - // Non Self Closing Tag - // TODO uncomment when macro is properly working - - // #[test] - // fn non_self_closing_void_element_no_children_separate_closing_tag() { - // let formatted = format_element_with_self_closing_tag! { < input >< / input > }; - // insta::assert_snapshot!(formatted, @""); - // } - - // #[test] - // fn non_self_closing_void_element_no_children_self_closing_tag() { - // let formatted = format_element_with_self_closing_tag! { < input / > }; - // insta::assert_snapshot!(formatted, @""); - // } - - // #[test] - // fn non_self_closing_non_void_element_with_child() { - // let formatted = format_element_with_self_closing_tag! { < div > "Child" < / div > }; - // insta::assert_snapshot!(formatted, @r#"
"Hello"
"#); - // } - - // #[test] - // fn non_self_closing_non_void_element_no_children_separate_closing_tag() { - // let formatted = format_element_with_self_closing_tag! { < div >< / div > }; - // insta::assert_snapshot!(formatted, @"
"); - // } - - // #[test] - // fn non_self_closing_non_void_element_no_children_self_closing_tag() { - // let formatted = format_element_with_self_closing_tag! { < div / > }; - // insta::assert_snapshot!(formatted, @"
"); - // } + fn non_void_element_no_children_self_closing_tag() { + let preserve_formatted = + format_element_with_closing_style! { ClosingTagStyle::Preserve, < div / > }; + let self_closing_formatted = + format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < div / > }; + let non_self_closing_formatted = + format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < div / > }; + + insta::assert_snapshot!(preserve_formatted, @"
"); + insta::assert_snapshot!(self_closing_formatted, @"
"); + insta::assert_snapshot!(non_self_closing_formatted, @"
"); + } } From b2fa0a2c6ca620831f36a87c18cbe8a0a877ac24 Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:40:29 +0200 Subject: [PATCH 6/8] Fix conflict --- formatter/src/formatter/element.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index 719855d..067ee92 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -442,6 +442,12 @@ mod tests { "#); } + #[test] + fn with_generics() { + let formatted = format_element! { /> }; + insta::assert_snapshot!(formatted, @"/>"); + } + // Closing Tags Behaviour #[test] From 0b121b8474d3d01002026a64aac6348dff93a565 Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Thu, 11 Jul 2024 18:44:09 +0200 Subject: [PATCH 7/8] Update new test --- formatter/src/formatter/element.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index 841cc5d..1df4cb0 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -446,7 +446,7 @@ mod tests { #[test] fn with_generics() { let formatted = format_element! { /> }; - insta::assert_snapshot!(formatted, @"/>"); + insta::assert_snapshot!(formatted, @" />"); } // Closing Tags Behaviour From 4253ccf73599210cb11041d47a12f01a2bf377fe Mon Sep 17 00:00:00 2001 From: V4ldum <56064897+V4ldum@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:32:47 +0200 Subject: [PATCH 8/8] Fix space for self closing tag & add test for formatting with one attribute --- formatter/src/formatter/element.rs | 68 ++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/formatter/src/formatter/element.rs b/formatter/src/formatter/element.rs index 1df4cb0..df811dc 100644 --- a/formatter/src/formatter/element.rs +++ b/formatter/src/formatter/element.rs @@ -22,10 +22,9 @@ impl Formatter<'_> { self.node_name(&element.open_tag.name); leptosfmt_prettyplease::unparse_generics(&element.open_tag.generics, self.printer); - self.attributes(element.attributes()); + self.attributes(element.attributes(), is_self_closing); if is_self_closing { - self.printer.nbsp(); self.printer.word("/>"); } else { self.printer.word(">") @@ -38,31 +37,43 @@ impl Formatter<'_> { self.printer.word(">"); } - fn attributes(&mut self, attributes: &[NodeAttribute]) { - if attributes.is_empty() { - return; - } + fn attributes(&mut self, attributes: &[NodeAttribute], trailing_space: bool) { + match attributes { + [] => { + if trailing_space { + self.printer.nbsp(); + } + } + [attribute] => { + self.printer.cbox(0); + self.printer.nbsp(); + self.attribute(attribute); - if let [attribute] = attributes { - self.printer.cbox(0); - self.printer.nbsp(); - self.attribute(attribute); - self.printer.end(); - } else { - self.printer.cbox_indent(); - self.printer.space(); + if trailing_space { + self.printer.nbsp(); + } + self.printer.end(); + } + _ => { + self.printer.cbox_indent(); + self.printer.space(); - let mut iter = attributes.iter().peekable(); - while let Some(attr) = iter.next() { - self.attribute(attr); + let mut iter = attributes.iter().peekable(); + while let Some(attr) = iter.next() { + self.attribute(attr); - if iter.peek().is_some() { - self.printer.space() + if iter.peek().is_some() { + self.printer.space() + } } - } - self.printer.zerobreak(); - self.printer.end_dedent(); + if trailing_space { + self.printer.space(); // Only results in a space if the consistent box didn't break + } else { + self.printer.zerobreak(); + } + self.printer.end_dedent(); + } } } @@ -477,6 +488,19 @@ mod tests { insta::assert_snapshot!(non_self_closing_formatted, @""); } + #[test] + fn void_element_no_children_self_closing_tag_single_attr() { + let preserve_formatted = + format_element_with_closing_style! { ClosingTagStyle::Preserve, < input key=1 / > }; + let self_closing_formatted = + format_element_with_closing_style! { ClosingTagStyle::SelfClosing, < input key=1 / > }; + let non_self_closing_formatted = format_element_with_closing_style! { ClosingTagStyle::NonSelfClosing, < input key=1 / > }; + + insta::assert_snapshot!(preserve_formatted, @""); + insta::assert_snapshot!(self_closing_formatted, @""); + insta::assert_snapshot!(non_self_closing_formatted, @""); + } + #[test] fn void_element_no_children_self_closing_tag_multi_line() { let preserve_formatted = format_element_with_closing_style! { ClosingTagStyle::Preserve, < input key=1 class="veryveryvery longlonglong attributesattributesattributes listlistlist" / > };