Repository: a-h/templ
Pull request #767 (2024-05-30 15:31:54)

fix: format single line raw go code


Patch:
diff --git a/cmd/templ/generatecmd/proxy/proxy.go b/cmd/templ/generatecmd/proxy/proxy.go
index 9b942be..a129888 100644
--- a/cmd/templ/generatecmd/proxy/proxy.go
+++ b/cmd/templ/generatecmd/proxy/proxy.go
@@ -16,6 +16,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/PuerkitoBio/goquery"
 	"github.com/a-h/templ/cmd/templ/generatecmd/sse"
 	"github.com/andybalholm/brotli"
 
@@ -36,7 +37,16 @@ type Handler struct {
 }
 
 func insertScriptTagIntoBody(body string) (updated string) {
-	return strings.Replace(body, "</body>", scriptTag+"</body>", -1)
+	doc, err := goquery.NewDocumentFromReader(strings.NewReader(body))
+	if err != nil {
+		return strings.Replace(body, "</body>", scriptTag+"</body>", -1)
+	}
+	doc.Find("body").AppendHtml(scriptTag)
+	r, err := doc.Html()
+	if err != nil {
+		return strings.Replace(body, "</body>", scriptTag+"</body>", -1)
+	}
+	return r
 }
 
 type passthroughWriteCloser struct {
diff --git a/docs/docs/09-commands-and-tools/02-ide-support.md b/docs/docs/09-commands-and-tools/02-ide-support.md
index 639c9bf..6606532 100644
--- a/docs/docs/09-commands-and-tools/02-ide-support.md
+++ b/docs/docs/09-commands-and-tools/02-ide-support.md
@@ -322,6 +322,15 @@ require'nvim-treesitter.configs'.setup {
 }
 ```
 
+## Vim
+
+Currently support for vim is limited. Configure formatting with the following VimScript:
+
+```vim
+set autoread
+autocmd BufWritePost *.templ silent! execute "!PATH=\"$PATH:$(go env GOPATH)/bin\" templ fmt <afile> >/dev/null 2>&1" | redraw!
+```
+
 ## Helix
 
 https://helix-editor.com/
diff --git a/parser/v2/types.go b/parser/v2/types.go
index 12c305c..02a437d 100644
--- a/parser/v2/types.go
+++ b/parser/v2/types.go
@@ -1136,14 +1136,14 @@ func (gc GoCode) Write(w io.Writer, indent int) error {
 	if isWhitespace(gc.Expression.Value) {
 		gc.Expression.Value = ""
 	}
-	if !gc.Multiline {
-		return writeIndent(w, indent, `{{ `, gc.Expression.Value, ` }}`)
-	}
-	formatted, err := format.Source([]byte(gc.Expression.Value))
+	source, err := format.Source([]byte(gc.Expression.Value))
 	if err != nil {
-		return err
+		source = []byte(gc.Expression.Value)
+	}
+	if !gc.Multiline {
+		return writeIndent(w, indent, `{{ `, string(source), ` }}`)
 	}
-	if err := writeIndent(w, indent, "{{"+string(formatted)+"\n"); err != nil {
+	if err := writeIndent(w, indent, "{{"+string(source)+"\n"); err != nil {
 		return err
 	}
 	return writeIndent(w, indent, "}}")
diff --git a/runtime.go b/runtime.go
index 1a18e90..9f6df50 100644
--- a/runtime.go
+++ b/runtime.go
@@ -233,6 +233,10 @@ func (cp *cssProcessor) Add(item any) {
 		for _, item := range c {
 			cp.Add(item)
 		}
+	case []CSSClass:
+		for _, item := range c {
+			cp.Add(item)
+		}
 	case func() CSSClass:
 		cp.AddClassName(c().ClassName(), true)
 	default:
@@ -438,6 +442,10 @@ func renderCSSItemsToBuilder(sb *strings.Builder, v *contextValue, classes ...an
 			renderCSSItemsToBuilder(sb, v, ccc.Key)
 		case CSSClasses:
 			renderCSSItemsToBuilder(sb, v, ccc...)
+		case []CSSClass:
+			for _, item := range ccc {
+				renderCSSItemsToBuilder(sb, v, item)
+			}
 		case func() CSSClass:
 			renderCSSItemsToBuilder(sb, v, ccc())
 		case []string:


Test patch:
diff --git a/cmd/templ/generatecmd/proxy/proxy_test.go b/cmd/templ/generatecmd/proxy/proxy_test.go
index b33478f..d2bc915 100644
--- a/cmd/templ/generatecmd/proxy/proxy_test.go
+++ b/cmd/templ/generatecmd/proxy/proxy_test.go
@@ -161,6 +161,49 @@ func TestProxy(t *testing.T) {
 			t.Errorf("unexpected response body (-got +want):\n%s", diff)
 		}
 	})
+	t.Run("plain: body tags get the script inserted ignoring js with body tags", func(t *testing.T) {
+		// Arrange
+		r := &http.Response{
+			Body:   io.NopCloser(strings.NewReader(`<html><body><script>console.log("<body></body>")</script></body></html>`)),
+			Header: make(http.Header),
+			Request: &http.Request{
+				URL: &url.URL{
+					Scheme: "http",
+					Host:   "example.com",
+				},
+			},
+		}
+		r.Header.Set("Content-Type", "text/html, charset=utf-8")
+		r.Header.Set("Content-Length", "26")
+
+		expectedString := insertScriptTagIntoBody(`<html><body><script>console.log("<body></body>")</script></body></html>`)
+		if !strings.Contains(expectedString, scriptTag) {
+			t.Fatalf("expected the script tag to be inserted, but it wasn't: %q", expectedString)
+		}
+		if !strings.Contains(expectedString, `console.log("<body></body>")`) {
+			t.Fatalf("expected the script tag to be inserted, but mangled the html: %q", expectedString)
+		}
+
+		// Act
+		log := slog.New(slog.NewJSONHandler(io.Discard, nil))
+		h := New(log, "127.0.0.1", 7474, &url.URL{Scheme: "http", Host: "example.com"})
+		err := h.modifyResponse(r)
+		if err != nil {
+			t.Fatalf("unexpected error: %v", err)
+		}
+
+		// Assert
+		if r.Header.Get("Content-Length") != fmt.Sprintf("%d", len(expectedString)) {
+			t.Errorf("expected content length to be %d, got %v", len(expectedString), r.Header.Get("Content-Length"))
+		}
+		actualBody, err := io.ReadAll(r.Body)
+		if err != nil {
+			t.Fatalf("unexpected error reading response: %v", err)
+		}
+		if diff := cmp.Diff(expectedString, string(actualBody)); diff != "" {
+			t.Errorf("unexpected response body (-got +want):\n%s", diff)
+		}
+	})
 	t.Run("gzip: non-html content is not modified", func(t *testing.T) {
 		// Arrange
 		r := &http.Response{
diff --git a/parser/v2/format_test.go b/parser/v2/format_test.go
index 3c6ee1e..bd96a3e 100644
--- a/parser/v2/format_test.go
+++ b/parser/v2/format_test.go
@@ -5,11 +5,17 @@ import (
 	"path/filepath"
 	"testing"
 
+	"github.com/a-h/templ/cfg"
 	"github.com/google/go-cmp/cmp"
 	"golang.org/x/tools/txtar"
 )
 
 func TestFormatting(t *testing.T) {
+	oldFlags := cfg.Experiment
+	t.Cleanup(func() {
+		cfg.Experiment = oldFlags
+	})
+	cfg.Experiment.RawGo = true
 	files, _ := filepath.Glob("formattestdata/*.txt")
 	if len(files) == 0 {
 		t.Errorf("no test files found")
diff --git a/parser/v2/formattestdata/raw_go_is_formatted.txt b/parser/v2/formattestdata/raw_go_is_formatted.txt
new file mode 100644
index 0000000..3209bd6
--- /dev/null
+++ b/parser/v2/formattestdata/raw_go_is_formatted.txt
@@ -0,0 +1,12 @@
+-- in --
+package test
+
+templ nameList(items []Item) {
+	{{ first  := items[0] }}
+}
+-- out --
+package test
+
+templ nameList(items []Item) {
+	{{ first := items[0] }}
+}
diff --git a/runtime_test.go b/runtime_test.go
index d481acf..8fcd1f8 100644
--- a/runtime_test.go
+++ b/runtime_test.go
@@ -145,6 +145,7 @@ var cssInputs = []any{
 	func() templ.CSSClass { return templ.ConstantCSSClass("l") },              // func() CSSClass
 	templ.CSSClass(templ.ConstantCSSClass("m")),                               // CSSClass
 	customClass{name: "n"},                                                    // CSSClass
+	[]templ.CSSClass{customClass{name: "n"}},                                  // []CSSClass
 	templ.KV[templ.ConstantCSSClass, bool](templ.ConstantCSSClass("o"), true), // KeyValue[ConstantCSSClass, bool]
 	[]templ.KeyValue[templ.ConstantCSSClass, bool]{
 		templ.KV(templ.ConstantCSSClass("p"), true),