diff --git a/.version b/.version index 87e643b96..6c6ec4881 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.2.470 \ No newline at end of file +0.2.470 diff --git a/cmd/templ/migratecmd/main.go b/cmd/templ/migratecmd/main.go index 1d8a8b17b..fd3bd686c 100644 --- a/cmd/templ/migratecmd/main.go +++ b/cmd/templ/migratecmd/main.go @@ -72,7 +72,7 @@ func migrate(fileName string) (err error) { var v2Template v2.TemplateFile // Copy the package and any imports. - var sb strings.Builder + sb := new(strings.Builder) sb.WriteString("package " + v1Template.Package.Expression.Value) sb.WriteString("\n") if len(v1Template.Imports) > 0 { diff --git a/parser/v1/elementparser_test.go b/parser/v1/elementparser_test.go index 0640813ef..22c416002 100644 --- a/parser/v1/elementparser_test.go +++ b/parser/v1/elementparser_test.go @@ -601,7 +601,7 @@ func TestElementParserErrors(t *testing.T) { } func TestBigElement(t *testing.T) { - var sb strings.Builder + sb := new(strings.Builder) sb.WriteString("
") for i := 0; i < 4096*4; i++ { sb.WriteString("a") diff --git a/parser/v2/calltemplateparser.go b/parser/v2/calltemplateparser.go index 5c68954ea..66cc610e8 100644 --- a/parser/v2/calltemplateparser.go +++ b/parser/v2/calltemplateparser.go @@ -10,13 +10,14 @@ var callTemplateExpressionStart = parse.Or(parse.String("{! "), parse.String("{! type callTemplateExpressionParser struct{} -func (p callTemplateExpressionParser) Parse(pi *parse.Input) (r CallTemplateExpression, ok bool, err error) { +func (p callTemplateExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { // Check the prefix first. if _, ok, err = callTemplateExpressionStart.Parse(pi); err != nil || !ok { return } // Once we have a prefix, we must have an expression that returns a template. + var r CallTemplateExpression if r.Expression, ok, err = exp.Parse(pi); err != nil || !ok { return } diff --git a/parser/v2/childrenparser.go b/parser/v2/childrenparser.go index 30a6a57a8..127537be1 100644 --- a/parser/v2/childrenparser.go +++ b/parser/v2/childrenparser.go @@ -4,13 +4,18 @@ import ( "github.com/a-h/parse" ) -var childrenExpression = parse.Func(func(in *parse.Input) (out ChildrenExpression, ok bool, err error) { - _, ok, err = parse.StringFrom( - openBraceWithOptionalPadding, - parse.OptionalWhitespace, - parse.String("children..."), - parse.OptionalWhitespace, - closeBraceWithOptionalPadding, - ).Parse(in) - return out, ok, err +var childrenExpressionParser = parse.StringFrom( + openBraceWithOptionalPadding, + parse.OptionalWhitespace, + parse.String("children..."), + parse.OptionalWhitespace, + closeBraceWithOptionalPadding, +) + +var childrenExpression = parse.Func(func(in *parse.Input) (n Node, ok bool, err error) { + _, ok, err = childrenExpressionParser.Parse(in) + if err != nil || !ok { + return + } + return ChildrenExpression{}, true, nil }) diff --git a/parser/v2/cssparser.go b/parser/v2/cssparser.go index 88b762fed..7a22151e7 100644 --- a/parser/v2/cssparser.go +++ b/parser/v2/cssparser.go @@ -166,10 +166,12 @@ var expressionCSSPropertyParser = parse.Func(func(pi *parse.Input) (r Expression } // { string } - if r.Value, ok, err = stringExpression.Parse(pi); err != nil || !ok { + var se Node + if se, ok, err = stringExpression.Parse(pi); err != nil || !ok { pi.Seek(start) return } + r.Value = se.(StringExpression) // ; if _, ok, err = Must(parse.String(";"), "missing expected semicolon (;)").Parse(pi); err != nil || !ok { diff --git a/parser/v2/doctypeparser.go b/parser/v2/doctypeparser.go index ed79a8ccb..0039e0820 100644 --- a/parser/v2/doctypeparser.go +++ b/parser/v2/doctypeparser.go @@ -6,7 +6,8 @@ import ( var doctypeStartParser = parse.StringInsensitive("") for i := 0; i < 4096*4; i++ { sb.WriteString("a") diff --git a/parser/v2/expressionparser.go b/parser/v2/expressionparser.go index abd194f20..ac26f8f62 100644 --- a/parser/v2/expressionparser.go +++ b/parser/v2/expressionparser.go @@ -7,8 +7,8 @@ import ( ) // StripType takes the parser and throws away the return value. -func StripType[T any](p parse.Parser[T]) parse.Parser[interface{}] { - return parse.Func(func(in *parse.Input) (out interface{}, ok bool, err error) { +func StripType[T any](p parse.Parser[T]) parse.Parser[any] { + return parse.Func(func(in *parse.Input) (out any, ok bool, err error) { return p.Parse(in) }) } @@ -57,6 +57,10 @@ var openBracket = parse.String("(") var closeBracket = parse.String(")") var closeBracketWithOptionalPadding = parse.StringFrom(optionalSpaces, closeBracket) +var stringUntilNewLine = parse.StringUntil[string](parse.NewLine) +var newLineOrEOF = parse.Or(parse.NewLine, parse.EOF[string]()) +var stringUntilNewLineOrEOF = parse.StringUntil(newLineOrEOF) + var exp = expressionParser{ startBraceCount: 1, } @@ -70,7 +74,7 @@ func (p expressionParser) Parse(pi *parse.Input) (s Expression, ok bool, err err braceCount := p.startBraceCount - var sb strings.Builder + sb := new(strings.Builder) loop: for { var result string @@ -147,7 +151,7 @@ func (p functionArgsParser) Parse(pi *parse.Input) (s Expression, ok bool, err e bracketCount := p.startBracketCount - var sb strings.Builder + sb := new(strings.Builder) loop: for { var result string diff --git a/parser/v2/forexpressionparser.go b/parser/v2/forexpressionparser.go index 3ab04926f..ac0a8527c 100644 --- a/parser/v2/forexpressionparser.go +++ b/parser/v2/forexpressionparser.go @@ -4,7 +4,7 @@ import ( "github.com/a-h/parse" ) -var forExpression = parse.Func(func(pi *parse.Input) (r ForExpression, ok bool, err error) { +var forExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { // Check the prefix first. if _, ok, err = parse.String("for ").Parse(pi); err != nil || !ok { return @@ -13,6 +13,7 @@ var forExpression = parse.Func(func(pi *parse.Input) (r ForExpression, ok bool, // Once we've got a prefix, read until {\n. // If there's no match, there's no {\n, which is an error. from := pi.Position() + var r ForExpression until := parse.All(openBraceWithOptionalPadding, parse.NewLine) var fexp string if fexp, ok, err = Must(parse.StringUntil(until), "for: "+unterminatedMissingCurly).Parse(pi); err != nil || !ok { diff --git a/parser/v2/gocommentparser.go b/parser/v2/gocommentparser.go index 106148689..e27eb29a7 100644 --- a/parser/v2/gocommentparser.go +++ b/parser/v2/gocommentparser.go @@ -12,8 +12,9 @@ type goSingleLineCommentParser struct { var goSingleLineComment = goSingleLineCommentParser{} -func (p goSingleLineCommentParser) Parse(pi *parse.Input) (c GoComment, ok bool, err error) { +func (p goSingleLineCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { // Comment start. + var c GoComment if _, ok, err = goSingleLineCommentStart.Parse(pi); err != nil || !ok { return } @@ -37,8 +38,9 @@ type goMultiLineCommentParser struct { var goMultiLineComment = goMultiLineCommentParser{} -func (p goMultiLineCommentParser) Parse(pi *parse.Input) (c GoComment, ok bool, err error) { +func (p goMultiLineCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { // Comment start. + var c GoComment if _, ok, err = goMultiLineCommentStart.Parse(pi); err != nil || !ok { return } @@ -55,4 +57,4 @@ func (p goMultiLineCommentParser) Parse(pi *parse.Input) (c GoComment, ok bool, return c, true, nil } -var goComment = parse.Any[GoComment](goSingleLineComment, goMultiLineComment) +var goComment = parse.Any[Node](goSingleLineComment, goMultiLineComment) diff --git a/parser/v2/htmlcommentparser.go b/parser/v2/htmlcommentparser.go index 4a570075f..e42db0003 100644 --- a/parser/v2/htmlcommentparser.go +++ b/parser/v2/htmlcommentparser.go @@ -12,8 +12,9 @@ type htmlCommentParser struct { var htmlComment = htmlCommentParser{} -func (p htmlCommentParser) Parse(pi *parse.Input) (c HTMLComment, ok bool, err error) { +func (p htmlCommentParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { // Comment start. + var c HTMLComment if _, ok, err = htmlCommentStart.Parse(pi); err != nil || !ok { return } diff --git a/parser/v2/ifexpressionparser.go b/parser/v2/ifexpressionparser.go index d1d4b41a3..9e756d6e8 100644 --- a/parser/v2/ifexpressionparser.go +++ b/parser/v2/ifexpressionparser.go @@ -8,7 +8,7 @@ var ifExpression ifExpressionParser type ifExpressionParser struct{} -func (ifExpressionParser) Parse(pi *parse.Input) (r IfExpression, ok bool, err error) { +func (ifExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { // Check the prefix first. if _, ok, err = parse.String("if ").Parse(pi); err != nil || !ok { return @@ -16,6 +16,7 @@ func (ifExpressionParser) Parse(pi *parse.Input) (r IfExpression, ok bool, err e // Once we've got a prefix, read until {\n. // If there's no match, there's no {\n, which is an error. + var r IfExpression if r.Expression, ok, err = Must(ExpressionOf(parse.StringUntil(parse.All(openBraceWithOptionalPadding, parse.NewLine))), "if: "+unterminatedMissingCurly).Parse(pi); err != nil || !ok { return } diff --git a/parser/v2/packageparser.go b/parser/v2/packageparser.go index 8bdd63e90..2e973dbbd 100644 --- a/parser/v2/packageparser.go +++ b/parser/v2/packageparser.go @@ -15,7 +15,7 @@ var pkg = parse.Func(func(pi *parse.Input) (pkg Package, ok bool, err error) { // Once we have the prefix, it's an expression until the end of the line. var exp string - if exp, ok, err = Must(parse.StringUntil(parse.NewLine), "package literal not terminated").Parse(pi); err != nil || !ok { + if exp, ok, err = Must(stringUntilNewLine, "package literal not terminated").Parse(pi); err != nil || !ok { return } if len(exp) == 0 { diff --git a/parser/v2/raw.go b/parser/v2/raw.go index 706eb2d3c..004e346a9 100644 --- a/parser/v2/raw.go +++ b/parser/v2/raw.go @@ -18,7 +18,7 @@ type rawElementParser struct { name string } -func (p rawElementParser) Parse(pi *parse.Input) (e RawElement, ok bool, err error) { +func (p rawElementParser) Parse(pi *parse.Input) (n Node, ok bool, err error) { start := pi.Index() // < @@ -27,6 +27,7 @@ func (p rawElementParser) Parse(pi *parse.Input) (e RawElement, ok bool, err err } // Element name. + var e RawElement if e.Name, ok, err = parse.String(p.name).Parse(pi); err != nil || !ok { pi.Seek(start) return diff --git a/parser/v2/stringexpressionparser.go b/parser/v2/stringexpressionparser.go index cd8f8318f..80ad2b311 100644 --- a/parser/v2/stringexpressionparser.go +++ b/parser/v2/stringexpressionparser.go @@ -4,13 +4,14 @@ import ( "github.com/a-h/parse" ) -var stringExpression = parse.Func(func(pi *parse.Input) (r StringExpression, ok bool, err error) { +var stringExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { // Check the prefix first. if _, ok, err = parse.Or(parse.String("{ "), parse.String("{")).Parse(pi); err != nil || !ok { return } // Once we have a prefix, we must have an expression that returns a string. + var r StringExpression if r.Expression, ok, err = exp.Parse(pi); err != nil || !ok { return } diff --git a/parser/v2/stringexpressionparser_test.go b/parser/v2/stringexpressionparser_test.go index c4bc83eb1..e7a777e22 100644 --- a/parser/v2/stringexpressionparser_test.go +++ b/parser/v2/stringexpressionparser_test.go @@ -62,13 +62,14 @@ func TestStringExpressionParser(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { input := parse.NewInput(tt.input) - actual, ok, err := stringExpression.Parse(input) + an, ok, err := stringExpression.Parse(input) if err != nil { t.Fatalf("unexpected error: %v", err) } if !ok { t.Fatalf("unexpected failure for input %q", tt.input) } + actual := an.(StringExpression) if diff := cmp.Diff(tt.expected, actual); diff != "" { t.Error(diff) } diff --git a/parser/v2/switchexpressionparser.go b/parser/v2/switchexpressionparser.go index 780b34f8b..9f7694e38 100644 --- a/parser/v2/switchexpressionparser.go +++ b/parser/v2/switchexpressionparser.go @@ -6,13 +6,14 @@ import ( "github.com/a-h/parse" ) -var switchExpression = parse.Func(func(pi *parse.Input) (r SwitchExpression, ok bool, err error) { +var switchExpression = parse.Func(func(pi *parse.Input) (n Node, ok bool, err error) { // Check the prefix first. if _, ok, err = parse.String("switch ").Parse(pi); err != nil || !ok { return } // Once we've got a prefix, read until {\n. + var r SwitchExpression endOfStatementExpression := ExpressionOf(parse.StringUntil(parse.All(openBraceWithOptionalPadding, parse.NewLine))) if r.Expression, ok, err = Must(endOfStatementExpression, "switch: "+unterminatedMissingCurly).Parse(pi); err != nil || !ok { return diff --git a/parser/v2/templatefile.go b/parser/v2/templatefile.go index 782d5d3f7..d6afd2000 100644 --- a/parser/v2/templatefile.go +++ b/parser/v2/templatefile.go @@ -60,16 +60,18 @@ func NewTemplateFileParser(pkg string) TemplateFileParser { } } -var ErrLegacyFileFormat = errors.New("Legacy file format - run templ migrate") -var ErrTemplateNotFound = errors.New("Template not found") +var ErrLegacyFileFormat = errors.New("legacy file format - run templ migrate") +var ErrTemplateNotFound = errors.New("template not found") type TemplateFileParser struct { DefaultPackage string } +var legacyPackageParser = parse.String("{% package") + func (p TemplateFileParser) Parse(pi *parse.Input) (tf TemplateFile, ok bool, err error) { // If we're parsing a legacy file, complain that migration needs to happen. - _, ok, err = parse.String("{% package").Parse(pi) + _, ok, err = legacyPackageParser.Parse(pi) if err != nil { return } @@ -91,7 +93,7 @@ func (p TemplateFileParser) Parse(pi *parse.Input) (tf TemplateFile, ok bool, er } var line string - line, ok, err = parse.StringUntil(parse.NewLine).Parse(pi) + line, ok, err = stringUntilNewLine.Parse(pi) if err != nil { return } @@ -146,14 +148,14 @@ outer: } // Anything that isn't template content is Go code. - var code strings.Builder + code := new(strings.Builder) from := pi.Position() inner: for { // Check to see if this line isn't Go code. last := pi.Index() var l string - if l, ok, err = parse.StringUntil(parse.Or(parse.NewLine, parse.EOF[string]())).Parse(pi); err != nil { + if l, ok, err = stringUntilNewLineOrEOF.Parse(pi); err != nil { return } hasTemplatePrefix := strings.HasPrefix(l, "templ ") || strings.HasPrefix(l, "css ") || strings.HasPrefix(l, "script ") diff --git a/parser/v2/templateparser.go b/parser/v2/templateparser.go index c619c6ad9..673f139ba 100644 --- a/parser/v2/templateparser.go +++ b/parser/v2/templateparser.go @@ -63,7 +63,24 @@ type templateNodeParser[TUntil any] struct { untilName string } -var rawElements = parse.Any[RawElement](styleElement, scriptElement) +var rawElements = parse.Any[Node](styleElement, scriptElement) + +var templateNodeParsers = []parse.Parser[Node]{ + docType, // + htmlComment, //