diff --git a/parser/v2/types.go b/parser/v2/types.go
index 9dd2f054a..38eedefde 100644
--- a/parser/v2/types.go
+++ b/parser/v2/types.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"errors"
"fmt"
"go/format"
@@ -155,16 +156,21 @@ func (exp GoExpression) Write(w io.Writer, indent int) error {
if err != nil {
return writeIndent(w, indent, exp.Expression.Value)
}
-
_, err = w.Write(data)
return err
}
-func writeIndent(w io.Writer, level int, s string) (err error) {
- if _, err = w.Write([]byte(strings.Repeat("\t", level))); err != nil {
- return
+func writeIndent(w io.Writer, level int, s ...string) (err error) {
+ indent := strings.Repeat("\t", level)
+ if _, err = io.WriteString(w, indent); err != nil {
+ return err
+ }
+ for _, ss := range s {
+ _, err = io.WriteString(w, ss)
+ if err != nil {
+ return
+ }
}
- _, err = w.Write([]byte(s))
return
}
@@ -216,7 +222,7 @@ type CSSTemplate struct {
func (css CSSTemplate) IsTemplateFileNode() bool { return true }
func (css CSSTemplate) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "css "+css.Name.Value+"() {\n"); err != nil {
+ if err := writeIndent(w, indent, "css ", css.Name.Value, "() {\n"); err != nil {
return err
}
for _, p := range css.Properties {
@@ -273,7 +279,7 @@ type ExpressionCSSProperty struct {
func (c ExpressionCSSProperty) IsCSSProperty() bool { return true }
func (c ExpressionCSSProperty) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, c.Name+": "); err != nil {
+ if err := writeIndent(w, indent, c.Name, ": "); err != nil {
return err
}
if err := c.Value.Write(w, 0); err != nil {
@@ -292,7 +298,7 @@ type DocType struct {
func (dt DocType) IsNode() bool { return true }
func (dt DocType) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, "")
+ return writeIndent(w, indent, "")
}
// HTMLTemplate definition.
@@ -310,7 +316,7 @@ type HTMLTemplate struct {
func (t HTMLTemplate) IsTemplateFileNode() bool { return true }
func (t HTMLTemplate) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "templ "+t.Expression.Value+" {\n"); err != nil {
+ if err := writeIndent(w, indent, "templ ", t.Expression.Value, " {\n"); err != nil {
return err
}
if err := writeNodesIndented(w, indent+1, t.Children); err != nil {
@@ -462,7 +468,7 @@ func containsNonTextNodes(nodes []Node) bool {
func (e Element) IsNode() bool { return true }
func (e Element) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "<"+e.Name); err != nil {
+ if err := writeIndent(w, indent, "<", e.Name); err != nil {
return err
}
for i := 0; i < len(e.Attributes); i++ {
@@ -497,7 +503,7 @@ func (e Element) Write(w io.Writer, indent int) error {
if err := writeNodesIndented(w, indent+1, e.Children); err != nil {
return err
}
- if err := writeIndent(w, indent, ""+e.Name+">"); err != nil {
+ if err := writeIndent(w, indent, "", e.Name, ">"); err != nil {
return err
}
return nil
@@ -519,7 +525,7 @@ func (e Element) Write(w io.Writer, indent int) error {
}
return nil
}
- if err := writeIndent(w, closeAngleBracketIndent, ">"+e.Name+">"); err != nil {
+ if err := writeIndent(w, closeAngleBracketIndent, ">", e.Name, ">"); err != nil {
return err
}
return nil
@@ -607,7 +613,7 @@ type RawElement struct {
func (e RawElement) IsNode() bool { return true }
func (e RawElement) Write(w io.Writer, indent int) error {
// Start.
- if err := writeIndent(w, indent, "<"+e.Name); err != nil {
+ if err := writeIndent(w, indent, "<", e.Name); err != nil {
return err
}
for i := 0; i < len(e.Attributes); i++ {
@@ -687,11 +693,55 @@ type ExpressionAttribute struct {
}
func (ea ExpressionAttribute) String() string {
- return ea.Name + `={ ` + ea.Expression.Value + ` }`
+ sb := new(strings.Builder)
+ ea.Write(sb, 0)
+ return sb.String()
}
-func (ea ExpressionAttribute) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, ea.String())
+func (ea ExpressionAttribute) formatExpression() (exp []string) {
+ trimmed := strings.TrimSpace(ea.Expression.Value)
+ if !strings.Contains(trimmed, "\n") {
+ formatted, err := format.Source([]byte(trimmed))
+ if err != nil {
+ return []string{trimmed}
+ }
+ return []string{string(formatted)}
+ }
+
+ buf := bytes.NewBufferString("[]any{\n")
+ buf.WriteString(trimmed)
+ buf.WriteString("\n}")
+
+ formatted, err := format.Source(buf.Bytes())
+ if err != nil {
+ return []string{trimmed}
+ }
+
+ // Trim prefix and suffix.
+ lines := strings.Split(string(formatted), "\n")
+ if len(lines) < 3 {
+ return []string{trimmed}
+ }
+
+ // Return.
+ return lines[1 : len(lines)-1]
+}
+
+func (ea ExpressionAttribute) Write(w io.Writer, indent int) (err error) {
+ lines := ea.formatExpression()
+ if len(lines) == 1 {
+ return writeIndent(w, indent, ea.Name, `={ `, lines[0], ` }`)
+ }
+
+ if err = writeIndent(w, indent, ea.Name, "={\n"); err != nil {
+ return err
+ }
+ for _, line := range lines {
+ if err = writeIndent(w, indent, line, "\n"); err != nil {
+ return err
+ }
+ }
+ return writeIndent(w, indent, "}")
}
// ")
+ return writeIndent(w, indent, "")
}
// Nodes.
@@ -797,7 +847,7 @@ type CallTemplateExpression struct {
func (cte CallTemplateExpression) IsNode() bool { return true }
func (cte CallTemplateExpression) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, `{! `+cte.Expression.Value+` }`)
+ return writeIndent(w, indent, `{! `, cte.Expression.Value, ` }`)
}
// TemplElementExpression can be used to create and render a template using data.
@@ -856,7 +906,7 @@ type ElseIfExpression struct {
func (n IfExpression) IsNode() bool { return true }
func (n IfExpression) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "if "+n.Expression.Value+" {\n"); err != nil {
+ if err := writeIndent(w, indent, "if ", n.Expression.Value, " {\n"); err != nil {
return err
}
indent++
@@ -865,7 +915,7 @@ func (n IfExpression) Write(w io.Writer, indent int) error {
}
indent--
for _, elseIf := range n.ElseIfs {
- if err := writeIndent(w, indent, "} else if "+elseIf.Expression.Value+" {\n"); err != nil {
+ if err := writeIndent(w, indent, "} else if ", elseIf.Expression.Value, " {\n"); err != nil {
return err
}
indent++
@@ -898,13 +948,13 @@ type SwitchExpression struct {
func (se SwitchExpression) IsNode() bool { return true }
func (se SwitchExpression) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "switch "+se.Expression.Value+" {\n"); err != nil {
+ if err := writeIndent(w, indent, "switch ", se.Expression.Value, " {\n"); err != nil {
return err
}
indent++
for i := 0; i < len(se.Cases); i++ {
c := se.Cases[i]
- if err := writeIndent(w, indent, c.Expression.Value+"\n"); err != nil {
+ if err := writeIndent(w, indent, c.Expression.Value, "\n"); err != nil {
return err
}
if err := writeNodesIndented(w, indent+1, c.Children); err != nil {
@@ -934,7 +984,7 @@ type ForExpression struct {
func (fe ForExpression) IsNode() bool { return true }
func (fe ForExpression) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "for "+fe.Expression.Value+" {\n"); err != nil {
+ if err := writeIndent(w, indent, "for ", fe.Expression.Value, " {\n"); err != nil {
return err
}
if err := writeNodesIndented(w, indent+1, fe.Children); err != nil {
@@ -961,7 +1011,7 @@ func (se StringExpression) Trailing() TrailingSpace {
func (se StringExpression) IsNode() bool { return true }
func (se StringExpression) IsStyleDeclarationValue() bool { return true }
func (se StringExpression) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, `{ `+se.Expression.Value+` }`)
+ return writeIndent(w, indent, `{ `, se.Expression.Value, ` }`)
}
// ScriptTemplate is a script block.
@@ -973,7 +1023,7 @@ type ScriptTemplate struct {
func (s ScriptTemplate) IsTemplateFileNode() bool { return true }
func (s ScriptTemplate) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "script "+s.Name.Value+"("+s.Parameters.Value+") {\n"); err != nil {
+ if err := writeIndent(w, indent, "script ", s.Name.Value, "(", s.Parameters.Value, ") {\n"); err != nil {
return err
}
if _, err := io.WriteString(w, s.Value); err != nil {
diff --git a/parser/v2/types_test.go b/parser/v2/types_test.go
index 1b6f17ce0..85af99c12 100644
--- a/parser/v2/types_test.go
+++ b/parser/v2/types_test.go
@@ -539,6 +539,45 @@ templ x() {
}
+`,
+ },
+ {
+ name: "templ expression attribute are formatted correctly when multiline",
+ input: ` // first line removed to make indentation clear
+package main
+
+templ x(id string, class string) {
+
+}
+`,
+ expected: ` // first line removed to make indentation clear
+package main
+
+templ x(id string, class string) {
+
+}
`,
},
{