Skip to content

Commit

Permalink
feat: add support for templates to be receivers, see #46 and #35 (ite…
Browse files Browse the repository at this point in the history
…m 1)
  • Loading branch information
a-h committed May 1, 2022
1 parent 03290d0 commit e3284e3
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 203 deletions.
18 changes: 5 additions & 13 deletions generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,21 +304,13 @@ func (g *generator) writeTemplate(t parser.HTMLTemplate) error {
if _, err = g.w.Write("func "); err != nil {
return err
}
if r, err = g.w.Write(t.Name.Value); err != nil {
return err
}
g.sourceMap.Add(t.Name, r)
// (
if _, err = g.w.Write("("); err != nil {
// (r *Receiver) Name(params []string)
if r, err = g.w.Write(t.Expression.Value); err != nil {
return err
}
// Write parameters.
if r, err = g.w.Write(t.Parameters.Value); err != nil {
return err
}
g.sourceMap.Add(t.Parameters, r)
// ) templ.Component {
if _, err = g.w.Write(") templ.Component {\n"); err != nil {
g.sourceMap.Add(t.Expression, r)
// templ.Component {
if _, err = g.w.Write(" templ.Component {\n"); err != nil {
return err
}
indentLevel++
Expand Down
3 changes: 1 addition & 2 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ func (p templateParser) Parse(pi parse.Input) parse.Result {
return tepr
}
te := tepr.Item.(templateExpression)
r.Name = te.Name
r.Parameters = te.Parameters
r.Expression = te.Expression

// Once we're in a template, we should expect some template whitespace, if/switch/for,
// or node string expressions etc.
Expand Down
62 changes: 20 additions & 42 deletions parser/templateparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package parser

import (
"io"
"strings"

"github.com/a-h/lexical/parse"
)
Expand All @@ -11,20 +10,16 @@ import (

// TemplateExpression.
// templ Func(p Parameter) {
// templ (data Data) Func(p Parameter) {
// templ (data []string) Func(p Parameter) {
type templateExpression struct {
Name Expression
Parameters Expression
Expression Expression
}

func newTemplateExpressionParser() templateExpressionParser {
return templateExpressionParser{}
}

var templateNameParser = parse.All(parse.WithStringConcatCombiner,
parse.Letter,
parse.Many(parse.WithStringConcatCombiner, 0, 1000, parse.Any(parse.Letter, parse.ZeroToNine)),
)

type templateExpressionParser struct {
}

Expand All @@ -39,50 +34,33 @@ func (p templateExpressionParser) Parse(pi parse.Input) parse.Result {
return prefixResult
}

// Once we have the prefix, we must have a name and parameters.
// Read the name of the function.
from := NewPositionFromInput(pi)
pr := templateNameParser(pi)
if pr.Error != nil && pr.Error != io.EOF {
return pr
}
// If there's no match, the name wasn't correctly terminated.
if !pr.Success {
return parse.Failure("templateExpressionParser", newParseError("template expression: invalid name", from, NewPositionFromInput(pi)))
}
// Remove the final "(" from the position.
to := NewPositionFromInput(pi)
to.Col--
to.Index--
r.Name = NewExpression(strings.TrimSuffix(pr.Item.(string), "("), from, to)

// Eat the left bracket.
if lb := parse.Rune('(')(pi); !lb.Success {
return parse.Failure("templateExpressionParser", newParseError("template expression: parameters missing open bracket", from, NewPositionFromInput(pi)))
}
// Once we have the prefix, everything to the brace at the end of the line is Go.
// e.g.
// templ (x []string) Test() {
// becomes:
// func (x []string) Test() templ.Component {

// Read the parameters.
from = NewPositionFromInput(pi)
pr = parse.StringUntil(parse.Rune(')'))(pi) // p Person, other Other, t thing.Thing)
// Once we've got a prefix, read until {\n.
from := NewPositionFromInput(pi)
pr := parse.StringUntil(parse.All(parse.WithStringConcatCombiner, openBraceWithOptionalPadding, newLine))(pi)
if pr.Error != nil && pr.Error != io.EOF {
return pr
}
// If there's no match, the name wasn't correctly terminated.
// If there's no match, there's no {\n, which is an error.
if !pr.Success {
return parse.Failure("templateExpressionParser", newParseError("template expression: parameters missing close bracket", from, NewPositionFromInput(pi)))
return parse.Failure("templateExpressionParser", newParseError("templ: unterminated (missing closing '{\n')", from, NewPositionFromInput(pi)))
}
r.Parameters = NewExpression(strings.TrimSuffix(pr.Item.(string), ")"), from, NewPositionFromInput(pi))
r.Expression = NewExpression(pr.Item.(string), from, NewPositionFromInput(pi))

// Eat ") {".
// Eat " {".
from = NewPositionFromInput(pi)
if lb := expressionFuncEnd(pi); !lb.Success {
return parse.Failure("templateExpressionParser", newParseError("template expression: unterminated (missing ') {')", from, NewPositionFromInput(pi)))
if te := expressionEnd(pi); !te.Success {
return parse.Failure("templateExpressionParser", newParseError("templ: unterminated (missing closing '{')", from, NewPositionFromInput(pi)))
}

// Expect a newline.
from = NewPositionFromInput(pi)
if lb := newLine(pi); !lb.Success {
return parse.Failure("templateExpressionParser", newParseError("template expression: missing terminating newline", from, NewPositionFromInput(pi)))
// Eat required newline.
if lb := newLine(pi); lb.Error != nil {
return lb
}

return parse.Success("templateExpressionParser", r, nil)
Expand Down
Loading

0 comments on commit e3284e3

Please sign in to comment.