From e398947f130821f20fa805b4c8e74453602bdd59 Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:05:29 +0200 Subject: [PATCH 1/7] fix(fmt): Preserve multiple newlines between elements (#374) --- parser/v2/elementparser.go | 2 +- parser/v2/elementparser_test.go | 37 +++++++++++++++++++ .../element_double_newline_is_preserved.txt | 28 ++++++++++++++ parser/v2/gocodeparser.go | 2 +- parser/v2/stringexpressionparser.go | 2 +- parser/v2/textparser.go | 2 +- parser/v2/textparser_test.go | 16 +++++++- parser/v2/types.go | 24 +++++++++--- 8 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 parser/v2/formattestdata/element_double_newline_is_preserved.txt diff --git a/parser/v2/elementparser.go b/parser/v2/elementparser.go index 3c3e73d3f..60290e4c4 100644 --- a/parser/v2/elementparser.go +++ b/parser/v2/elementparser.go @@ -471,7 +471,7 @@ func addTrailingSpaceAndValidate(start parse.Position, e Element, pi *parse.Inpu if err != nil { return e, false, err } - e.TrailingSpace, err = NewTrailingSpace(ws) + e.TrailingSpace, err = NewTrailingSpace(ws, true) if err != nil { return e, false, err } diff --git a/parser/v2/elementparser_test.go b/parser/v2/elementparser_test.go index 0c7796dc7..065f89ab2 100644 --- a/parser/v2/elementparser_test.go +++ b/parser/v2/elementparser_test.go @@ -1538,6 +1538,43 @@ func TestElementParser(t *testing.T) { }, }, }, + { + name: "element: with multiple newlines, should collapse to two", + input: `
+ + + + + +
`, + expected: Element{ + Name: "div", + NameRange: Range{ + From: Position{Index: 1, Line: 0, Col: 1}, + To: Position{Index: 4, Line: 0, Col: 4}, + }, + IndentChildren: true, + Children: []Node{ + Whitespace{Value: "\n\t"}, + Element{ + Name: "span", + NameRange: Range{ + From: Position{Index: 8, Line: 1, Col: 2}, + To: Position{Index: 12, Line: 1, Col: 6}, + }, + TrailingSpace: SpaceVerticalDouble, + }, + Element{ + Name: "span", + NameRange: Range{ + From: Position{Index: 26, Line: 5, Col: 2}, + To: Position{Index: 30, Line: 5, Col: 6}, + }, + TrailingSpace: SpaceVertical, + }, + }, + }, + }, { name: "element: can contain text that starts with for", input: `
for which any diff --git a/parser/v2/formattestdata/element_double_newline_is_preserved.txt b/parser/v2/formattestdata/element_double_newline_is_preserved.txt new file mode 100644 index 000000000..383dfc262 --- /dev/null +++ b/parser/v2/formattestdata/element_double_newline_is_preserved.txt @@ -0,0 +1,28 @@ +-- in -- +package main + +templ x() { +
+ Hello + + World + + + + + Foo Bar +
+} +-- out -- +package main + +templ x() { +
+ + Hello + World + + + Foo Bar +
+} diff --git a/parser/v2/gocodeparser.go b/parser/v2/gocodeparser.go index 027bcc45d..1c0b5c91b 100644 --- a/parser/v2/gocodeparser.go +++ b/parser/v2/gocodeparser.go @@ -36,7 +36,7 @@ var goCode = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { if err != nil { return r, false, err } - r.TrailingSpace, err = NewTrailingSpace(ws) + r.TrailingSpace, err = NewTrailingSpace(ws, true) if err != nil { return r, false, err } diff --git a/parser/v2/stringexpressionparser.go b/parser/v2/stringexpressionparser.go index 0a4457163..e48aca650 100644 --- a/parser/v2/stringexpressionparser.go +++ b/parser/v2/stringexpressionparser.go @@ -30,7 +30,7 @@ var stringExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err er if err != nil { return r, false, err } - r.TrailingSpace, err = NewTrailingSpace(ws) + r.TrailingSpace, err = NewTrailingSpace(ws, false) if err != nil { return r, false, err } diff --git a/parser/v2/textparser.go b/parser/v2/textparser.go index d5353a9c4..fef81e805 100644 --- a/parser/v2/textparser.go +++ b/parser/v2/textparser.go @@ -35,7 +35,7 @@ var textParser = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { if err != nil { return t, false, err } - t.TrailingSpace, err = NewTrailingSpace(ws) + t.TrailingSpace, err = NewTrailingSpace(ws, false) if err != nil { return t, false, err } diff --git a/parser/v2/textparser_test.go b/parser/v2/textparser_test.go index 2e4602f33..3118d0f29 100644 --- a/parser/v2/textparser_test.go +++ b/parser/v2/textparser_test.go @@ -80,7 +80,7 @@ func TestTextParser(t *testing.T) { }, }, { - name: "Multiline text is colected line by line", + name: "Multiline text is collected line by line", input: "Line 1\nLine 2", expected: Text{ Value: "Line 1", @@ -92,7 +92,7 @@ func TestTextParser(t *testing.T) { }, }, { - name: "Multiline text is colected line by line (Windows)", + name: "Multiline text is collected line by line (Windows)", input: "Line 1\r\nLine 2", expected: Text{ Value: "Line 1", @@ -103,6 +103,18 @@ func TestTextParser(t *testing.T) { TrailingSpace: "\n", }, }, + { + name: "Multiline text with multiple newlines is collected line by line", + input: "Line 1\n\n\n\nLine 2", + expected: Text{ + Value: "Line 1", + Range: Range{ + From: Position{Index: 0, Line: 0, Col: 0}, + To: Position{Index: 6, Line: 0, Col: 6}, + }, + TrailingSpace: "\n", + }, + }, } for _, tt := range tests { tt := tt diff --git a/parser/v2/types.go b/parser/v2/types.go index 34186c438..de93cd5f7 100644 --- a/parser/v2/types.go +++ b/parser/v2/types.go @@ -375,17 +375,27 @@ func (t HTMLTemplate) Write(w io.Writer, indent int) error { type TrailingSpace string const ( - SpaceNone TrailingSpace = "" - SpaceHorizontal TrailingSpace = " " - SpaceVertical TrailingSpace = "\n" + SpaceNone TrailingSpace = "" + SpaceHorizontal TrailingSpace = " " + SpaceVertical TrailingSpace = "\n" + SpaceVerticalDouble TrailingSpace = "\n\n" ) var ErrNonSpaceCharacter = errors.New("non space character found") -func NewTrailingSpace(s string) (ts TrailingSpace, err error) { +func NewTrailingSpace(s string, allowMulti bool) (ts TrailingSpace, err error) { var hasHorizontalSpace bool - for _, r := range s { + + runes := []rune(s) + + for i, r := range s { if r == '\n' { + if allowMulti && i < len(runes)-1 { + next := runes[i+1] + if next == '\n' { + return SpaceVerticalDouble, nil + } + } return SpaceVertical, nil } if unicode.IsSpace(r) { @@ -621,7 +631,9 @@ func writeNodes(w io.Writer, level int, nodes []Node, indent bool) error { } // Put a newline after the last node in indentation mode. if indent && ((nextNodeIsBlock(nodes, i) || i == len(nodes)-1) || shouldAlwaysBreakAfter(nodes[i])) { - trailing = SpaceVertical + if !strings.Contains(string(trailing), string(SpaceVertical)) { + trailing = SpaceVertical + } } switch trailing { case SpaceNone: From 2f6aa042a02d7457bfceb93c24b0a915ca30b94e Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Sat, 14 Sep 2024 01:22:33 +0200 Subject: [PATCH 2/7] fix(fmt): Double newline indent & strip newline in minify --- generator/generator.go | 2 +- .../element_double_newline_indent_issue.txt | 25 +++++++++++++++++++ ...wline_string_literal_termination_issue.txt | 25 +++++++++++++++++++ parser/v2/types.go | 8 +++--- 4 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 parser/v2/formattestdata/element_double_newline_indent_issue.txt create mode 100644 parser/v2/formattestdata/element_double_newline_string_literal_termination_issue.txt diff --git a/generator/generator.go b/generator/generator.go index 943c695b5..6830d943a 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -604,7 +604,7 @@ func (g *generator) writeWhitespaceTrailer(indentLevel int, n parser.TrailingSpa } // Normalize whitespace for minified output. In HTML, a single space is equivalent to // any number of spaces, tabs, or newlines. - if n == parser.SpaceVertical { + if n == parser.SpaceVertical || n == parser.SpaceVerticalDouble { n = parser.SpaceHorizontal } if _, err = g.w.WriteStringLiteral(indentLevel, string(n)); err != nil { diff --git a/parser/v2/formattestdata/element_double_newline_indent_issue.txt b/parser/v2/formattestdata/element_double_newline_indent_issue.txt new file mode 100644 index 000000000..4732a5a3b --- /dev/null +++ b/parser/v2/formattestdata/element_double_newline_indent_issue.txt @@ -0,0 +1,25 @@ +-- in -- +package main + +templ x() { +
+ + + // This line is indented incorrectly + + +
+} + +-- out -- +package main + +templ x() { +
+ + + // This line is indented incorrectly + + +
+} diff --git a/parser/v2/formattestdata/element_double_newline_string_literal_termination_issue.txt b/parser/v2/formattestdata/element_double_newline_string_literal_termination_issue.txt new file mode 100644 index 000000000..0962cc808 --- /dev/null +++ b/parser/v2/formattestdata/element_double_newline_string_literal_termination_issue.txt @@ -0,0 +1,25 @@ +-- in -- +package main + +// The below template caused the generated code to not strip newlines, causing error +templ x() { +
+ + + + + + +
+} +-- out -- +package main + +// The below template caused the generated code to not strip newlines, causing error +templ x() { +
+ + + +
+} diff --git a/parser/v2/types.go b/parser/v2/types.go index de93cd5f7..e300d4d1c 100644 --- a/parser/v2/types.go +++ b/parser/v2/types.go @@ -630,10 +630,8 @@ func writeNodes(w io.Writer, level int, nodes []Node, indent bool) error { trailing = wst.Trailing() } // Put a newline after the last node in indentation mode. - if indent && ((nextNodeIsBlock(nodes, i) || i == len(nodes)-1) || shouldAlwaysBreakAfter(nodes[i])) { - if !strings.Contains(string(trailing), string(SpaceVertical)) { - trailing = SpaceVertical - } + if indent && trailing != SpaceVerticalDouble && ((nextNodeIsBlock(nodes, i) || i == len(nodes)-1) || shouldAlwaysBreakAfter(nodes[i])) { + trailing = SpaceVertical } switch trailing { case SpaceNone: @@ -642,6 +640,8 @@ func writeNodes(w io.Writer, level int, nodes []Node, indent bool) error { level = 0 case SpaceVertical: level = startLevel + case SpaceVerticalDouble: + level = startLevel } if _, err := w.Write([]byte(trailing)); err != nil { return err From dc3c684cf14b359044cf195cca00a1636d0e5941 Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Sat, 14 Sep 2024 01:53:21 +0200 Subject: [PATCH 3/7] fix(fmt): Preserve newline after templ elements --- ...element_double_newline_after_component.txt | 29 +++++++++++++++++++ parser/v2/templateparser_test.go | 2 +- parser/v2/templelementparser.go | 26 ++++++++++++++++- parser/v2/templelementparser_test.go | 6 +++- parser/v2/types.go | 6 ++++ 5 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 parser/v2/formattestdata/element_double_newline_after_component.txt diff --git a/parser/v2/formattestdata/element_double_newline_after_component.txt b/parser/v2/formattestdata/element_double_newline_after_component.txt new file mode 100644 index 000000000..5792f45fd --- /dev/null +++ b/parser/v2/formattestdata/element_double_newline_after_component.txt @@ -0,0 +1,29 @@ +-- in -- +package main + +templ x() { +
+ @Hero() + + + + @Hero() + + + + +
+} +-- out -- +package main + +templ x() { +
+ @Hero() + + + @Hero() + + +
+} diff --git a/parser/v2/templateparser_test.go b/parser/v2/templateparser_test.go index 6b0eff2db..6c814951f 100644 --- a/parser/v2/templateparser_test.go +++ b/parser/v2/templateparser_test.go @@ -626,8 +626,8 @@ func TestTemplateParser(t *testing.T) { }, }, }, + TrailingSpace: SpaceHorizontal, }, - Whitespace{Value: " "}, Text{ Value: "Home", Range: Range{ diff --git a/parser/v2/templelementparser.go b/parser/v2/templelementparser.go index 9a50b5b03..51e884977 100644 --- a/parser/v2/templelementparser.go +++ b/parser/v2/templelementparser.go @@ -13,7 +13,11 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e return } - var r TemplElementExpression + r := TemplElementExpression{ + // Default behavior is always a trailing space + TrailingSpace: SpaceVertical, + } + // Parse the Go expression. if r.Expression, err = parseGo("templ element", pi, goexpression.TemplExpression); err != nil { return r, false, err @@ -26,6 +30,16 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e return } if !hasOpenBrace { + // Parse trailing whitespace after expression. + ws, _, err := parse.Whitespace.Parse(pi) + if err != nil { + return r, false, err + } + r.TrailingSpace, err = NewTrailingSpace(ws, true) + if err != nil { + return r, false, err + } + return r, true, nil } @@ -46,6 +60,16 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e return } + // Parse trailing whitespace after closing brace. + ws, _, err := parse.Whitespace.Parse(pi) + if err != nil { + return r, false, err + } + r.TrailingSpace, err = NewTrailingSpace(ws, true) + if err != nil { + return r, false, err + } + return r, true, nil } diff --git a/parser/v2/templelementparser_test.go b/parser/v2/templelementparser_test.go index 478620dc3..1ef8ffaf5 100644 --- a/parser/v2/templelementparser_test.go +++ b/parser/v2/templelementparser_test.go @@ -32,6 +32,7 @@ func TestTemplElementExpressionParser(t *testing.T) { }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -53,6 +54,7 @@ func TestTemplElementExpressionParser(t *testing.T) { }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -80,6 +82,7 @@ func TestTemplElementExpressionParser(t *testing.T) { }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -190,8 +193,8 @@ func TestTemplElementExpressionParser(t *testing.T) { To: Position{28, 1, 11}, }, }, + TrailingSpace: SpaceVertical, }, - Whitespace{Value: "\n\t\t\t"}, }, }, }, @@ -215,6 +218,7 @@ func TestTemplElementExpressionParser(t *testing.T) { }, }, }, + TrailingSpace: SpaceHorizontal, }, }, { diff --git a/parser/v2/types.go b/parser/v2/types.go index e300d4d1c..507d93b36 100644 --- a/parser/v2/types.go +++ b/parser/v2/types.go @@ -956,6 +956,12 @@ type TemplElementExpression struct { Expression Expression // Children returns the elements in a block element. Children []Node + // TrailingSpace lists what happens after the element. + TrailingSpace TrailingSpace +} + +func (t TemplElementExpression) Trailing() TrailingSpace { + return t.TrailingSpace } func (tee TemplElementExpression) ChildNodes() []Node { From aa31f9374a99fb9d457411de6b039253e5329cee Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Thu, 19 Sep 2024 20:45:50 +0200 Subject: [PATCH 4/7] fix(fmt): Preserve newlines in most parsers (#374) --- parser/v2/calltemplateparser.go | 5 + parser/v2/calltemplateparser_test.go | 22 ++++ parser/v2/elementparser.go | 7 +- parser/v2/forexpressionparser.go | 15 ++- parser/v2/forexpressionparser_test.go | 2 + .../calltemplate_newline_is_preserved.txt | 34 ++++++ .../comments_newline_is_preserved.txt | 31 ++++++ .../for_loops_are_placed_on_a_new_line.txt | 3 +- .../for_loops_newline_is_preserved.txt | 33 ++++++ .../if_statement_newline_is_preserved.txt | 37 +++++++ ...if_statements_are_placed_on_a_new_line.txt | 3 +- .../switch_newline_is_preserved.txt | 43 ++++++++ ...ch_statements_are_placed_on_a_new_line.txt | 3 +- parser/v2/gocodeparser.go | 7 +- parser/v2/htmlcommentparser.go | 5 + parser/v2/ifexpressionparser.go | 15 ++- parser/v2/ifexpressionparser_test.go | 11 +- parser/v2/stringexpressionparser.go | 7 +- parser/v2/switchexpressionparser.go | 15 ++- parser/v2/switchexpressionparser_test.go | 4 + parser/v2/templateparser_test.go | 10 +- parser/v2/templelementparser.go | 14 +-- parser/v2/textparser.go | 7 +- parser/v2/types.go | 101 +++++++++++++++++- parser/v2/whitespaceparser.go | 16 +++ 25 files changed, 397 insertions(+), 53 deletions(-) create mode 100644 parser/v2/formattestdata/calltemplate_newline_is_preserved.txt create mode 100644 parser/v2/formattestdata/comments_newline_is_preserved.txt create mode 100644 parser/v2/formattestdata/for_loops_newline_is_preserved.txt create mode 100644 parser/v2/formattestdata/if_statement_newline_is_preserved.txt create mode 100644 parser/v2/formattestdata/switch_newline_is_preserved.txt diff --git a/parser/v2/calltemplateparser.go b/parser/v2/calltemplateparser.go index 3e82a2064..70f6a3444 100644 --- a/parser/v2/calltemplateparser.go +++ b/parser/v2/calltemplateparser.go @@ -29,5 +29,10 @@ func (p callTemplateExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e return } + // Parse trailing whitespace. + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + return r, false, err + } + return r, true, nil } diff --git a/parser/v2/calltemplateparser_test.go b/parser/v2/calltemplateparser_test.go index 765d91500..22319114e 100644 --- a/parser/v2/calltemplateparser_test.go +++ b/parser/v2/calltemplateparser_test.go @@ -76,6 +76,28 @@ func TestCallTemplateExpressionParser(t *testing.T) { }, }, }, + { + name: "call: can parse the initial expression and leave the text", + input: `{!Other(p.Test)} Home`, + expected: CallTemplateExpression{ + Expression: Expression{ + Value: "Other(p.Test)", + Range: Range{ + From: Position{ + Index: 2, + Line: 0, + Col: 2, + }, + To: Position{ + Index: 15, + Line: 0, + Col: 15, + }, + }, + }, + TrailingSpace: SpaceHorizontal, + }, + }, } for _, tt := range tests { tt := tt diff --git a/parser/v2/elementparser.go b/parser/v2/elementparser.go index 60290e4c4..12d28f3c5 100644 --- a/parser/v2/elementparser.go +++ b/parser/v2/elementparser.go @@ -467,12 +467,7 @@ func addTrailingSpaceAndValidate(start parse.Position, e Element, pi *parse.Inpu return e, false, err } // Add trailing space. - ws, _, err := parse.Whitespace.Parse(pi) - if err != nil { - return e, false, err - } - e.TrailingSpace, err = NewTrailingSpace(ws, true) - if err != nil { + if _, _, err := addTrailingSpace(&e, pi, true); err != nil { return e, false, err } diff --git a/parser/v2/forexpressionparser.go b/parser/v2/forexpressionparser.go index 3f8bc2ec1..8978291db 100644 --- a/parser/v2/forexpressionparser.go +++ b/parser/v2/forexpressionparser.go @@ -10,7 +10,10 @@ var forExpression parse.Parser[Node] = forExpressionParser{} type forExpressionParser struct{} func (forExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { - var r ForExpression + r := ForExpression{ + // Default behavior is always a trailing space + TrailingSpace: SpaceVertical, + } start := pi.Index() // Strip leading whitespace and look for `for `. @@ -48,5 +51,15 @@ func (forExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { return } + // Parse trailing whitespace. + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + return r, false, err + } + + // If the trailing space is not vertical, set it to vertical. + if r.TrailingSpace != SpaceVertical && r.TrailingSpace != SpaceVerticalDouble { + r.TrailingSpace = SpaceVertical + } + return r, true, nil } diff --git a/parser/v2/forexpressionparser_test.go b/parser/v2/forexpressionparser_test.go index 4a012e7b2..630dc17ad 100644 --- a/parser/v2/forexpressionparser_test.go +++ b/parser/v2/forexpressionparser_test.go @@ -64,6 +64,7 @@ func TestForExpressionParser(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -117,6 +118,7 @@ func TestForExpressionParser(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, } diff --git a/parser/v2/formattestdata/calltemplate_newline_is_preserved.txt b/parser/v2/formattestdata/calltemplate_newline_is_preserved.txt new file mode 100644 index 000000000..d04232fc4 --- /dev/null +++ b/parser/v2/formattestdata/calltemplate_newline_is_preserved.txt @@ -0,0 +1,34 @@ +-- in -- +package main + +templ test() { + + + + {! Other(p.Test) } + {!Other(p.Test)} Home + +
Some standard templ
+ +{!Other(p.Test) } + + + + + +} +-- out -- +package main + +templ test() { + + + @Other(p.Test) + @Other(p.Test) Home +
Some standard templ
+ + @Other(p.Test) + + + +} diff --git a/parser/v2/formattestdata/comments_newline_is_preserved.txt b/parser/v2/formattestdata/comments_newline_is_preserved.txt new file mode 100644 index 000000000..30f7ca4af --- /dev/null +++ b/parser/v2/formattestdata/comments_newline_is_preserved.txt @@ -0,0 +1,31 @@ +-- in -- +package main + +templ test() { + + + // This is not included in the output. +
Some standard templ
+ + + /* This is not included in the output too. */ + /* + Leave this alone. + */ + + +} +-- out -- +package main + +templ test() { + + + // This is not included in the output. +
Some standard templ
+ + /* This is not included in the output too. */ + /* + Leave this alone. + */ +} diff --git a/parser/v2/formattestdata/for_loops_are_placed_on_a_new_line.txt b/parser/v2/formattestdata/for_loops_are_placed_on_a_new_line.txt index 97f7d586f..0ca1de359 100644 --- a/parser/v2/formattestdata/for_loops_are_placed_on_a_new_line.txt +++ b/parser/v2/formattestdata/for_loops_are_placed_on_a_new_line.txt @@ -4,7 +4,7 @@ package test templ input(items []string) {
{ "the" }
{ "other" }
for _, item := range items {
{ item }
-}
+}After closing bracket
} -- out -- package test @@ -16,5 +16,6 @@ templ input(items []string) { for _, item := range items {
{ item }
} + After closing bracket } diff --git a/parser/v2/formattestdata/for_loops_newline_is_preserved.txt b/parser/v2/formattestdata/for_loops_newline_is_preserved.txt new file mode 100644 index 000000000..a0c734b30 --- /dev/null +++ b/parser/v2/formattestdata/for_loops_newline_is_preserved.txt @@ -0,0 +1,33 @@ +-- in -- +package main + +templ x() { +
+ + + for _, item := range items { + +
{ item }
+ + } + + + + Foo Bar +
+} +-- out -- +package main + +templ x() { +
+ + + for _, item := range items { +
{ item }
+ + } + + Foo Bar +
+} diff --git a/parser/v2/formattestdata/if_statement_newline_is_preserved.txt b/parser/v2/formattestdata/if_statement_newline_is_preserved.txt new file mode 100644 index 000000000..fb77235a9 --- /dev/null +++ b/parser/v2/formattestdata/if_statement_newline_is_preserved.txt @@ -0,0 +1,37 @@ +-- in -- +package main + +templ x() { +
+ + + if true { + Test + + } else { + + Test + } + + + + Foo Bar +
+} +-- out -- +package main + +templ x() { +
+ + + if true { + Test + + } else { + Test + } + + Foo Bar +
+} diff --git a/parser/v2/formattestdata/if_statements_are_placed_on_a_new_line.txt b/parser/v2/formattestdata/if_statements_are_placed_on_a_new_line.txt index 77a40ba98..6418fd4cb 100644 --- a/parser/v2/formattestdata/if_statements_are_placed_on_a_new_line.txt +++ b/parser/v2/formattestdata/if_statements_are_placed_on_a_new_line.txt @@ -6,7 +6,7 @@ templ input(items []string) {
{ items[0] }
} else {
{ items[1] }
- } + } After closing brace } -- out -- @@ -21,5 +21,6 @@ templ input(items []string) { } else {
{ items[1] }
} + After closing brace } diff --git a/parser/v2/formattestdata/switch_newline_is_preserved.txt b/parser/v2/formattestdata/switch_newline_is_preserved.txt new file mode 100644 index 000000000..b9ebf5289 --- /dev/null +++ b/parser/v2/formattestdata/switch_newline_is_preserved.txt @@ -0,0 +1,43 @@ +-- in -- +package main + +templ x() { +
+ + + switch items[0] { + + + case "a": +
{ items[0] }
+ + + case "b": +
{ items[1] }
+ + } + + + + Foo Bar +
+} +-- out -- +package main + +templ x() { +
+ + + switch items[0] { + case "a": +
{ items[0] }
+ + case "b": +
{ items[1] }
+ + } + + Foo Bar +
+} diff --git a/parser/v2/formattestdata/switch_statements_are_placed_on_a_new_line.txt b/parser/v2/formattestdata/switch_statements_are_placed_on_a_new_line.txt index bcee5f9cf..2cf814a89 100644 --- a/parser/v2/formattestdata/switch_statements_are_placed_on_a_new_line.txt +++ b/parser/v2/formattestdata/switch_statements_are_placed_on_a_new_line.txt @@ -7,7 +7,7 @@ templ input(items []string) {
{ items[0] }
case "b":
{ items[1] }
-} +}After closing bracket } -- out -- package test @@ -22,5 +22,6 @@ templ input(items []string) { case "b":
{ items[1] }
} + After closing bracket } diff --git a/parser/v2/gocodeparser.go b/parser/v2/gocodeparser.go index 1c0b5c91b..0d536420a 100644 --- a/parser/v2/gocodeparser.go +++ b/parser/v2/gocodeparser.go @@ -32,12 +32,7 @@ var goCode = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - ws, _, err := parse.Whitespace.Parse(pi) - if err != nil { - return r, false, err - } - r.TrailingSpace, err = NewTrailingSpace(ws, true) - if err != nil { + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { return r, false, err } diff --git a/parser/v2/htmlcommentparser.go b/parser/v2/htmlcommentparser.go index 83853d90d..39ecc59b8 100644 --- a/parser/v2/htmlcommentparser.go +++ b/parser/v2/htmlcommentparser.go @@ -35,5 +35,10 @@ func (p htmlCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { return } + // Parse trailing whitespace. + if _, _, err := addTrailingSpace(&c, pi, true); err != nil { + return c, false, err + } + return c, true, nil } diff --git a/parser/v2/ifexpressionparser.go b/parser/v2/ifexpressionparser.go index f8cddb0df..89460fca0 100644 --- a/parser/v2/ifexpressionparser.go +++ b/parser/v2/ifexpressionparser.go @@ -12,7 +12,10 @@ var untilElseIfElseOrEnd = parse.Any(StripType(elseIfExpression), StripType(else type ifExpressionParser struct{} func (ifExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { - var r IfExpression + r := IfExpression{ + // Default behavior is always a trailing space + TrailingSpace: SpaceVertical, + } start := pi.Index() if !peekPrefix(pi, "if ") { @@ -60,6 +63,16 @@ func (ifExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { return } + // Parse trailing whitespace. + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + return r, false, err + } + + // If the trailing space is not vertical, set it to vertical. + if r.TrailingSpace != SpaceVertical && r.TrailingSpace != SpaceVerticalDouble { + r.TrailingSpace = SpaceVertical + } + return r, true, nil } diff --git a/parser/v2/ifexpressionparser_test.go b/parser/v2/ifexpressionparser_test.go index d80300e41..f6712b4cd 100644 --- a/parser/v2/ifexpressionparser_test.go +++ b/parser/v2/ifexpressionparser_test.go @@ -70,6 +70,7 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -136,6 +137,7 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -171,6 +173,7 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -230,6 +233,7 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -296,6 +300,7 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -370,9 +375,10 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, - Whitespace{Value: "\n\t\t\t\t"}, }, + TrailingSpace: SpaceVertical, }, }, { @@ -427,6 +433,7 @@ func TestIfExpression(t *testing.T) { }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -505,6 +512,7 @@ func TestIfExpression(t *testing.T) { }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -597,6 +605,7 @@ func TestIfExpression(t *testing.T) { TrailingSpace: SpaceVertical, }, }, + TrailingSpace: SpaceVertical, }, }, } diff --git a/parser/v2/stringexpressionparser.go b/parser/v2/stringexpressionparser.go index e48aca650..a2e851647 100644 --- a/parser/v2/stringexpressionparser.go +++ b/parser/v2/stringexpressionparser.go @@ -26,12 +26,7 @@ var stringExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err er } // Parse trailing whitespace. - ws, _, err := parse.Whitespace.Parse(pi) - if err != nil { - return r, false, err - } - r.TrailingSpace, err = NewTrailingSpace(ws, false) - if err != nil { + if _, _, err := addTrailingSpace(&r, pi, false); err != nil { return r, false, err } diff --git a/parser/v2/switchexpressionparser.go b/parser/v2/switchexpressionparser.go index f21e4bf8d..99fc15e0f 100644 --- a/parser/v2/switchexpressionparser.go +++ b/parser/v2/switchexpressionparser.go @@ -10,7 +10,10 @@ var switchExpression parse.Parser[Node] = switchExpressionParser{} type switchExpressionParser struct{} func (switchExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { - var r SwitchExpression + r := SwitchExpression{ + // Default behavior is always a trailing space + TrailingSpace: SpaceVertical, + } start := pi.Index() // Check the prefix first. @@ -51,6 +54,16 @@ func (switchExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error return } + // Parse trailing whitespace. + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + return r, false, err + } + + // If the trailing space is not vertical, set it to vertical. + if r.TrailingSpace != SpaceVertical && r.TrailingSpace != SpaceVerticalDouble { + r.TrailingSpace = SpaceVertical + } + return r, true, nil } diff --git a/parser/v2/switchexpressionparser_test.go b/parser/v2/switchexpressionparser_test.go index e17cae717..b90633dd3 100644 --- a/parser/v2/switchexpressionparser_test.go +++ b/parser/v2/switchexpressionparser_test.go @@ -33,6 +33,7 @@ func TestSwitchExpressionParser(t *testing.T) { }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -111,6 +112,7 @@ default: }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -188,6 +190,7 @@ default: }, }, }, + TrailingSpace: SpaceVertical, }, }, { @@ -296,6 +299,7 @@ default: }, }, }, + TrailingSpace: SpaceVertical, }, }, } diff --git a/parser/v2/templateparser_test.go b/parser/v2/templateparser_test.go index 6c814951f..4fa55846d 100644 --- a/parser/v2/templateparser_test.go +++ b/parser/v2/templateparser_test.go @@ -430,9 +430,7 @@ func TestTemplateParser(t *testing.T) { TrailingSpace: SpaceVertical, }, }, - }, - Whitespace{ - Value: "\n", + TrailingSpace: SpaceVertical, }, }, }, @@ -736,10 +734,8 @@ func TestTemplateParser(t *testing.T) { }, Children: []Node{ Whitespace{Value: "\t"}, - HTMLComment{Contents: " Single line "}, - Whitespace{Value: "\n\t"}, - HTMLComment{Contents: " \n\t\tMultiline\n\t"}, - Whitespace{Value: "\n"}, + HTMLComment{Contents: " Single line ", TrailingSpace: SpaceVertical}, + HTMLComment{Contents: " \n\t\tMultiline\n\t", TrailingSpace: SpaceVertical}, }, }, }, diff --git a/parser/v2/templelementparser.go b/parser/v2/templelementparser.go index 51e884977..cfaac0f3f 100644 --- a/parser/v2/templelementparser.go +++ b/parser/v2/templelementparser.go @@ -31,12 +31,7 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e } if !hasOpenBrace { // Parse trailing whitespace after expression. - ws, _, err := parse.Whitespace.Parse(pi) - if err != nil { - return r, false, err - } - r.TrailingSpace, err = NewTrailingSpace(ws, true) - if err != nil { + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { return r, false, err } @@ -61,12 +56,7 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e } // Parse trailing whitespace after closing brace. - ws, _, err := parse.Whitespace.Parse(pi) - if err != nil { - return r, false, err - } - r.TrailingSpace, err = NewTrailingSpace(ws, true) - if err != nil { + if _, _, err := addTrailingSpace(&r, pi, true); err != nil { return r, false, err } diff --git a/parser/v2/textparser.go b/parser/v2/textparser.go index fef81e805..e33621260 100644 --- a/parser/v2/textparser.go +++ b/parser/v2/textparser.go @@ -31,12 +31,7 @@ var textParser = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - ws, _, err := parse.Whitespace.Parse(pi) - if err != nil { - return t, false, err - } - t.TrailingSpace, err = NewTrailingSpace(ws, false) - if err != nil { + if _, _, err := addTrailingSpace(&t, pi, false); err != nil { return t, false, err } diff --git a/parser/v2/types.go b/parser/v2/types.go index 507d93b36..3c1f216eb 100644 --- a/parser/v2/types.go +++ b/parser/v2/types.go @@ -434,6 +434,30 @@ var ( _ WhitespaceTrailer = Element{} _ WhitespaceTrailer = Text{} _ WhitespaceTrailer = StringExpression{} + _ WhitespaceTrailer = TemplElementExpression{} + _ WhitespaceTrailer = GoCode{} + _ WhitespaceTrailer = IfExpression{} + _ WhitespaceTrailer = ForExpression{} + _ WhitespaceTrailer = SwitchExpression{} + _ WhitespaceTrailer = HTMLComment{} + _ WhitespaceTrailer = CallTemplateExpression{} +) + +type TrailingSpaceSetter interface { + SetTrailingSpace(ts TrailingSpace) +} + +var ( + _ TrailingSpaceSetter = &Element{} + _ TrailingSpaceSetter = &Text{} + _ TrailingSpaceSetter = &StringExpression{} + _ TrailingSpaceSetter = &TemplElementExpression{} + _ TrailingSpaceSetter = &GoCode{} + _ TrailingSpaceSetter = &IfExpression{} + _ TrailingSpaceSetter = &ForExpression{} + _ TrailingSpaceSetter = &SwitchExpression{} + _ TrailingSpaceSetter = &HTMLComment{} + _ TrailingSpaceSetter = &CallTemplateExpression{} ) // Text node within the document. @@ -450,6 +474,10 @@ func (t Text) Trailing() TrailingSpace { return t.TrailingSpace } +func (t *Text) SetTrailingSpace(ts TrailingSpace) { + t.TrailingSpace = ts +} + func (t Text) IsNode() bool { return true } func (t Text) Write(w io.Writer, indent int) error { return writeIndent(w, indent, t.Value) @@ -470,6 +498,10 @@ func (e Element) Trailing() TrailingSpace { return e.TrailingSpace } +func (e *Element) SetTrailingSpace(ts TrailingSpace) { + e.TrailingSpace = ts +} + var voidElements = map[string]struct{}{ "area": {}, "base": {}, "br": {}, "col": {}, "command": {}, "embed": {}, "hr": {}, "img": {}, "input": {}, "keygen": {}, "link": {}, "meta": {}, "param": {}, "source": {}, "track": {}, "wbr": {}, } @@ -923,6 +955,16 @@ func (c GoComment) Write(w io.Writer, indent int) error { // HTMLComment. type HTMLComment struct { Contents string + // TrailingSpace lists what happens after the element. + TrailingSpace TrailingSpace +} + +func (c HTMLComment) Trailing() TrailingSpace { + return c.TrailingSpace +} + +func (c *HTMLComment) SetTrailingSpace(ts TrailingSpace) { + c.TrailingSpace = ts } func (c HTMLComment) IsNode() bool { return true } @@ -939,6 +981,16 @@ func (c HTMLComment) Write(w io.Writer, indent int) error { type CallTemplateExpression struct { // Expression returns a template to execute. Expression Expression + // TrailingSpace lists what happens after the expression. + TrailingSpace TrailingSpace +} + +func (cte CallTemplateExpression) Trailing() TrailingSpace { + return cte.TrailingSpace +} + +func (cte *CallTemplateExpression) SetTrailingSpace(ts TrailingSpace) { + cte.TrailingSpace = ts } func (cte CallTemplateExpression) IsNode() bool { return true } @@ -964,6 +1016,10 @@ func (t TemplElementExpression) Trailing() TrailingSpace { return t.TrailingSpace } +func (t *TemplElementExpression) SetTrailingSpace(ts TrailingSpace) { + t.TrailingSpace = ts +} + func (tee TemplElementExpression) ChildNodes() []Node { return tee.Children } @@ -1034,6 +1090,8 @@ type IfExpression struct { Then []Node ElseIfs []ElseIfExpression Else []Node + // TrailingSpace lists what happens after the expression. + TrailingSpace TrailingSpace } type ElseIfExpression struct { @@ -1041,6 +1099,14 @@ type ElseIfExpression struct { Then []Node } +func (n IfExpression) Trailing() TrailingSpace { + return n.TrailingSpace +} + +func (n *IfExpression) SetTrailingSpace(ts TrailingSpace) { + n.TrailingSpace = ts +} + func (n IfExpression) ChildNodes() []Node { var nodes []Node nodes = append(nodes, n.Then...) @@ -1089,7 +1155,17 @@ func (n IfExpression) Write(w io.Writer, indent int) error { // } type SwitchExpression struct { Expression Expression - Cases []CaseExpression + // TrailingSpace lists what happens after the expression. + TrailingSpace TrailingSpace + Cases []CaseExpression +} + +func (se SwitchExpression) Trailing() TrailingSpace { + return se.TrailingSpace +} + +func (se *SwitchExpression) SetTrailingSpace(ts TrailingSpace) { + se.TrailingSpace = ts } func (se SwitchExpression) ChildNodes() []Node { @@ -1132,7 +1208,17 @@ type CaseExpression struct { // } type ForExpression struct { Expression Expression - Children []Node + // TrailingSpace lists what happens after the expression. + TrailingSpace TrailingSpace + Children []Node +} + +func (fe ForExpression) Trailing() TrailingSpace { + return fe.TrailingSpace +} + +func (fe *ForExpression) SetTrailingSpace(ts TrailingSpace) { + fe.TrailingSpace = ts } func (fe ForExpression) ChildNodes() []Node { @@ -1165,6 +1251,10 @@ func (gc GoCode) Trailing() TrailingSpace { return gc.TrailingSpace } +func (gc *GoCode) SetTrailingSpace(ts TrailingSpace) { + gc.TrailingSpace = ts +} + func (gc GoCode) IsNode() bool { return true } func (gc GoCode) Write(w io.Writer, indent int) error { if isWhitespace(gc.Expression.Value) { @@ -1195,7 +1285,12 @@ func (se StringExpression) Trailing() TrailingSpace { return se.TrailingSpace } -func (se StringExpression) IsNode() bool { return true } +func (se *StringExpression) SetTrailingSpace(ts TrailingSpace) { + se.TrailingSpace = ts +} + +func (se StringExpression) IsNode() bool { return true } + func (se StringExpression) IsStyleDeclarationValue() bool { return true } func (se StringExpression) Write(w io.Writer, indent int) error { if isWhitespace(se.Expression.Value) { diff --git a/parser/v2/whitespaceparser.go b/parser/v2/whitespaceparser.go index c57964349..340d79d94 100644 --- a/parser/v2/whitespaceparser.go +++ b/parser/v2/whitespaceparser.go @@ -2,6 +2,22 @@ package parser import "github.com/a-h/parse" +func addTrailingSpace[TNode TrailingSpaceSetter](e TNode, pi *parse.Input, allowMulti bool) (n TNode, ok bool, err error) { + // Add trailing space. + ws, _, err := parse.Whitespace.Parse(pi) + if err != nil { + return e, false, err + } + trailingSpace, err := NewTrailingSpace(ws, allowMulti) + if err != nil { + return e, false, err + } + + e.SetTrailingSpace(trailingSpace) + + return e, true, nil +} + // Eat any whitespace. var whitespaceExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { var r Whitespace From 24ba4f1182beda0be60dbb6cb18141f369dc0daa Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Fri, 20 Sep 2024 21:31:20 +0200 Subject: [PATCH 5/7] fix(fmt): Simplify setting trailing space (#374) --- parser/v2/calltemplateparser.go | 3 +- parser/v2/elementparser.go | 3 +- parser/v2/forexpressionparser.go | 8 +--- parser/v2/gocodeparser.go | 3 +- parser/v2/htmlcommentparser.go | 3 +- parser/v2/ifexpressionparser.go | 8 +--- parser/v2/stringexpressionparser.go | 3 +- parser/v2/switchexpressionparser.go | 8 +--- parser/v2/templelementparser.go | 6 ++- parser/v2/textparser.go | 3 +- parser/v2/types.go | 57 ----------------------------- parser/v2/whitespaceparser.go | 16 +++++--- 12 files changed, 32 insertions(+), 89 deletions(-) diff --git a/parser/v2/calltemplateparser.go b/parser/v2/calltemplateparser.go index 70f6a3444..db4e698cc 100644 --- a/parser/v2/calltemplateparser.go +++ b/parser/v2/calltemplateparser.go @@ -30,7 +30,8 @@ func (p callTemplateExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, false) + if err != nil { return r, false, err } diff --git a/parser/v2/elementparser.go b/parser/v2/elementparser.go index 12d28f3c5..d5796a112 100644 --- a/parser/v2/elementparser.go +++ b/parser/v2/elementparser.go @@ -467,7 +467,8 @@ func addTrailingSpaceAndValidate(start parse.Position, e Element, pi *parse.Inpu return e, false, err } // Add trailing space. - if _, _, err := addTrailingSpace(&e, pi, true); err != nil { + e.TrailingSpace, err = parseTrailingSpace(pi, true, false) + if err != nil { return e, false, err } diff --git a/parser/v2/forexpressionparser.go b/parser/v2/forexpressionparser.go index 8978291db..2e498105c 100644 --- a/parser/v2/forexpressionparser.go +++ b/parser/v2/forexpressionparser.go @@ -52,14 +52,10 @@ func (forExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, true) + if err != nil { return r, false, err } - // If the trailing space is not vertical, set it to vertical. - if r.TrailingSpace != SpaceVertical && r.TrailingSpace != SpaceVerticalDouble { - r.TrailingSpace = SpaceVertical - } - return r, true, nil } diff --git a/parser/v2/gocodeparser.go b/parser/v2/gocodeparser.go index 0d536420a..42c58bd02 100644 --- a/parser/v2/gocodeparser.go +++ b/parser/v2/gocodeparser.go @@ -32,7 +32,8 @@ var goCode = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, false) + if err != nil { return r, false, err } diff --git a/parser/v2/htmlcommentparser.go b/parser/v2/htmlcommentparser.go index 39ecc59b8..462fcde39 100644 --- a/parser/v2/htmlcommentparser.go +++ b/parser/v2/htmlcommentparser.go @@ -36,7 +36,8 @@ func (p htmlCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&c, pi, true); err != nil { + c.TrailingSpace, err = parseTrailingSpace(pi, true, false) + if err != nil { return c, false, err } diff --git a/parser/v2/ifexpressionparser.go b/parser/v2/ifexpressionparser.go index 89460fca0..7462a3757 100644 --- a/parser/v2/ifexpressionparser.go +++ b/parser/v2/ifexpressionparser.go @@ -64,15 +64,11 @@ func (ifExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, true) + if err != nil { return r, false, err } - // If the trailing space is not vertical, set it to vertical. - if r.TrailingSpace != SpaceVertical && r.TrailingSpace != SpaceVerticalDouble { - r.TrailingSpace = SpaceVertical - } - return r, true, nil } diff --git a/parser/v2/stringexpressionparser.go b/parser/v2/stringexpressionparser.go index a2e851647..190453944 100644 --- a/parser/v2/stringexpressionparser.go +++ b/parser/v2/stringexpressionparser.go @@ -26,7 +26,8 @@ var stringExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err er } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&r, pi, false); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, false, false) + if err != nil { return r, false, err } diff --git a/parser/v2/switchexpressionparser.go b/parser/v2/switchexpressionparser.go index 99fc15e0f..539063a67 100644 --- a/parser/v2/switchexpressionparser.go +++ b/parser/v2/switchexpressionparser.go @@ -55,15 +55,11 @@ func (switchExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, true) + if err != nil { return r, false, err } - // If the trailing space is not vertical, set it to vertical. - if r.TrailingSpace != SpaceVertical && r.TrailingSpace != SpaceVerticalDouble { - r.TrailingSpace = SpaceVertical - } - return r, true, nil } diff --git a/parser/v2/templelementparser.go b/parser/v2/templelementparser.go index cfaac0f3f..60adf1d12 100644 --- a/parser/v2/templelementparser.go +++ b/parser/v2/templelementparser.go @@ -31,7 +31,8 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e } if !hasOpenBrace { // Parse trailing whitespace after expression. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, false) + if err != nil { return r, false, err } @@ -56,7 +57,8 @@ func (p templElementExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, e } // Parse trailing whitespace after closing brace. - if _, _, err := addTrailingSpace(&r, pi, true); err != nil { + r.TrailingSpace, err = parseTrailingSpace(pi, true, false) + if err != nil { return r, false, err } diff --git a/parser/v2/textparser.go b/parser/v2/textparser.go index e33621260..50cefda44 100644 --- a/parser/v2/textparser.go +++ b/parser/v2/textparser.go @@ -31,7 +31,8 @@ var textParser = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { } // Parse trailing whitespace. - if _, _, err := addTrailingSpace(&t, pi, false); err != nil { + t.TrailingSpace, err = parseTrailingSpace(pi, false, false) + if err != nil { return t, false, err } diff --git a/parser/v2/types.go b/parser/v2/types.go index 3c1f216eb..6ef2f4379 100644 --- a/parser/v2/types.go +++ b/parser/v2/types.go @@ -443,23 +443,6 @@ var ( _ WhitespaceTrailer = CallTemplateExpression{} ) -type TrailingSpaceSetter interface { - SetTrailingSpace(ts TrailingSpace) -} - -var ( - _ TrailingSpaceSetter = &Element{} - _ TrailingSpaceSetter = &Text{} - _ TrailingSpaceSetter = &StringExpression{} - _ TrailingSpaceSetter = &TemplElementExpression{} - _ TrailingSpaceSetter = &GoCode{} - _ TrailingSpaceSetter = &IfExpression{} - _ TrailingSpaceSetter = &ForExpression{} - _ TrailingSpaceSetter = &SwitchExpression{} - _ TrailingSpaceSetter = &HTMLComment{} - _ TrailingSpaceSetter = &CallTemplateExpression{} -) - // Text node within the document. type Text struct { // Range of the text within the templ file. @@ -474,10 +457,6 @@ func (t Text) Trailing() TrailingSpace { return t.TrailingSpace } -func (t *Text) SetTrailingSpace(ts TrailingSpace) { - t.TrailingSpace = ts -} - func (t Text) IsNode() bool { return true } func (t Text) Write(w io.Writer, indent int) error { return writeIndent(w, indent, t.Value) @@ -498,10 +477,6 @@ func (e Element) Trailing() TrailingSpace { return e.TrailingSpace } -func (e *Element) SetTrailingSpace(ts TrailingSpace) { - e.TrailingSpace = ts -} - var voidElements = map[string]struct{}{ "area": {}, "base": {}, "br": {}, "col": {}, "command": {}, "embed": {}, "hr": {}, "img": {}, "input": {}, "keygen": {}, "link": {}, "meta": {}, "param": {}, "source": {}, "track": {}, "wbr": {}, } @@ -963,10 +938,6 @@ func (c HTMLComment) Trailing() TrailingSpace { return c.TrailingSpace } -func (c *HTMLComment) SetTrailingSpace(ts TrailingSpace) { - c.TrailingSpace = ts -} - func (c HTMLComment) IsNode() bool { return true } func (c HTMLComment) Write(w io.Writer, indent int) error { return writeIndent(w, indent, "") @@ -989,10 +960,6 @@ func (cte CallTemplateExpression) Trailing() TrailingSpace { return cte.TrailingSpace } -func (cte *CallTemplateExpression) SetTrailingSpace(ts TrailingSpace) { - cte.TrailingSpace = ts -} - func (cte CallTemplateExpression) IsNode() bool { return true } func (cte CallTemplateExpression) Write(w io.Writer, indent int) error { // Rewrite to new call syntax @@ -1016,10 +983,6 @@ func (t TemplElementExpression) Trailing() TrailingSpace { return t.TrailingSpace } -func (t *TemplElementExpression) SetTrailingSpace(ts TrailingSpace) { - t.TrailingSpace = ts -} - func (tee TemplElementExpression) ChildNodes() []Node { return tee.Children } @@ -1103,10 +1066,6 @@ func (n IfExpression) Trailing() TrailingSpace { return n.TrailingSpace } -func (n *IfExpression) SetTrailingSpace(ts TrailingSpace) { - n.TrailingSpace = ts -} - func (n IfExpression) ChildNodes() []Node { var nodes []Node nodes = append(nodes, n.Then...) @@ -1164,10 +1123,6 @@ func (se SwitchExpression) Trailing() TrailingSpace { return se.TrailingSpace } -func (se *SwitchExpression) SetTrailingSpace(ts TrailingSpace) { - se.TrailingSpace = ts -} - func (se SwitchExpression) ChildNodes() []Node { var nodes []Node for _, c := range se.Cases { @@ -1217,10 +1172,6 @@ func (fe ForExpression) Trailing() TrailingSpace { return fe.TrailingSpace } -func (fe *ForExpression) SetTrailingSpace(ts TrailingSpace) { - fe.TrailingSpace = ts -} - func (fe ForExpression) ChildNodes() []Node { return fe.Children } @@ -1251,10 +1202,6 @@ func (gc GoCode) Trailing() TrailingSpace { return gc.TrailingSpace } -func (gc *GoCode) SetTrailingSpace(ts TrailingSpace) { - gc.TrailingSpace = ts -} - func (gc GoCode) IsNode() bool { return true } func (gc GoCode) Write(w io.Writer, indent int) error { if isWhitespace(gc.Expression.Value) { @@ -1285,10 +1232,6 @@ func (se StringExpression) Trailing() TrailingSpace { return se.TrailingSpace } -func (se *StringExpression) SetTrailingSpace(ts TrailingSpace) { - se.TrailingSpace = ts -} - func (se StringExpression) IsNode() bool { return true } func (se StringExpression) IsStyleDeclarationValue() bool { return true } diff --git a/parser/v2/whitespaceparser.go b/parser/v2/whitespaceparser.go index 340d79d94..064373b1e 100644 --- a/parser/v2/whitespaceparser.go +++ b/parser/v2/whitespaceparser.go @@ -2,20 +2,24 @@ package parser import "github.com/a-h/parse" -func addTrailingSpace[TNode TrailingSpaceSetter](e TNode, pi *parse.Input, allowMulti bool) (n TNode, ok bool, err error) { +func parseTrailingSpace(pi *parse.Input, allowMulti bool, forceVertical bool) (space TrailingSpace, err error) { // Add trailing space. ws, _, err := parse.Whitespace.Parse(pi) if err != nil { - return e, false, err + return "", err } - trailingSpace, err := NewTrailingSpace(ws, allowMulti) + + trailing, err := NewTrailingSpace(ws, allowMulti) if err != nil { - return e, false, err + return "", err } - e.SetTrailingSpace(trailingSpace) + // If the trailing space is not vertical, set it to vertical. + if forceVertical && trailing != SpaceVertical && trailing != SpaceVerticalDouble { + return SpaceVertical, nil + } - return e, true, nil + return trailing, nil } // Eat any whitespace. From af46dd5e32a6c17ea4c3c0c8061adace28b6cef0 Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Sun, 29 Sep 2024 18:40:13 +0200 Subject: [PATCH 6/7] fix(gen): If, for, switch test whitespace (#374) --- generator/generator.go | 10 ++++++++-- generator/test-import/template_templ.go | 8 -------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/generator/generator.go b/generator/generator.go index 6830d943a..e13b03c2c 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -528,6 +528,8 @@ func (g *generator) writeNodes(indentLevel int, nodes []parser.Node, next parser } func (g *generator) writeNode(indentLevel int, current parser.Node, next parser.Node) (err error) { + maybeWhitespace := true + switch n := current.(type) { case parser.DocType: err = g.writeDocType(indentLevel, n) @@ -540,14 +542,17 @@ func (g *generator) writeNode(indentLevel int, current parser.Node, next parser. case parser.RawElement: err = g.writeRawElement(indentLevel, n) case parser.ForExpression: + maybeWhitespace = false err = g.writeForExpression(indentLevel, n, next) case parser.CallTemplateExpression: err = g.writeCallTemplateExpression(indentLevel, n) case parser.TemplElementExpression: err = g.writeTemplElementExpression(indentLevel, n) case parser.IfExpression: + maybeWhitespace = false err = g.writeIfExpression(indentLevel, n, next) case parser.SwitchExpression: + maybeWhitespace = false err = g.writeSwitchExpression(indentLevel, n, next) case parser.StringExpression: err = g.writeStringExpression(indentLevel, n.Expression) @@ -566,8 +571,9 @@ func (g *generator) writeNode(indentLevel int, current parser.Node, next parser. // Write trailing whitespace, if there is a next node that might need the space. // If the next node is inline or text, we might need it. // If the current node is a block element, we don't need it. - needed := (isInlineOrText(current) && isInlineOrText(next)) - if ws, ok := current.(parser.WhitespaceTrailer); ok && needed { + // If, switch and for as current node skip whitespace, but not always when next node. + neededWhitespace := maybeWhitespace && isInlineOrText(current) && isInlineOrText(next) + if ws, ok := current.(parser.WhitespaceTrailer); ok && neededWhitespace { if err := g.writeWhitespaceTrailer(indentLevel, ws.Trailing()); err != nil { return err } diff --git a/generator/test-import/template_templ.go b/generator/test-import/template_templ.go index 0dde67bee..a6144059a 100644 --- a/generator/test-import/template_templ.go +++ b/generator/test-import/template_templ.go @@ -136,10 +136,6 @@ func main() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -162,10 +158,6 @@ func main() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") - if templ_7745c5c3_Err != nil { - return templ_7745c5c3_Err - } templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) From 5e7bd2e8fdf6f7858a2706a6b4e0ee4fb20cefe7 Mon Sep 17 00:00:00 2001 From: Alexander Arvidsson <2972103+AlexanderArvidsson@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:59:41 +0200 Subject: [PATCH 7/7] fix(gen): TemplElementExpression whitespace test (#374) --- generator/generator.go | 12 +++++++++++- generator/test-import/template_templ.go | 8 ++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/generator/generator.go b/generator/generator.go index e13b03c2c..f2f2ded27 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -529,6 +529,7 @@ func (g *generator) writeNodes(indentLevel int, nodes []parser.Node, next parser func (g *generator) writeNode(indentLevel int, current parser.Node, next parser.Node) (err error) { maybeWhitespace := true + forceWhitespace := false switch n := current.(type) { case parser.DocType: @@ -548,6 +549,14 @@ func (g *generator) writeNode(indentLevel int, current parser.Node, next parser. err = g.writeCallTemplateExpression(indentLevel, n) case parser.TemplElementExpression: err = g.writeTemplElementExpression(indentLevel, n) + + // TemplElementExpression with block should always have whitespace if the next element is also + // a TemplElementExpression + if len(n.Children) > 0 { + if _, ok := next.(parser.TemplElementExpression); ok { + forceWhitespace = true + } + } case parser.IfExpression: maybeWhitespace = false err = g.writeIfExpression(indentLevel, n, next) @@ -572,7 +581,8 @@ func (g *generator) writeNode(indentLevel int, current parser.Node, next parser. // If the next node is inline or text, we might need it. // If the current node is a block element, we don't need it. // If, switch and for as current node skip whitespace, but not always when next node. - neededWhitespace := maybeWhitespace && isInlineOrText(current) && isInlineOrText(next) + neededWhitespace := forceWhitespace || (maybeWhitespace && isInlineOrText(current) && isInlineOrText(next)) + if ws, ok := current.(parser.WhitespaceTrailer); ok && neededWhitespace { if err := g.writeWhitespaceTrailer(indentLevel, ws.Trailing()); err != nil { return err diff --git a/generator/test-import/template_templ.go b/generator/test-import/template_templ.go index a6144059a..0dde67bee 100644 --- a/generator/test-import/template_templ.go +++ b/generator/test-import/template_templ.go @@ -136,6 +136,10 @@ func main() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } templ_7745c5c3_Var6 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) @@ -158,6 +162,10 @@ func main() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } templ_7745c5c3_Var7 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)