From 616b3429a0b2e7a7057da6a8e7ba495a846db801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 1 Jul 2020 10:43:17 +0200 Subject: [PATCH] Add support for inline partials Fixes #7444 --- docs/content/en/templates/partials.md | 15 ++++++ hugolib/template_test.go | 36 +++++++++++++ tpl/tplimpl/template.go | 68 +++++++++++++++++++++++- tpl/tplimpl/template_ast_transformers.go | 3 ++ 4 files changed, 121 insertions(+), 1 deletion(-) diff --git a/docs/content/en/templates/partials.md b/docs/content/en/templates/partials.md index 873f5e696ab..d23622deda7 100644 --- a/docs/content/en/templates/partials.md +++ b/docs/content/en/templates/partials.md @@ -81,6 +81,21 @@ This means the partial will *only* be able to access those variables. The partia In addition to outputting markup, partials can be used to return a value of any type. In order to return a value, a partial must include a lone `return` statement. +## Inline partials + +{{< new-in "0.74.0" >}} + +You can also define partials inline in the template. But remember that template namespace is global, so you need to make sure that the names are unique to avoid conflicts. + +```go-html-template +Value: {{ partial "my-inline-partial" . }} + +{{ define "partials/my-inline-partial" }} +{{ $value := 32 }} +{{ return $value }} +{{ end }} +``` + ### Example GetFeatured ```go-html-template {{/* layouts/partials/GetFeatured.html */}} diff --git a/hugolib/template_test.go b/hugolib/template_test.go index 29993120d8b..7d92f4c2ec7 100644 --- a/hugolib/template_test.go +++ b/hugolib/template_test.go @@ -597,3 +597,39 @@ func collectIdentities(set map[identity.Identity]bool, provider identity.Provide func ident(level int) string { return strings.Repeat(" ", level) } + +func TestPartialInline(t *testing.T) { + + b := newTestSitesBuilder(t) + + b.WithContent("p1.md", "") + + b.WithTemplates( + "index.html", ` + +{{ $p1 := partial "p1" . }} +{{ $p2 := partial "p2" . }} + +P1: {{ $p1 }} +P2: {{ $p2 }} + +{{ define "partials/p1" }}Inline: p1{{ end }} + +{{ define "partials/p2" }} +{{ $value := 32 }} +{{ return $value }} +{{ end }} + + +`, + ) + + b.CreateSites().Build(BuildCfg{}) + + b.AssertFileContent("public/index.html", + ` +P1: Inline: p1 +P2: 32`, + ) + +} diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index 81b62b3427f..184eb067e7c 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -720,10 +720,52 @@ func (t *templateHandler) noBaseNeeded(name string) bool { } func (t *templateHandler) postTransform() error { + defineCheckedHTML := false + defineCheckedText := false + for _, v := range t.main.templates { if v.typ == templateShortcode { t.addShortcodeVariant(v) } + + if defineCheckedHTML && defineCheckedText { + continue + } + + isText := isText(v.Template) + if isText { + if defineCheckedText { + continue + } + defineCheckedText = true + } else { + if defineCheckedHTML { + continue + } + defineCheckedHTML = true + } + + templs := templates(v.Template) + for _, templ := range templs { + if templ.Name() == "" || !strings.HasPrefix(templ.Name(), "partials/") { + continue + } + + ts := newTemplateState(templ, templateInfo{name: templ.Name()}) + ts.typ = templatePartial + + if _, found := t.main.templates[templ.Name()]; !found { + // This is a template defined inline. + + _, err := applyTemplateTransformers(ts, t.main.newTemplateLookup(ts)) + if err != nil { + return err + } + t.main.templates[templ.Name()] = ts + + } + + } } for name, source := range t.transformNotFound { @@ -872,8 +914,13 @@ func (t *templateState) ParseInfo() tpl.ParseInfo { } func (t *templateState) isText() bool { - _, isText := t.Template.(*texttemplate.Template) + return isText(t.Template) +} + +func isText(templ tpl.Template) bool { + _, isText := templ.(*texttemplate.Template) return isText + } type templateStateMap struct { @@ -960,3 +1007,22 @@ func unwrap(templ tpl.Template) tpl.Template { } return templ } + +func templates(in tpl.Template) []tpl.Template { + var templs []tpl.Template + in = unwrap(in) + if textt, ok := in.(*texttemplate.Template); ok { + for _, t := range textt.Templates() { + templs = append(templs, t) + } + } + + if htmlt, ok := in.(*htmltemplate.Template); ok { + for _, t := range htmlt.Templates() { + templs = append(templs, t) + } + } + + return templs + +} diff --git a/tpl/tplimpl/template_ast_transformers.go b/tpl/tplimpl/template_ast_transformers.go index 015cf72afd3..a05de489ba6 100644 --- a/tpl/tplimpl/template_ast_transformers.go +++ b/tpl/tplimpl/template_ast_transformers.go @@ -97,9 +97,11 @@ func applyTemplateTransformers( _, err := c.applyTransformations(tree.Root) if err == nil && c.returnNode != nil { + // This is a partial with a return statement. c.t.parseInfo.HasReturn = true tree.Root = c.wrapInPartialReturnWrapper(tree.Root) + } return c, err @@ -179,6 +181,7 @@ func (c *templateContext) applyTransformations(n parse.Node) (bool, error) { case *parse.CommandNode: c.collectPartialInfo(x) c.collectInner(x) + keep := c.collectReturnNode(x) for _, elem := range x.Args {