Skip to content

Commit

Permalink
tpl/tplimpl: Handle late transformation of templates
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Apr 16, 2019
1 parent 56550d1 commit 3374c34
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 51 deletions.
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
116 changes: 91 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,41 @@ 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 +565,90 @@ 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.html.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 +666,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 +680,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 +693,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 +956,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

0 comments on commit 3374c34

Please sign in to comment.