Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tpl/tplimpl: Handle late transformation of templates #5866

Merged
merged 1 commit into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tpl/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ type TemplateHandler interface {

NewTextTemplate() TemplateParseFinder

MarkReady()
MarkReady() error
RebuildClone()
}

Expand Down
6 changes: 3 additions & 3 deletions tpl/tplimpl/ace.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ func (t *templateHandler) addAceTemplate(name, basePath, innerPath string, baseC

typ := resolveTemplateType(name)

info, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
c, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
if err != nil {
return err
}

if typ == templateShortcode {
t.addShortcodeVariant(name, info, templ)
t.addShortcodeVariant(name, c.Info, templ)
} else {
t.templateInfo[name] = info
t.templateInfo[name] = c.Info
}

return nil
Expand Down
118 changes: 93 additions & 25 deletions tpl/tplimpl/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"html/template"
"strings"
texttemplate "text/template"
"text/template/parse"

"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/tpl/tplimpl/embedded"
Expand Down Expand Up @@ -51,7 +52,6 @@ var (
_ tpl.TemplateFinder = (*textTemplates)(nil)
_ templateLoader = (*htmlTemplates)(nil)
_ templateLoader = (*textTemplates)(nil)
_ templateLoader = (*templateHandler)(nil)
_ templateFuncsterTemplater = (*htmlTemplates)(nil)
_ templateFuncsterTemplater = (*textTemplates)(nil)
)
Expand All @@ -66,7 +66,7 @@ type templateErr struct {

type templateLoader interface {
handleMaster(name, overlayFilename, masterFilename string, onMissing func(filename string) (templateInfo, error)) error
addTemplate(name, tpl string) error
addTemplate(name, tpl string) (*templateContext, error)
addLateTemplate(name, tpl string) error
}

Expand Down Expand Up @@ -329,6 +329,7 @@ func (t *templateHandler) clone(d *deps.Deps) *templateHandler {
func newTemplateAdapter(deps *deps.Deps) *templateHandler {
common := &templatesCommon{
nameBaseTemplateName: make(map[string]string),
transformNotFound: make(map[string]bool),
}

htmlT := &htmlTemplates{
Expand Down Expand Up @@ -364,6 +365,10 @@ type templatesCommon struct {

// Used to get proper filenames in errors
nameBaseTemplateName map[string]string

// Holds names of the templates not found during the first AST transformation
// pass.
transformNotFound map[string]bool
}
type htmlTemplates struct {
mu sync.RWMutex
Expand Down Expand Up @@ -491,37 +496,42 @@ func (t *templateHandler) LoadTemplates(prefix string) error {

}

func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) error {
func (t *htmlTemplates) addTemplateIn(tt *template.Template, name, tpl string) (*templateContext, error) {
t.mu.Lock()
defer t.mu.Unlock()

templ, err := tt.New(name).Parse(tpl)
if err != nil {
return err
return nil, err
}

typ := resolveTemplateType(name)

info, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
c, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
if err != nil {
return err
return nil, err
}

for k, _ := range c.notFound {
t.transformNotFound[k] = true
}

if typ == templateShortcode {
t.handler.addShortcodeVariant(name, info, templ)
t.handler.addShortcodeVariant(name, c.Info, templ)
} else {
t.handler.templateInfo[name] = info
t.handler.templateInfo[name] = c.Info
}

return nil
return c, nil
}

func (t *htmlTemplates) addTemplate(name, tpl string) error {
func (t *htmlTemplates) addTemplate(name, tpl string) (*templateContext, error) {
return t.addTemplateIn(t.t, name, tpl)
}

func (t *htmlTemplates) addLateTemplate(name, tpl string) error {
return t.addTemplateIn(t.clone, name, tpl)
_, err := t.addTemplateIn(t.clone, name, tpl)
return err
}

type textTemplate struct {
Expand Down Expand Up @@ -556,41 +566,91 @@ func (t *textTemplate) parseIn(tt *texttemplate.Template, name, tpl string) (*te
return templ, nil
}

func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) error {
func (t *textTemplates) addTemplateIn(tt *texttemplate.Template, name, tpl string) (*templateContext, error) {
name = strings.TrimPrefix(name, textTmplNamePrefix)
templ, err := t.parseIn(tt, name, tpl)
if err != nil {
return err
return nil, err
}

typ := resolveTemplateType(name)

info, err := applyTemplateTransformersToTextTemplate(typ, templ)
c, err := applyTemplateTransformersToTextTemplate(typ, templ)
if err != nil {
return err
return nil, err
}

for k, _ := range c.notFound {
t.transformNotFound[k] = true
}

if typ == templateShortcode {
t.handler.addShortcodeVariant(name, info, templ)
t.handler.addShortcodeVariant(name, c.Info, templ)
} else {
t.handler.templateInfo[name] = info
t.handler.templateInfo[name] = c.Info
}

return nil
return c, nil
}

func (t *textTemplates) addTemplate(name, tpl string) error {
func (t *textTemplates) addTemplate(name, tpl string) (*templateContext, error) {
return t.addTemplateIn(t.t, name, tpl)
}

func (t *textTemplates) addLateTemplate(name, tpl string) error {
return t.addTemplateIn(t.clone, name, tpl)
_, err := t.addTemplateIn(t.clone, name, tpl)
return err
}

func (t *templateHandler) addTemplate(name, tpl string) error {
return t.AddTemplate(name, tpl)
}

func (t *templateHandler) postTransform() error {
if len(t.html.transformNotFound) == 0 && len(t.text.transformNotFound) == 0 {
return nil
}

defer func() {
t.text.transformNotFound = make(map[string]bool)
t.html.transformNotFound = make(map[string]bool)
}()

for _, s := range []struct {
lookup func(name string) *parse.Tree
transformNotFound map[string]bool
}{
// html templates
{func(name string) *parse.Tree {
templ := t.html.lookup(name)
if templ == nil {
return nil
}
return templ.Tree
}, t.html.transformNotFound},
// text templates
{func(name string) *parse.Tree {
templT := t.text.lookup(name)
if templT == nil {
return nil
}
return templT.Tree
}, t.text.transformNotFound},
} {
for name, _ := range s.transformNotFound {
templ := s.lookup(name)
if templ != nil {
_, err := applyTemplateTransformers(templateUndefined, templ, s.lookup)
if err != nil {
return err
}
}
}
}

return nil
}

func (t *templateHandler) addLateTemplate(name, tpl string) error {
return t.AddLateTemplate(name, tpl)
}
Expand All @@ -608,9 +668,11 @@ func (t *templateHandler) AddLateTemplate(name, tpl string) error {
// AddTemplate parses and adds a template to the collection.
// Templates with name prefixed with "_text" will be handled as plain
// text templates.
// TODO(bep) clean up these addTemplate variants
func (t *templateHandler) AddTemplate(name, tpl string) error {
h := t.getTemplateHandler(name)
if err := h.addTemplate(name, tpl); err != nil {
_, err := h.addTemplate(name, tpl)
if err != nil {
return err
}
return nil
Expand All @@ -620,7 +682,11 @@ func (t *templateHandler) AddTemplate(name, tpl string) error {
// after this is set.
// TODO(bep) if this proves to be resource heavy, we could detect
// earlier if we really need this, or make it lazy.
func (t *templateHandler) MarkReady() {
func (t *templateHandler) MarkReady() error {
if err := t.postTransform(); err != nil {
return err
}

if t.html.clone == nil {
t.html.clone = template.Must(t.html.t.Clone())
t.html.cloneClone = template.Must(t.html.clone.Clone())
Expand All @@ -629,6 +695,8 @@ func (t *templateHandler) MarkReady() {
t.text.clone = texttemplate.Must(t.text.t.Clone())
t.text.cloneClone = texttemplate.Must(t.text.clone.Clone())
}

return nil
}

// RebuildClone rebuilds the cloned templates. Used for live-reloads.
Expand Down Expand Up @@ -890,15 +958,15 @@ func (t *templateHandler) addTemplateFile(name, baseTemplatePath, path string) e

typ := resolveTemplateType(name)

info, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
c, err := applyTemplateTransformersToHMLTTemplate(typ, templ)
if err != nil {
return err
}

if typ == templateShortcode {
t.addShortcodeVariant(templateName, info, templ)
t.addShortcodeVariant(templateName, c.Info, templ)
} else {
t.templateInfo[name] = info
t.templateInfo[name] = c.Info
}

return nil
Expand Down
7 changes: 2 additions & 5 deletions tpl/tplimpl/templateProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ func (*TemplateProvider) Update(deps *deps.Deps) error {

}

newTmpl.MarkReady()

return nil
return newTmpl.MarkReady()

}

Expand All @@ -60,7 +58,6 @@ func (*TemplateProvider) Clone(d *deps.Deps) error {

d.Tmpl = clone

clone.MarkReady()
return clone.MarkReady()

return nil
}
25 changes: 17 additions & 8 deletions tpl/tplimpl/template_ast_transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const (
type templateContext struct {
decl decl
visited map[string]bool
notFound map[string]bool
lookupFn func(name string) *parse.Tree

// The last error encountered.
Expand All @@ -72,16 +73,24 @@ func (c templateContext) getIfNotVisited(name string) *parse.Tree {
return nil
}
c.visited[name] = true
return c.lookupFn(name)
templ := c.lookupFn(name)
if templ == nil {
// This may be a inline template defined outside of this file
// and not yet parsed. Unusual, but it happens.
// Store the name to try again later.
c.notFound[name] = true
}

return templ
}

func newTemplateContext(lookupFn func(name string) *parse.Tree) *templateContext {
return &templateContext{
Info: tpl.Info{Config: tpl.DefaultConfig},
lookupFn: lookupFn,
decl: make(map[string]string),
visited: make(map[string]bool)}

visited: make(map[string]bool),
notFound: make(map[string]bool)}
}

func createParseTreeLookup(templ *template.Template) func(nn string) *parse.Tree {
Expand All @@ -94,11 +103,11 @@ func createParseTreeLookup(templ *template.Template) func(nn string) *parse.Tree
}
}

func applyTemplateTransformersToHMLTTemplate(typ templateType, templ *template.Template) (tpl.Info, error) {
func applyTemplateTransformersToHMLTTemplate(typ templateType, templ *template.Template) (*templateContext, error) {
return applyTemplateTransformers(typ, templ.Tree, createParseTreeLookup(templ))
}

func applyTemplateTransformersToTextTemplate(typ templateType, templ *texttemplate.Template) (tpl.Info, error) {
func applyTemplateTransformersToTextTemplate(typ templateType, templ *texttemplate.Template) (*templateContext, error) {
return applyTemplateTransformers(typ, templ.Tree,
func(nn string) *parse.Tree {
tt := templ.Lookup(nn)
Expand All @@ -109,9 +118,9 @@ func applyTemplateTransformersToTextTemplate(typ templateType, templ *texttempla
})
}

func applyTemplateTransformers(typ templateType, 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) (*templateContext, error) {
if templ == nil {
return tpl.Info{}, errors.New("expected template, but none provided")
return nil, errors.New("expected template, but none provided")
}

c := newTemplateContext(lookupFn)
Expand All @@ -125,7 +134,7 @@ func applyTemplateTransformers(typ templateType, templ *parse.Tree, lookupFn fun
templ.Root = c.wrapInPartialReturnWrapper(templ.Root)
}

return c.Info, err
return c, err
}

const (
Expand Down
Loading