diff options
Diffstat (limited to 'tpl/tplimpl/template_ast_transformers.go')
-rw-r--r-- | tpl/tplimpl/template_ast_transformers.go | 102 |
1 files changed, 87 insertions, 15 deletions
diff --git a/tpl/tplimpl/template_ast_transformers.go b/tpl/tplimpl/template_ast_transformers.go index 28898c55b..57fafcd88 100644 --- a/tpl/tplimpl/template_ast_transformers.go +++ b/tpl/tplimpl/template_ast_transformers.go @@ -39,6 +39,14 @@ var reservedContainers = map[string]bool{ "Data": true, } +type templateType int + +const ( + templateUndefined templateType = iota + templateShortcode + templatePartial +) + type templateContext struct { decl decl visited map[string]bool @@ -47,14 +55,16 @@ type templateContext struct { // The last error encountered. err error - // Only needed for shortcodes - isShortcode bool + typ templateType // Set when we're done checking for config header. configChecked bool // Contains some info about the template tpl.Info + + // Store away the return node in partials. + returnNode *parse.CommandNode } func (c templateContext) getIfNotVisited(name string) *parse.Tree { @@ -84,12 +94,12 @@ func createParseTreeLookup(templ *template.Template) func(nn string) *parse.Tree } } -func applyTemplateTransformersToHMLTTemplate(isShortcode bool, templ *template.Template) (tpl.Info, error) { - return applyTemplateTransformers(isShortcode, templ.Tree, createParseTreeLookup(templ)) +func applyTemplateTransformersToHMLTTemplate(typ templateType, templ *template.Template) (tpl.Info, error) { + return applyTemplateTransformers(typ, templ.Tree, createParseTreeLookup(templ)) } -func applyTemplateTransformersToTextTemplate(isShortcode bool, templ *texttemplate.Template) (tpl.Info, error) { - return applyTemplateTransformers(isShortcode, templ.Tree, +func applyTemplateTransformersToTextTemplate(typ templateType, templ *texttemplate.Template) (tpl.Info, error) { + return applyTemplateTransformers(typ, templ.Tree, func(nn string) *parse.Tree { tt := templ.Lookup(nn) if tt != nil { @@ -99,19 +109,54 @@ func applyTemplateTransformersToTextTemplate(isShortcode bool, templ *texttempla }) } -func applyTemplateTransformers(isShortcode bool, templ *parse.Tree, lookupFn func(name string) *parse.Tree) (tpl.Info, error) { +func applyTemplateTransformers(typ templateType, templ *parse.Tree, lookupFn func(name string) *parse.Tree) (tpl.Info, error) { if templ == nil { return tpl.Info{}, errors.New("expected template, but none provided") } c := newTemplateContext(lookupFn) - c.isShortcode = isShortcode + c.typ = typ + + _, err := c.applyTransformations(templ.Root) - err := c.applyTransformations(templ.Root) + if err == nil && c.returnNode != nil { + // This is a partial with a return statement. + c.Info.HasReturn = true + templ.Root = c.wrapInPartialReturnWrapper(templ.Root) + } return c.Info, err } +const ( + partialReturnWrapperTempl = `{{ $_hugo_dot := $ }}{{ $ := .Arg }}{{ with .Arg }}{{ $_hugo_dot.Set ("PLACEHOLDER") }}{{ end }}` +) + +var partialReturnWrapper *parse.ListNode + +func init() { + templ, err := texttemplate.New("").Parse(partialReturnWrapperTempl) + if err != nil { + panic(err) + } + partialReturnWrapper = templ.Tree.Root +} + +func (c *templateContext) wrapInPartialReturnWrapper(n *parse.ListNode) *parse.ListNode { + wrapper := partialReturnWrapper.CopyList() + withNode := wrapper.Nodes[2].(*parse.WithNode) + retn := withNode.List.Nodes[0] + setCmd := retn.(*parse.ActionNode).Pipe.Cmds[0] + setPipe := setCmd.Args[1].(*parse.PipeNode) + // Replace PLACEHOLDER with the real return value. + // Note that this is a PipeNode, so it will be wrapped in parens. + setPipe.Cmds = []*parse.CommandNode{c.returnNode} + withNode.List.Nodes = append(n.Nodes, retn) + + return wrapper + +} + // The truth logic in Go's template package is broken for certain values // for the if and with keywords. This works around that problem by wrapping // the node passed to if/with in a getif conditional. @@ -141,7 +186,7 @@ func (c *templateContext) wrapWithGetIf(p *parse.PipeNode) { // 1) Make all .Params.CamelCase and similar into lowercase. // 2) Wraps every with and if pipe in getif // 3) Collects some information about the template content. -func (c *templateContext) applyTransformations(n parse.Node) error { +func (c *templateContext) applyTransformations(n parse.Node) (bool, error) { switch x := n.(type) { case *parse.ListNode: if x != nil { @@ -169,12 +214,16 @@ func (c *templateContext) applyTransformations(n parse.Node) error { c.decl[x.Decl[0].Ident[0]] = x.Cmds[0].String() } - for _, cmd := range x.Cmds { - c.applyTransformations(cmd) + for i, cmd := range x.Cmds { + keep, _ := c.applyTransformations(cmd) + if !keep { + x.Cmds = append(x.Cmds[:i], x.Cmds[i+1:]...) + } } case *parse.CommandNode: c.collectInner(x) + keep := c.collectReturnNode(x) for _, elem := range x.Args { switch an := elem.(type) { @@ -191,9 +240,10 @@ func (c *templateContext) applyTransformations(n parse.Node) error { } } } + return keep, c.err } - return c.err + return true, c.err } func (c *templateContext) applyTransformationsToNodes(nodes ...parse.Node) { @@ -229,7 +279,7 @@ func (c *templateContext) hasIdent(idents []string, ident string) bool { // on the form: // {{ $_hugo_config:= `{ "version": 1 }` }} func (c *templateContext) collectConfig(n *parse.PipeNode) { - if !c.isShortcode { + if c.typ != templateShortcode { return } if c.configChecked { @@ -271,7 +321,7 @@ func (c *templateContext) collectConfig(n *parse.PipeNode) { // collectInner determines if the given CommandNode represents a // shortcode call to its .Inner. func (c *templateContext) collectInner(n *parse.CommandNode) { - if !c.isShortcode { + if c.typ != templateShortcode { return } if c.Info.IsInner || len(n.Args) == 0 { @@ -295,6 +345,28 @@ func (c *templateContext) collectInner(n *parse.CommandNode) { } +func (c *templateContext) collectReturnNode(n *parse.CommandNode) bool { + if c.typ != templatePartial || c.returnNode != nil { + return true + } + + if len(n.Args) < 2 { + return true + } + + ident, ok := n.Args[0].(*parse.IdentifierNode) + if !ok || ident.Ident != "return" { + return true + } + + c.returnNode = n + // Remove the "return" identifiers + c.returnNode.Args = c.returnNode.Args[1:] + + return false + +} + // indexOfReplacementStart will return the index of where to start doing replacement, // -1 if none needed. func (d decl) indexOfReplacementStart(idents []string) int { |