diff --git a/.version b/.version
index bceeec9b3..a7990c86c 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-0.2.648
\ No newline at end of file
+0.2.651
\ No newline at end of file
diff --git a/cmd/templ/fmtcmd/main.go b/cmd/templ/fmtcmd/main.go
index e460f2e02..98dcb1b55 100644
--- a/cmd/templ/fmtcmd/main.go
+++ b/cmd/templ/fmtcmd/main.go
@@ -144,7 +144,8 @@ func format(write writer, read reader) (err error) {
return err
}
w := new(bytes.Buffer)
- if err = t.Write(w); err != nil {
+ cw := parser.NewContextWriter(w, parser.WriteContextAll)
+ if err = t.Write(cw); err != nil {
return fmt.Errorf("formatting error: %w", err)
}
return write(fileName, w.String())
diff --git a/cmd/templ/lspcmd/proxy/server.go b/cmd/templ/lspcmd/proxy/server.go
index 662b689e2..72daf66ed 100644
--- a/cmd/templ/lspcmd/proxy/server.go
+++ b/cmd/templ/lspcmd/proxy/server.go
@@ -734,7 +734,8 @@ func (p *Server) Formatting(ctx context.Context, params *lsp.DocumentFormattingP
return
}
w := new(strings.Builder)
- err = template.Write(w)
+ cw := parser.NewContextWriter(w, parser.WriteContextAll)
+ err = template.Write(cw)
if err != nil {
p.Log.Error("handleFormatting: faled to write template", zap.Error(err))
return
diff --git a/cmd/templ/migratecmd/main.go b/cmd/templ/migratecmd/main.go
index bf6e9fbb3..21a5d2de5 100644
--- a/cmd/templ/migratecmd/main.go
+++ b/cmd/templ/migratecmd/main.go
@@ -94,7 +94,8 @@ func migrate(fileName string) (err error) {
// Write the updated file.
w := new(bytes.Buffer)
- err = v2Template.Write(w)
+ cw := v2.NewContextWriter(w, v2.WriteContextAll)
+ err = v2Template.Write(cw)
if err != nil {
return fmt.Errorf("%s formatting error: %w", fileName, err)
}
diff --git a/parser/v2/cssparser_test.go b/parser/v2/cssparser_test.go
index 851608ff3..c241bbeda 100644
--- a/parser/v2/cssparser_test.go
+++ b/parser/v2/cssparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"testing"
"github.com/a-h/parse"
@@ -9,13 +10,15 @@ import (
func TestExpressionCSSPropertyParser(t *testing.T) {
tests := []struct {
- name string
- input string
- expected ExpressionCSSProperty
+ name string
+ input string
+ expected ExpressionCSSProperty
+ expectedCSS string
}{
{
- name: "css: single constant property",
- input: `background-color: { constants.BackgroundColor };`,
+ name: "css: single constant property",
+ input: `background-color: { constants.BackgroundColor };`,
+ expectedCSS: "background-color: ' ';\n",
expected: ExpressionCSSProperty{
Name: "background-color",
Value: StringExpression{
@@ -38,8 +41,9 @@ func TestExpressionCSSPropertyParser(t *testing.T) {
},
},
{
- name: "css: single constant property with windows newlines",
- input: "background-color:\r\n{ constants.BackgroundColor };\r\n",
+ name: "css: single constant property with windows newlines",
+ input: "background-color:\r\n{ constants.BackgroundColor };\r\n",
+ expectedCSS: "background-color: ' ';\n",
expected: ExpressionCSSProperty{
Name: "background-color",
Value: StringExpression{
@@ -76,27 +80,47 @@ func TestExpressionCSSPropertyParser(t *testing.T) {
if diff := cmp.Diff(tt.expected, result); diff != "" {
t.Errorf(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextCSS)
+ if err := result.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualCSS := w.String()
+ if diff := cmp.Diff(tt.expectedCSS, actualCSS); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedCSS))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualCSS))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualCSS)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
func TestConstantCSSPropertyParser(t *testing.T) {
tests := []struct {
- name string
- input string
- expected ConstantCSSProperty
+ name string
+ input string
+ expected ConstantCSSProperty
+ expectedCSS string
}{
{
- name: "css: single constant property",
- input: `background-color: #ffffff;`,
+ name: "css: single constant property",
+ input: `background-color: #ffffff;`,
+ expectedCSS: "background-color: #ffffff;\n",
expected: ConstantCSSProperty{
Name: "background-color",
Value: "#ffffff",
},
},
{
- name: "css: single constant webkit property",
- input: `-webkit-text-stroke-color: #ffffff;`,
+ name: "css: single constant webkit property",
+ input: `-webkit-text-stroke-color: #ffffff;`,
+ expectedCSS: "-webkit-text-stroke-color: #ffffff;\n",
expected: ConstantCSSProperty{
Name: "-webkit-text-stroke-color",
Value: "#ffffff",
@@ -117,20 +141,40 @@ func TestConstantCSSPropertyParser(t *testing.T) {
if diff := cmp.Diff(tt.expected, result); diff != "" {
t.Errorf(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextCSS)
+ if err := result.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualCSS := w.String()
+ if diff := cmp.Diff(tt.expectedCSS, actualCSS); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedCSS))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualCSS))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualCSS)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
func TestCSSParser(t *testing.T) {
tests := []struct {
- name string
- input string
- expected CSSTemplate
+ name string
+ input string
+ expected CSSTemplate
+ expectedCSS string
}{
{
name: "css: no parameters, no content",
input: `css Name() {
}`,
+ expectedCSS: `
+ `,
expected: CSSTemplate{
Name: "Name",
Expression: Expression{
@@ -155,6 +199,8 @@ func TestCSSParser(t *testing.T) {
name: "css: without spaces",
input: `css Name() {
}`,
+ expectedCSS: `
+ `,
expected: CSSTemplate{
Name: "Name",
Expression: Expression{
@@ -180,6 +226,9 @@ func TestCSSParser(t *testing.T) {
input: `css Name() {
background-color: #ffffff;
}`,
+ expectedCSS: `
+ background-color: #ffffff;
+ `,
expected: CSSTemplate{
Name: "Name",
Expression: Expression{
@@ -210,6 +259,9 @@ background-color: #ffffff;
input: `css Name() {
background-color: { constants.BackgroundColor };
}`,
+ expectedCSS: `
+ background-color: ' ';
+ `,
expected: CSSTemplate{
Name: "Name",
Expression: Expression{
@@ -256,6 +308,9 @@ background-color: { constants.BackgroundColor };
input: `css Name(prop string) {
background-color: { prop };
}`,
+ expectedCSS: `
+ background-color: ' ';
+ `,
expected: CSSTemplate{
Name: "Name",
Expression: Expression{
@@ -312,6 +367,23 @@ background-color: { prop };
if diff := cmp.Diff(tt.expected, result); diff != "" {
t.Errorf(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextCSS)
+ if err := result.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualCSS := w.String()
+ if diff := cmp.Diff(tt.expectedCSS, actualCSS); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedCSS))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualCSS))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualCSS)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
diff --git a/parser/v2/elementparser_test.go b/parser/v2/elementparser_test.go
index 6bb695aa3..a54d9fb50 100644
--- a/parser/v2/elementparser_test.go
+++ b/parser/v2/elementparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"strings"
"testing"
@@ -552,13 +553,15 @@ if test {
func TestElementParser(t *testing.T) {
tests := []struct {
- name string
- input string
- expected Element
+ name string
+ input string
+ expected Element
+ expectedHTML string
}{
{
- name: "element: self-closing with single constant attribute",
- input: ``,
+ name: "element: self-closing with single constant attribute",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -578,8 +581,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with single bool expression attribute",
- input: `
`,
+ name: "element: self-closing with single bool expression attribute",
+ input: `
`,
+ expectedHTML: `
`,
expected: Element{
Name: "hr",
NameRange: Range{
@@ -613,8 +617,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with single expression attribute",
- input: ``,
+ name: "element: self-closing with single expression attribute",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -648,8 +653,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with multiple constant attributes",
- input: ``,
+ name: "element: self-closing with multiple constant attributes",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -677,8 +683,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with multiple spreads attributes",
- input: ``,
+ name: "element: self-closing with multiple spreads attributes",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -724,8 +731,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with multiple boolean attributes",
- input: `
`,
+ name: "element: self-closing with multiple boolean attributes",
+ input: `
`,
+ expectedHTML: `
`,
expected: Element{
Name: "hr",
NameRange: Range{
@@ -774,8 +782,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with multiple constant and expr attributes",
- input: ``,
+ name: "element: self-closing with multiple constant and expr attributes",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -831,9 +840,13 @@ func TestElementParser(t *testing.T) {
class="important"
}
>Test
-}
-
`,
+ expectedHTML: `Test
`,
expected: Element{
Name: "div",
NameRange: Range{
@@ -887,8 +900,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with no attributes",
- input: `
`,
+ name: "element: self-closing with no attributes",
+ input: `
`,
+ expectedHTML: `
`,
expected: Element{
Name: "hr",
NameRange: Range{
@@ -898,8 +912,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: self-closing with attribute",
- input: `
`,
+ name: "element: self-closing with attribute",
+ input: `
`,
+ expectedHTML: `
`,
expected: Element{
Name: "hr",
NameRange: Range{
@@ -924,6 +939,12 @@ func TestElementParser(t *testing.T) {
if true {
class="itIsTrue"
}
+/>`,
+ expectedHTML: `
`,
expected: Element{
Name: "hr",
@@ -979,6 +1000,14 @@ func TestElementParser(t *testing.T) {
} else {
class="itIsNotTrue"
}
+/>`,
+ expectedHTML: `
`,
expected: Element{
Name: "hr",
@@ -1042,6 +1071,12 @@ func TestElementParser(t *testing.T) {
if true {
class="itIsTrue"
}
+>Test`,
+ expectedHTML: `Test
`,
expected: Element{
Name: "p",
@@ -1093,8 +1128,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: open and close",
- input: ``,
+ name: "element: open and close",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -1104,8 +1140,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: open and close with text",
- input: `The text`,
+ name: "element: open and close with text",
+ input: `The text`,
+ expectedHTML: `The text`,
expected: Element{
Name: "a",
NameRange: Range{
@@ -1120,8 +1157,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: with self-closing child element",
- input: ``,
+ name: "element: with self-closing child element",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -1140,8 +1178,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: with non-self-closing child element",
- input: ``,
+ name: "element: with non-self-closing child element",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -1160,8 +1199,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: containing space",
- input: ` `,
+ name: "element: containing space",
+ input: ` `,
+ expectedHTML: ` `,
expected: Element{
Name: "a",
NameRange: Range{
@@ -1186,8 +1226,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: with multiple child elements",
- input: ``,
+ name: "element: with multiple child elements",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "a",
NameRange: Range{
@@ -1222,8 +1263,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: empty",
- input: ``,
+ name: "element: empty",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "div",
NameRange: Range{
@@ -1233,8 +1275,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: containing string expression",
- input: `{ "test" }
`,
+ name: "element: containing string expression",
+ input: `{ "test" }
`,
+ expectedHTML: `
`,
expected: Element{
Name: "div",
NameRange: Range{
@@ -1263,8 +1306,9 @@ func TestElementParser(t *testing.T) {
},
},
{
- name: "element: inputs can contain class attributes",
- input: ``,
+ name: "element: inputs can contain class attributes",
+ input: ``,
+ expectedHTML: ``,
expected: Element{
Name: "input",
NameRange: Range{
@@ -1344,6 +1388,11 @@ func TestElementParser(t *testing.T) {
id="email"
name="email"
>`,
+ expectedHTML: ``,
expected: Element{
Name: "input",
IndentAttrs: true,
@@ -1394,6 +1443,23 @@ func TestElementParser(t *testing.T) {
if diff := cmp.Diff(tt.expected, result); diff != "" {
t.Errorf(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextHTML)
+ if err := result.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualHTML := w.String()
+ if diff := cmp.Diff(tt.expectedHTML, actualHTML); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedHTML))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualHTML))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualHTML)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
@@ -1512,3 +1578,12 @@ func TestBigElement(t *testing.T) {
t.Errorf("unexpected failure to parse")
}
}
+
+func getLineLengths(s string) (lengths []int) {
+ lines := strings.Split(s, "\n")
+ lengths = make([]int, len(lines))
+ for i := 0; i < len(lines); i++ {
+ lengths[i] = len(lines[i])
+ }
+ return lengths
+}
diff --git a/parser/v2/forexpressionparser_test.go b/parser/v2/forexpressionparser_test.go
index 793bb6f3f..db2c9d3a9 100644
--- a/parser/v2/forexpressionparser_test.go
+++ b/parser/v2/forexpressionparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"testing"
"github.com/a-h/parse"
@@ -9,15 +10,19 @@ import (
func TestForExpressionParser(t *testing.T) {
tests := []struct {
- name string
- input string
- expected interface{}
+ name string
+ input string
+ expected interface{}
+ expectedHTML string
}{
{
name: "for: simple",
input: `for _, item := range p.Items {
{ item }
}`,
+ expectedHTML: `
+
+ `,
expected: ForExpression{
Expression: Expression{
Value: `_, item := range p.Items`,
@@ -71,6 +76,9 @@ func TestForExpressionParser(t *testing.T) {
input: `for _, item := range p.Items{
{ item }
}`,
+ expectedHTML: `
+
+ `,
expected: ForExpression{
Expression: Expression{
Value: `_, item := range p.Items`,
@@ -134,6 +142,23 @@ func TestForExpressionParser(t *testing.T) {
if diff := cmp.Diff(tt.expected, actual); diff != "" {
t.Error(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextHTML)
+ if err := actual.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualHTML := w.String()
+ if diff := cmp.Diff(tt.expectedHTML, actualHTML); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedHTML))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualHTML))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualHTML)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
diff --git a/parser/v2/format_test.go b/parser/v2/format_test.go
index 64b69f1c3..5036a6d3a 100644
--- a/parser/v2/format_test.go
+++ b/parser/v2/format_test.go
@@ -28,7 +28,8 @@ func TestFormat(t *testing.T) {
t.Fatal(err)
}
var actual bytes.Buffer
- if err := tem.Write(&actual); err != nil {
+ cw := NewContextWriter(&actual, WriteContextAll)
+ if err := tem.Write(cw); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if diff := cmp.Diff(string(a.Files[1].Data), actual.String()); diff != "" {
diff --git a/parser/v2/htmlcommentparser_test.go b/parser/v2/htmlcommentparser_test.go
index 735a4820c..3924798d7 100644
--- a/parser/v2/htmlcommentparser_test.go
+++ b/parser/v2/htmlcommentparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"testing"
"github.com/a-h/parse"
@@ -67,6 +68,19 @@ func TestHTMLCommentParser(t *testing.T) {
if diff := cmp.Diff(tt.expected, result); diff != "" {
t.Errorf(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextHTML)
+ if err := result.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualHTML := w.String()
+ if diff := cmp.Diff(tt.input, actualHTML); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualHTML))
+ }
})
}
}
diff --git a/parser/v2/ifexpressionparser_test.go b/parser/v2/ifexpressionparser_test.go
index de3f3fcd8..3b5f52f18 100644
--- a/parser/v2/ifexpressionparser_test.go
+++ b/parser/v2/ifexpressionparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"testing"
"github.com/a-h/parse"
@@ -9,9 +10,10 @@ import (
func TestIfExpression(t *testing.T) {
tests := []struct {
- name string
- input string
- expected IfExpression
+ name string
+ input string
+ expected IfExpression
+ expectedHTML string
}{
{
name: "if: simple expression",
@@ -21,6 +23,11 @@ func TestIfExpression(t *testing.T) {
}
`,
+ expectedHTML: `
+
+
+
+ `,
expected: IfExpression{
Expression: Expression{
Value: `p.Test`,
@@ -607,6 +614,25 @@ func TestIfExpression(t *testing.T) {
if diff := cmp.Diff(tt.expected, actual); diff != "" {
t.Error(diff)
}
+
+ if tt.expectedHTML != "" {
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextHTML)
+ if err := actual.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualHTML := w.String()
+ if diff := cmp.Diff(tt.expectedHTML, actualHTML); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedHTML))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualHTML))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualHTML)); diff != "" {
+ t.Errorf(diff)
+ }
+ }
})
}
}
diff --git a/parser/v2/scripttemplateparser_test.go b/parser/v2/scripttemplateparser_test.go
index fae590f5d..e0fc00700 100644
--- a/parser/v2/scripttemplateparser_test.go
+++ b/parser/v2/scripttemplateparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"fmt"
"testing"
@@ -10,14 +11,17 @@ import (
func TestScriptTemplateParser(t *testing.T) {
var tests = []struct {
- name string
- input string
- expected ScriptTemplate
+ name string
+ input string
+ expected ScriptTemplate
+ expectedJS string
}{
{
name: "script: no parameters, no content",
input: `script Name() {
}`,
+ expectedJS: `
+ `,
expected: ScriptTemplate{
Name: Expression{
Value: "Name",
@@ -55,6 +59,8 @@ func TestScriptTemplateParser(t *testing.T) {
name: "script: no spaces",
input: `script Name(){
}`,
+ expectedJS: `
+ `,
expected: ScriptTemplate{
Name: Expression{
Value: "Name",
@@ -93,6 +99,9 @@ func TestScriptTemplateParser(t *testing.T) {
input: `script Name() {
var x = "x";
}`,
+ expectedJS: `
+var x = "x";
+ `,
expected: ScriptTemplate{
Name: Expression{
Value: "Name",
@@ -132,6 +141,9 @@ var x = "x";
input: `script Name(value string) {
console.log(value);
}`,
+ expectedJS: `
+console.log(value);
+ `,
expected: ScriptTemplate{
Name: Expression{
Value: "Name",
@@ -171,6 +183,9 @@ console.log(value);
input: `script Name() {
//'
}`,
+ expectedJS: `
+ //'
+ `,
expected: ScriptTemplate{
Name: Expression{
Value: "Name",
@@ -210,6 +225,9 @@ console.log(value);
input: `script Name() {
let x = '';
}`,
+ expectedJS: `
+ let x = '';
+ `,
expected: ScriptTemplate{
Name: Expression{
Value: "Name",
@@ -266,6 +284,23 @@ console.log(value);
t.Error("unexpected suffix")
t.Error(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextJS)
+ if err := actual.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualJS := w.String()
+ if diff := cmp.Diff(tt.expectedJS, actualJS); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedJS))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualJS))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualJS)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
diff --git a/parser/v2/switchexpressionparser_test.go b/parser/v2/switchexpressionparser_test.go
index e17cae717..ca524bd7a 100644
--- a/parser/v2/switchexpressionparser_test.go
+++ b/parser/v2/switchexpressionparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"testing"
"github.com/a-h/parse"
@@ -9,14 +10,17 @@ import (
func TestSwitchExpressionParser(t *testing.T) {
tests := []struct {
- name string
- input string
- expected SwitchExpression
+ name string
+ input string
+ expected SwitchExpression
+ expectedHTML string
}{
{
name: "switch: simple",
input: `switch "stringy" {
}`,
+ expectedHTML: `
+ `,
expected: SwitchExpression{
Expression: Expression{
Value: `"stringy"`,
@@ -43,6 +47,12 @@ default:
{ "span content" }
}`,
+ expectedHTML: `
+
+
+
+
+ `,
expected: SwitchExpression{
Expression: Expression{
Value: `"stringy"`,
@@ -121,6 +131,12 @@ default:
{ "span content" }
}`,
+ expectedHTML: `
+
+
+
+
+ `,
expected: SwitchExpression{
Expression: Expression{
Value: `"stringy"`,
@@ -198,6 +214,12 @@ default:
case "b":
{ "B" }
}`,
+ expectedHTML: `
+
+
+
+
+ `,
expected: SwitchExpression{
Expression: Expression{
Value: `"stringy"`,
@@ -314,6 +336,23 @@ default:
if diff := cmp.Diff(tt.expected, actual); diff != "" {
t.Error(diff)
}
+
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextHTML)
+ if err := actual.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualHTML := w.String()
+ if diff := cmp.Diff(tt.expectedHTML, actualHTML); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedHTML))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualHTML))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualHTML)); diff != "" {
+ t.Errorf(diff)
+ }
})
}
}
diff --git a/parser/v2/templatefile_test.go b/parser/v2/templatefile_test.go
index cd0b91aa9..457cdedab 100644
--- a/parser/v2/templatefile_test.go
+++ b/parser/v2/templatefile_test.go
@@ -291,7 +291,8 @@ templ Hello() {
}
sb := new(strings.Builder)
- err = tf.Write(sb)
+ cw := NewContextWriter(sb, WriteContextAll)
+ err = tf.Write(cw)
if err != nil {
t.Fatalf("failed to write template file: %v", err)
}
diff --git a/parser/v2/templateparser_test.go b/parser/v2/templateparser_test.go
index 56caec5a7..db9492af6 100644
--- a/parser/v2/templateparser_test.go
+++ b/parser/v2/templateparser_test.go
@@ -1,6 +1,7 @@
package parser
import (
+ "bytes"
"testing"
"github.com/a-h/parse"
@@ -9,10 +10,11 @@ import (
func TestTemplateParser(t *testing.T) {
var tests = []struct {
- name string
- input string
- expected HTMLTemplate
- expectError bool
+ name string
+ input string
+ expected HTMLTemplate
+ expectedHTML string
+ expectError bool
}{
{
name: "template: no parameters",
@@ -131,6 +133,9 @@ func TestTemplateParser(t *testing.T) {
input: `templ Name(p Parameter) {
{ "span content" }
}`,
+ expectedHTML: `
+
+ `,
expected: HTMLTemplate{
Expression: Expression{
Value: "Name(p Parameter)",
@@ -749,6 +754,24 @@ func TestTemplateParser(t *testing.T) {
case !tt.expectError && diff != "":
t.Errorf(diff)
}
+ if tt.expectedHTML != "" {
+ w := new(bytes.Buffer)
+ cw := NewContextWriter(w, WriteContextHTML)
+ if err := actual.Write(cw, 0); err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ actualHTML := w.String()
+ if diff := cmp.Diff(tt.expectedHTML, actualHTML); diff != "" {
+ t.Error(diff)
+
+ t.Errorf("input:\n%s", displayWhitespaceChars(tt.input))
+ t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedHTML))
+ t.Errorf("got:\n%s", displayWhitespaceChars(actualHTML))
+ }
+ if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualHTML)); diff != "" {
+ t.Errorf(diff)
+ }
+ }
})
}
}
diff --git a/parser/v2/types.go b/parser/v2/types.go
index 7ff842b24..6ab501874 100644
--- a/parser/v2/types.go
+++ b/parser/v2/types.go
@@ -46,6 +46,53 @@ import (
//
// }
+type WriteContext int
+
+func (wc WriteContext) IsSet(flag WriteContext) bool {
+ return wc&flag == flag
+}
+
+const WriteContextGo WriteContext = 0b0001
+const WriteContextHTML WriteContext = 0b0010
+const WriteContextCSS WriteContext = 0b0100
+const WriteContextJS WriteContext = 0b1000
+const WriteContextAll WriteContext = 0b1111
+
+type ContextWriter struct {
+ wc WriteContext
+ w io.Writer
+}
+
+func NewContextWriter(w io.Writer, wc WriteContext) ContextWriter {
+ return ContextWriter{
+ wc: wc,
+ w: w,
+ }
+}
+
+func (cw ContextWriter) Write(wc WriteContext, s string) (err error) {
+ if cw.wc.IsSet(wc) {
+ //TODO: Keep a map of indices to the writecontext, so we can look up in the LSP what type of context (Go, HTML, CSS, JS etc. should be used)
+ _, err := io.WriteString(cw.w, s)
+ return err
+ }
+
+ _, err = io.WriteString(cw.w, whiteSpaceString(s))
+ return
+}
+
+// Creates in equivalent length string to the one provided while preserving all white space characters.
+func whiteSpaceString(of string) string {
+ output := []rune(of)
+ for i, r := range output {
+ if unicode.IsSpace(r) {
+ continue
+ }
+ output[i] = ' '
+ }
+ return string(output)
+}
+
// Source mapping to map from the source code of the template to the
// in-memory representation.
type Position struct {
@@ -123,24 +170,24 @@ type TemplateFile struct {
Nodes []TemplateFileNode
}
-func (tf TemplateFile) Write(w io.Writer) error {
+func (tf TemplateFile) Write(cw ContextWriter) error {
for _, n := range tf.Header {
- if err := n.Write(w, 0); err != nil {
+ if err := n.Write(cw, 0); err != nil {
return err
}
}
var indent int
- if err := tf.Package.Write(w, indent); err != nil {
+ if err := tf.Package.Write(cw, indent); err != nil {
return err
}
- if _, err := io.WriteString(w, "\n\n"); err != nil {
+ if err := cw.Write(cw.wc, "\n\n"); err != nil {
return err
}
for i := 0; i < len(tf.Nodes); i++ {
- if err := tf.Nodes[i].Write(w, indent); err != nil {
+ if err := tf.Nodes[i].Write(cw, indent); err != nil {
return err
}
- if _, err := io.WriteString(w, getNodeWhitespace(tf.Nodes, i)); err != nil {
+ if err := cw.Write(cw.wc, getNodeWhitespace(tf.Nodes, i)); err != nil {
return err
}
}
@@ -167,7 +214,7 @@ func endsWithComment(s string) bool {
// TemplateFileNode can be a Template, CSS, Script or Go.
type TemplateFileNode interface {
IsTemplateFileNode() bool
- Write(w io.Writer, indent int) error
+ Write(cw ContextWriter, indent int) error
}
// TemplateFileGoExpression within a TemplateFile
@@ -176,36 +223,37 @@ type TemplateFileGoExpression struct {
}
func (exp TemplateFileGoExpression) IsTemplateFileNode() bool { return true }
-func (exp TemplateFileGoExpression) Write(w io.Writer, indent int) error {
+func (exp TemplateFileGoExpression) Write(cw ContextWriter, indent int) error {
data, err := format.Source([]byte(exp.Expression.Value))
if err != nil {
- return writeIndent(w, indent, exp.Expression.Value)
+ return writeIndent(cw, WriteContextGo, indent, exp.Expression.Value)
+ }
+ if err := cw.Write(WriteContextGo, string(data)); err != nil {
+ return err
}
- _, err = w.Write(data)
- return err
+ return nil
}
-func writeLinesIndented(w io.Writer, level int, s string) (err error) {
+func writeLinesIndented(cw ContextWriter, wc WriteContext, level int, s string) (err error) {
indent := strings.Repeat("\t", level)
lines := strings.Split(s, "\n")
indented := strings.Join(lines, "\n"+indent)
- if _, err = io.WriteString(w, indent); err != nil {
+ if err := cw.Write(wc, indent); err != nil {
return err
}
- _, err = io.WriteString(w, indented)
- if err != nil {
- return
+ if err := cw.Write(wc, indented); err != nil {
+ return err
}
- return
+ return nil
}
-func writeIndent(w io.Writer, level int, s ...string) (err error) {
+func writeIndent(cw ContextWriter, wc WriteContext, level int, s ...string) (err error) {
indent := strings.Repeat("\t", level)
- if _, err = io.WriteString(w, indent); err != nil {
+ if err = cw.Write(cw.wc, indent); err != nil {
return err
}
for _, ss := range s {
- _, err = io.WriteString(w, ss)
+ err = cw.Write(wc, ss)
if err != nil {
return
}
@@ -217,8 +265,8 @@ type Package struct {
Expression Expression
}
-func (p Package) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, p.Expression.Value)
+func (p Package) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextGo, indent, p.Expression.Value)
}
// Whitespace.
@@ -228,7 +276,7 @@ type Whitespace struct {
func (ws Whitespace) IsNode() bool { return true }
-func (ws Whitespace) Write(w io.Writer, indent int) error {
+func (ws Whitespace) Write(cw ContextWriter, indent int) error {
if ws.Value == "" || !strings.Contains(ws.Value, "\n") {
return nil
}
@@ -243,8 +291,7 @@ func (ws Whitespace) Write(w io.Writer, indent int) error {
// Since any space following another space is ignored, we can collapse to a single rule.
// So, the rule is... if there's a newline, it becomes a single space, or it's stripped.
// We have to remove the start and end space elsewhere.
- _, err := io.WriteString(w, " ")
- return err
+ return cw.Write(cw.wc, " ")
}
// CSS definition.
@@ -261,17 +308,17 @@ type CSSTemplate struct {
}
func (css CSSTemplate) IsTemplateFileNode() bool { return true }
-func (css CSSTemplate) Write(w io.Writer, indent int) error {
+func (css CSSTemplate) Write(cw ContextWriter, indent int) error {
source := formatFunctionArguments(css.Expression.Value)
- if err := writeIndent(w, indent, "css ", string(source), " {\n"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "css ", string(source), " {\n"); err != nil {
return err
}
for _, p := range css.Properties {
- if err := p.Write(w, indent+1); err != nil {
+ if err := p.Write(cw, indent+1); err != nil {
return err
}
}
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
@@ -280,7 +327,7 @@ func (css CSSTemplate) Write(w io.Writer, indent int) error {
// CSSProperty is a CSS property and value pair.
type CSSProperty interface {
IsCSSProperty() bool
- Write(w io.Writer, indent int) error
+ Write(cw ContextWriter, indent int) error
}
// color: #ffffff;
@@ -290,8 +337,8 @@ type ConstantCSSProperty struct {
}
func (c ConstantCSSProperty) IsCSSProperty() bool { return true }
-func (c ConstantCSSProperty) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, c.String(false)); err != nil {
+func (c ConstantCSSProperty) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextCSS, indent, c.String(false)); err != nil {
return err
}
return nil
@@ -320,14 +367,14 @@ 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 {
+func (c ExpressionCSSProperty) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextCSS, indent, c.Name, ": "); err != nil {
return err
}
- if err := c.Value.Write(w, 0); err != nil {
+ if err := c.Value.Write(cw, 0); err != nil {
return err
}
- if _, err := w.Write([]byte(";\n")); err != nil {
+ if err := cw.Write(WriteContextCSS, ";\n"); err != nil {
return err
}
return nil
@@ -339,8 +386,8 @@ type DocType struct {
}
func (dt DocType) IsNode() bool { return true }
-func (dt DocType) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, "")
+func (dt DocType) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextHTML, indent, "")
}
// HTMLTemplate definition.
@@ -357,15 +404,15 @@ type HTMLTemplate struct {
func (t HTMLTemplate) IsTemplateFileNode() bool { return true }
-func (t HTMLTemplate) Write(w io.Writer, indent int) error {
+func (t HTMLTemplate) Write(cw ContextWriter, indent int) error {
source := formatFunctionArguments(t.Expression.Value)
- if err := writeIndent(w, indent, "templ ", string(source), " {\n"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "templ ", string(source), " {\n"); err != nil {
return err
}
- if err := writeNodesIndented(w, indent+1, t.Children); err != nil {
+ if err := writeNodesIndented(cw, indent+1, t.Children); err != nil {
return err
}
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
@@ -409,7 +456,7 @@ type Nodes struct {
type Node interface {
IsNode() bool
// Write out the string.
- Write(w io.Writer, indent int) error
+ Write(cw ContextWriter, indent int) error
}
type CompositeNode interface {
@@ -440,8 +487,8 @@ func (t Text) Trailing() TrailingSpace {
}
func (t Text) IsNode() bool { return true }
-func (t Text) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, t.Value)
+func (t Text) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextHTML, indent, t.Value)
}
// or ...
@@ -527,8 +574,8 @@ func (e Element) ChildNodes() []Node {
return e.Children
}
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 {
+func (e Element) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextHTML, indent, "<", e.Name); err != nil {
return err
}
for i := 0; i < len(e.Attributes); i++ {
@@ -536,71 +583,71 @@ func (e Element) Write(w io.Writer, indent int) error {
// Only the conditional attributes get indented.
var attrIndent int
if e.IndentAttrs {
- if _, err := w.Write([]byte("\n")); err != nil {
+ if err := cw.Write(cw.wc, "\n"); err != nil {
return err
}
attrIndent = indent + 1
} else {
- if _, err := w.Write([]byte(" ")); err != nil {
+ if err := cw.Write(cw.wc, " "); err != nil {
return err
}
}
- if err := a.Write(w, attrIndent); err != nil {
+ if err := a.Write(cw, attrIndent); err != nil {
return err
}
}
var closeAngleBracketIndent int
if e.IndentAttrs {
- if _, err := w.Write([]byte("\n")); err != nil {
+ if err := cw.Write(cw.wc, "\n"); err != nil {
return err
}
closeAngleBracketIndent = indent
}
if e.hasNonWhitespaceChildren() {
if e.IndentChildren {
- if err := writeIndent(w, closeAngleBracketIndent, ">\n"); err != nil {
+ if err := writeIndent(cw, WriteContextHTML, closeAngleBracketIndent, ">\n"); err != nil {
return err
}
- if err := writeNodesIndented(w, indent+1, e.Children); err != nil {
+ if err := writeNodesIndented(cw, indent+1, e.Children); err != nil {
return err
}
- if err := writeIndent(w, indent, "", e.Name, ">"); err != nil {
+ if err := writeIndent(cw, WriteContextHTML, indent, "", e.Name, ">"); err != nil {
return err
}
return nil
}
- if err := writeIndent(w, closeAngleBracketIndent, ">"); err != nil {
+ if err := writeIndent(cw, WriteContextHTML, closeAngleBracketIndent, ">"); err != nil {
return err
}
- if err := writeNodesWithoutIndentation(w, e.Children); err != nil {
+ if err := writeNodesWithoutIndentation(cw, e.Children); err != nil {
return err
}
- if _, err := w.Write([]byte("" + e.Name + ">")); err != nil {
+ if err := cw.Write(WriteContextHTML, ""+e.Name+">"); err != nil {
return err
}
return nil
}
if e.IsVoidElement() {
- if err := writeIndent(w, closeAngleBracketIndent, "/>"); err != nil {
+ if err := writeIndent(cw, WriteContextHTML, closeAngleBracketIndent, "/>"); err != nil {
return err
}
return nil
}
- if err := writeIndent(w, closeAngleBracketIndent, ">", e.Name, ">"); err != nil {
+ if err := writeIndent(cw, WriteContextHTML, closeAngleBracketIndent, ">", e.Name, ">"); err != nil {
return err
}
return nil
}
-func writeNodesWithoutIndentation(w io.Writer, nodes []Node) error {
- return writeNodes(w, 0, nodes, false)
+func writeNodesWithoutIndentation(cw ContextWriter, nodes []Node) error {
+ return writeNodes(cw, 0, nodes, false)
}
-func writeNodesIndented(w io.Writer, level int, nodes []Node) error {
- return writeNodes(w, level, nodes, true)
+func writeNodesIndented(cw ContextWriter, level int, nodes []Node) error {
+ return writeNodes(cw, level, nodes, true)
}
-func writeNodes(w io.Writer, level int, nodes []Node, indent bool) error {
+func writeNodes(cw ContextWriter, level int, nodes []Node, indent bool) error {
startLevel := level
for i := 0; i < len(nodes); i++ {
_, isWhitespace := nodes[i].(Whitespace)
@@ -609,7 +656,7 @@ func writeNodes(w io.Writer, level int, nodes []Node, indent bool) error {
if isWhitespace {
continue
}
- if err := nodes[i].Write(w, level); err != nil {
+ if err := nodes[i].Write(cw, level); err != nil {
return err
}
@@ -630,7 +677,7 @@ func writeNodes(w io.Writer, level int, nodes []Node, indent bool) error {
case SpaceVertical:
level = startLevel
}
- if _, err := w.Write([]byte(trailing)); err != nil {
+ if err := cw.Write(cw.wc, string(trailing)); err != nil {
return err
}
}
@@ -672,30 +719,30 @@ type RawElement struct {
}
func (e RawElement) IsNode() bool { return true }
-func (e RawElement) Write(w io.Writer, indent int) error {
+func (e RawElement) Write(cw ContextWriter, indent int) error {
// Start.
- if err := writeIndent(w, indent, "<", e.Name); err != nil {
+ if err := writeIndent(cw, WriteContextHTML, indent, "<", e.Name); err != nil {
return err
}
for i := 0; i < len(e.Attributes); i++ {
- if _, err := w.Write([]byte(" ")); err != nil {
+ if err := cw.Write(WriteContextHTML, " "); err != nil {
return err
}
a := e.Attributes[i]
// Don't indent the attributes, only the conditional attributes get indented.
- if err := a.Write(w, 0); err != nil {
+ if err := a.Write(cw, 0); err != nil {
return err
}
}
- if _, err := w.Write([]byte(">")); err != nil {
+ if err := cw.Write(WriteContextHTML, ">"); err != nil {
return err
}
// Contents.
- if _, err := w.Write([]byte(e.Contents)); err != nil {
+ if err := cw.Write(cw.wc, e.Contents); err != nil {
return err
}
// Close.
- if _, err := w.Write([]byte("" + e.Name + ">")); err != nil {
+ if err := cw.Write(WriteContextHTML, ""+e.Name+">"); err != nil {
return err
}
return nil
@@ -703,7 +750,7 @@ func (e RawElement) Write(w io.Writer, indent int) error {
type Attribute interface {
// Write out the string.
- Write(w io.Writer, indent int) error
+ Write(cw ContextWriter, indent int) error
}
//
@@ -716,8 +763,8 @@ func (bca BoolConstantAttribute) String() string {
return bca.Name
}
-func (bca BoolConstantAttribute) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, bca.String())
+func (bca BoolConstantAttribute) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextHTML, indent, bca.Name)
}
// href=""
@@ -736,8 +783,8 @@ func (ca ConstantAttribute) String() string {
return ca.Name + `=` + quote + ca.Value + quote
}
-func (ca ConstantAttribute) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, ca.String())
+func (ca ConstantAttribute) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextHTML, indent, ca.String())
}
// noshade={ templ.Bool(...) }
@@ -751,8 +798,16 @@ func (bea BoolExpressionAttribute) String() string {
return bea.Name + `?={ ` + bea.Expression.Value + ` }`
}
-func (bea BoolExpressionAttribute) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, bea.String())
+func (bea BoolExpressionAttribute) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextHTML, indent, bea.Name); err != nil {
+ return err
+ }
+
+ if cw.wc.IsSet(WriteContextGo) {
+ return cw.Write(WriteContextGo, "?={ "+bea.Expression.Value+" }")
+ } else {
+ return cw.Write(WriteContextHTML, ` =" `+strings.Repeat(" ", len(bea.Expression.Value))+` "`)
+ }
}
// href={ ... }
@@ -762,12 +817,6 @@ type ExpressionAttribute struct {
NameRange Range
}
-func (ea ExpressionAttribute) String() string {
- sb := new(strings.Builder)
- _ = ea.Write(sb, 0)
- return sb.String()
-}
-
func (ea ExpressionAttribute) formatExpression() (exp []string) {
trimmed := strings.TrimSpace(ea.Expression.Value)
if !strings.Contains(trimmed, "\n") {
@@ -797,21 +846,42 @@ func (ea ExpressionAttribute) formatExpression() (exp []string) {
return lines[1 : len(lines)-1]
}
-func (ea ExpressionAttribute) Write(w io.Writer, indent int) (err error) {
+func (ea ExpressionAttribute) Write(cw ContextWriter, indent int) (err error) {
lines := ea.formatExpression()
+
+ if err = writeIndent(cw, WriteContextHTML, indent, ea.Name, "="); err != nil {
+ return err
+ }
+
if len(lines) == 1 {
- return writeIndent(w, indent, ea.Name, `={ `, lines[0], ` }`)
+ if cw.wc.IsSet(WriteContextGo) {
+ return writeIndent(cw, WriteContextGo, 0, `{ `, lines[0], ` }`)
+ } else {
+ return writeIndent(cw, WriteContextHTML, 0, `" `, whiteSpaceString(lines[0]), ` "`)
+ }
}
- if err = writeIndent(w, indent, ea.Name, "={\n"); err != nil {
- return err
+ if cw.wc.IsSet(WriteContextGo) {
+ if err := cw.Write(WriteContextGo, "{\n"); err != nil {
+ return err
+ }
+ } else {
+ if err := cw.Write(cw.wc, `"\n`); err != nil {
+ return err
+ }
}
+
for _, line := range lines {
- if err = writeIndent(w, indent, line, "\n"); err != nil {
+ if err = writeIndent(cw, WriteContextGo, indent, line, "\n"); err != nil {
return err
}
}
- return writeIndent(w, indent, "}")
+
+ if cw.wc.IsSet(WriteContextGo) {
+ return writeIndent(cw, WriteContextGo, indent, "}")
+ } else {
+ return writeIndent(cw, WriteContextHTML, indent, `"`)
+ }
}
//
@@ -823,8 +893,8 @@ func (sa SpreadAttributes) String() string {
return `{ ` + sa.Expression.Value + `... }`
}
-func (sa SpreadAttributes) Write(w io.Writer, indent int) error {
- return writeIndent(w, indent, sa.String())
+func (sa SpreadAttributes) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextGo, indent, sa.String())
}
// ")
+func (c HTMLComment) Write(cw ContextWriter, indent int) error {
+ return writeIndent(cw, WriteContextHTML, indent, "")
}
// Nodes.
@@ -929,9 +993,9 @@ type CallTemplateExpression struct {
}
func (cte CallTemplateExpression) IsNode() bool { return true }
-func (cte CallTemplateExpression) Write(w io.Writer, indent int) error {
+func (cte CallTemplateExpression) Write(cw ContextWriter, indent int) error {
// Rewrite to new call syntax
- return writeIndent(w, indent, `@`, cte.Expression.Value)
+ return writeIndent(cw, WriteContextGo, indent, `@`, cte.Expression.Value)
}
// TemplElementExpression can be used to create and render a template using data.
@@ -949,24 +1013,24 @@ func (tee TemplElementExpression) ChildNodes() []Node {
return tee.Children
}
func (tee TemplElementExpression) IsNode() bool { return true }
-func (tee TemplElementExpression) Write(w io.Writer, indent int) error {
+func (tee TemplElementExpression) Write(cw ContextWriter, indent int) error {
source, err := format.Source([]byte(tee.Expression.Value))
if err != nil {
source = []byte(tee.Expression.Value)
}
- if err := writeLinesIndented(w, indent, "@"+string(source)); err != nil {
+ if err := writeLinesIndented(cw, WriteContextGo, indent, "@"+string(source)); err != nil {
return err
}
if len(tee.Children) == 0 {
return nil
}
- if _, err = io.WriteString(w, " {\n"); err != nil {
+ if err := cw.Write(WriteContextGo, " {\n"); err != nil {
return err
}
- if err := writeNodesIndented(w, indent+1, tee.Children); err != nil {
+ if err := writeNodesIndented(cw, indent+1, tee.Children); err != nil {
return err
}
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
@@ -977,8 +1041,8 @@ func (tee TemplElementExpression) Write(w io.Writer, indent int) error {
type ChildrenExpression struct{}
func (ChildrenExpression) IsNode() bool { return true }
-func (ChildrenExpression) Write(w io.Writer, indent int) error {
- if err := writeIndent(w, indent, "{ children... }"); err != nil {
+func (ChildrenExpression) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextGo, indent, "{ children... }"); err != nil {
return err
}
return nil
@@ -1008,34 +1072,34 @@ func (n IfExpression) ChildNodes() []Node {
return nodes
}
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 {
+func (n IfExpression) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextGo, indent, "if ", n.Expression.Value, " {\n"); err != nil {
return err
}
indent++
- if err := writeNodesIndented(w, indent, n.Then); err != nil {
+ if err := writeNodesIndented(cw, indent, n.Then); err != nil {
return err
}
indent--
for _, elseIf := range n.ElseIfs {
- if err := writeIndent(w, indent, "} else if ", elseIf.Expression.Value, " {\n"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "} else if ", elseIf.Expression.Value, " {\n"); err != nil {
return err
}
indent++
- if err := writeNodesIndented(w, indent, elseIf.Then); err != nil {
+ if err := writeNodesIndented(cw, indent, elseIf.Then); err != nil {
return err
}
indent--
}
if len(n.Else) > 0 {
- if err := writeIndent(w, indent, "} else {\n"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "} else {\n"); err != nil {
return err
}
- if err := writeNodesIndented(w, indent+1, n.Else); err != nil {
+ if err := writeNodesIndented(cw, indent+1, n.Else); err != nil {
return err
}
}
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
@@ -1057,22 +1121,22 @@ func (se SwitchExpression) ChildNodes() []Node {
return nodes
}
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 {
+func (se SwitchExpression) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextGo, 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(cw, WriteContextGo, indent, c.Expression.Value, "\n"); err != nil {
return err
}
- if err := writeNodesIndented(w, indent+1, c.Children); err != nil {
+ if err := writeNodesIndented(cw, indent+1, c.Children); err != nil {
return err
}
}
indent--
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
@@ -1096,14 +1160,14 @@ func (fe ForExpression) ChildNodes() []Node {
return fe.Children
}
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 {
+func (fe ForExpression) Write(cw ContextWriter, indent int) error {
+ if err := writeIndent(cw, WriteContextGo, indent, "for ", fe.Expression.Value, " {\n"); err != nil {
return err
}
- if err := writeNodesIndented(w, indent+1, fe.Children); err != nil {
+ if err := writeNodesIndented(cw, indent+1, fe.Children); err != nil {
return err
}
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
@@ -1123,11 +1187,18 @@ 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 {
+func (se StringExpression) Write(cw ContextWriter, indent int) error {
if isWhitespace(se.Expression.Value) {
se.Expression.Value = ""
}
- return writeIndent(w, indent, `{ `, se.Expression.Value, ` }`)
+
+ if cw.wc.IsSet(WriteContextGo) {
+ return writeIndent(cw, WriteContextGo, indent, `{ `, se.Expression.Value, ` }`)
+ } else if cw.wc.IsSet(WriteContextCSS) {
+ return writeIndent(cw, WriteContextCSS, indent, `' `, whiteSpaceString(se.Expression.Value), ` '`)
+ } else {
+ return writeIndent(cw, WriteContextHTML, indent, `" `, whiteSpaceString(se.Expression.Value), ` "`)
+ }
}
// ScriptTemplate is a script block.
@@ -1138,15 +1209,15 @@ type ScriptTemplate struct {
}
func (s ScriptTemplate) IsTemplateFileNode() bool { return true }
-func (s ScriptTemplate) Write(w io.Writer, indent int) error {
+func (s ScriptTemplate) Write(cw ContextWriter, indent int) error {
source := formatFunctionArguments(s.Name.Value + "(" + s.Parameters.Value + ")")
- if err := writeIndent(w, indent, "script ", string(source), " {\n"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "script ", string(source), " {\n"); err != nil {
return err
}
- if _, err := io.WriteString(w, s.Value); err != nil {
+ if err := cw.Write(WriteContextJS, s.Value); err != nil {
return err
}
- if err := writeIndent(w, indent, "}"); err != nil {
+ if err := writeIndent(cw, WriteContextGo, indent, "}"); err != nil {
return err
}
return nil
diff --git a/parser/v2/types_test.go b/parser/v2/types_test.go
index 312aa007a..fe47baa85 100644
--- a/parser/v2/types_test.go
+++ b/parser/v2/types_test.go
@@ -833,7 +833,8 @@ templ test() {
t.Fatalf("failed to parse template: %v", err)
}
w := new(strings.Builder)
- err = template.Write(w)
+ cw := NewContextWriter(w, WriteContextAll)
+ err = template.Write(cw)
if err != nil {
t.Fatalf("failed to write template: %v", err)
}