From 1cf29200b4bb0a9c006155ec76759b7f4b1ad925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sat, 15 Apr 2017 11:33:53 +0200 Subject: [PATCH] tplimpl: Allow text partials in HTML templates Most obvius benefit of this is to include CSS partials with css file suffix into HTML templates. A valid workaround would be to rename the file `mystyles.html`, but that doesn't work too good for external editors etc. The css partial is a method used in some themes before Hugo 0.20, but then it stopped working. This commit reintroduces that behaviour. Note that the regular layout lookups for text templates, i.e. "single.json" will be prefixed with "_text/" on lookup and will only match in the text collection. Fixes #3273 --- tpl/tplimpl/template.go | 23 +++++++++++------------ tpl/tplimpl/templateFuncster.go | 18 ++++++------------ tpl/tplimpl/template_funcs.go | 2 +- tpl/tplimpl/template_funcs_test.go | 27 +++++++++++++++++++-------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go index f1ab37ee0..c86f4cf1e 100644 --- a/tpl/tplimpl/template.go +++ b/tpl/tplimpl/template.go @@ -97,23 +97,21 @@ func (t *templateHandler) PrintErrors() { // Lookup tries to find a template with the given name in both template // collections: First HTML, then the plain text template collection. func (t *templateHandler) Lookup(name string) *tpl.TemplateAdapter { - var te *tpl.TemplateAdapter - isTextTemplate := strings.HasPrefix(name, textTmplNamePrefix) - - if isTextTemplate { + if strings.HasPrefix(name, textTmplNamePrefix) { + // The caller has explicitly asked for a text template, so only look + // in the text template collection. // The templates are stored without the prefix identificator. name = strings.TrimPrefix(name, textTmplNamePrefix) - te = t.text.Lookup(name) - } else { - te = t.html.Lookup(name) + return t.text.Lookup(name) } - if te == nil { - return nil + // Look in both + if te := t.html.Lookup(name); te != nil { + return te } - return te + return t.text.Lookup(name) } func (t *templateHandler) clone(d *deps.Deps) *templateHandler { @@ -459,9 +457,10 @@ func (t *templateHandler) loadTemplates(absPath string, prefix string) { func (t *templateHandler) initFuncs() { - // The template funcs need separation between text and html templates. + // Both template types will get their own funcster instance, which + // in the current case contains the same set of funcs. for _, funcsterHolder := range []templateFuncsterTemplater{t.html, t.text} { - funcster := newTemplateFuncster(t.Deps, funcsterHolder) + funcster := newTemplateFuncster(t.Deps) // The URL funcs in the funcMap is somewhat language dependent, // so we need to wait until the language and site config is loaded. diff --git a/tpl/tplimpl/templateFuncster.go b/tpl/tplimpl/templateFuncster.go index ddcafedfd..52e968fdc 100644 --- a/tpl/tplimpl/templateFuncster.go +++ b/tpl/tplimpl/templateFuncster.go @@ -17,6 +17,7 @@ import ( "fmt" "html/template" "strings" + texttemplate "text/template" bp "github.com/spf13/hugo/bufferpool" @@ -31,17 +32,12 @@ type templateFuncster struct { cachedPartials partialCache image *imageHandler - // Make sure each funcster gets its own TemplateFinder to get - // proper text and HTML template separation. - Tmpl templateFuncsterTemplater - *deps.Deps } -func newTemplateFuncster(deps *deps.Deps, t templateFuncsterTemplater) *templateFuncster { +func newTemplateFuncster(deps *deps.Deps) *templateFuncster { return &templateFuncster{ Deps: deps, - Tmpl: t, cachedPartials: partialCache{p: make(map[string]interface{})}, image: &imageHandler{fs: deps.Fs, imageConfigCache: map[string]image.Config{}}, } @@ -75,14 +71,12 @@ func (t *templateFuncster) partial(name string, contextList ...interface{}) (int return "", err } - switch t.Tmpl.(type) { - case *htmlTemplates: - return template.HTML(b.String()), nil - case *textTemplates: + if _, ok := templ.Template.(*texttemplate.Template); ok { return b.String(), nil - default: - panic("Unknown type") } + + return template.HTML(b.String()), nil + } } diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go index 273753388..54cff81c6 100644 --- a/tpl/tplimpl/template_funcs.go +++ b/tpl/tplimpl/template_funcs.go @@ -2221,5 +2221,5 @@ func (t *templateFuncster) initFuncMap() { } t.funcMap = funcMap - t.Tmpl.setFuncs(funcMap) + t.Tmpl.(*templateHandler).setFuncs(funcMap) } diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go index 075581c66..70a0ad5e5 100644 --- a/tpl/tplimpl/template_funcs_test.go +++ b/tpl/tplimpl/template_funcs_test.go @@ -2869,20 +2869,27 @@ func TestPartialHTMLAndText(t *testing.T) { } config.WithTemplate = func(templ tpl.TemplateHandler) error { - if err := templ.AddTemplate("htmlTemplate.html", `HTML Test Partial: {{ partial "test.foo" . -}}`); err != nil { + if err := templ.AddTemplate("htmlTemplate.html", `HTML Test|HTML:{{ partial "test.html" . -}}|Text:{{ partial "test.txt" . }} +CSS plain: +CSS safe: +`); err != nil { return err } - if err := templ.AddTemplate("_text/textTemplate.txt", `Text Test Partial: {{ partial "test.foo" . -}}`); err != nil { + if err := templ.AddTemplate("_text/textTemplate.txt", `Text Test|HTML:{{ partial "test.html" . -}}|Text:{{ partial "test.txt" . }} +CSS plain: `); err != nil { return err } - // Use "foo" here to say that the extension doesn't really matter in this scenario. - // It will look for templates in "partials/test.foo" and "partials/test.foo.html". - if err := templ.AddTemplate("partials/test.foo", "HTML Name: {{ .Name }}"); err != nil { + if err := templ.AddTemplate("partials/test.html", "HTML Name: {{ .Name }}"); err != nil { return err } - if err := templ.AddTemplate("_text/partials/test.foo", "Text Name: {{ .Name }}"); err != nil { + if err := templ.AddTemplate("_text/partials/test.txt", "Text Name: {{ .Name }}"); err != nil { + return err + } + if err := templ.AddTemplate("_text/partials/mystyles.css", + `body { background-color: blue; } +`); err != nil { return err } @@ -2903,8 +2910,12 @@ func TestPartialHTMLAndText(t *testing.T) { resultText, err := templ.ExecuteToString(data) require.NoError(t, err) - require.Contains(t, resultHTML, "HTML Test Partial: HTML Name: a+b+c") - require.Contains(t, resultText, "Text Test Partial: Text Name: a+b+c") + require.Contains(t, resultHTML, "HTML Test|HTML:HTML Name: a+b+c|Text:Text Name: a+b+c") + require.Contains(t, resultHTML, `CSS plain: `) + require.Contains(t, resultHTML, `CSS safe: