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, ""); err != nil { + if err := writeIndent(w, indent, ""); 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, ">"); err != nil { + if err := writeIndent(w, closeAngleBracketIndent, ">"); 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) { + +} `, }, {