Skip to content

Commit d4c6dd1

Browse files
committed
tpl: Add templates.Current
This commit also * Unexport all internal state in TemplateInfo. * Make the dispatcher keys used for passing context.Context into uint8 from string to save memory allocations. Co-authored-by: Joe Mooring <[email protected]> Updates #13571
1 parent af0602c commit d4c6dd1

File tree

13 files changed

+324
-125
lines changed

13 files changed

+324
-125
lines changed

Diff for: common/hugo/hugo.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,13 @@ func (i HugoInfo) IsMultilingual() bool {
139139
return i.conf.IsMultilingual()
140140
}
141141

142-
type contextKey string
142+
type contextKey uint8
143143

144-
var markupScope = hcontext.NewContextDispatcher[string](contextKey("markupScope"))
144+
const (
145+
contextKeyMarkupScope contextKey = iota
146+
)
147+
148+
var markupScope = hcontext.NewContextDispatcher[string](contextKeyMarkupScope)
145149

146150
type Context struct{}
147151

Diff for: hugolib/page__content.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,13 @@ func (c *cachedContentScope) mustContentToC(ctx context.Context) contentTableOfC
667667
return ct
668668
}
669669

670-
var setGetContentCallbackInContext = hcontext.NewContextDispatcher[func(*pageContentOutput, contentTableOfContents)]("contentCallback")
670+
type contextKey uint8
671+
672+
const (
673+
contextKeyContentCallback contextKey = iota
674+
)
675+
676+
var setGetContentCallbackInContext = hcontext.NewContextDispatcher[func(*pageContentOutput, contentTableOfContents)](contextKeyContentCallback)
671677

672678
func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfContents, error) {
673679
cp := c.pco

Diff for: hugolib/page__per_output.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func (pco *pageContentOutput) Render(ctx context.Context, layout ...string) (tem
120120
// Make sure to send the *pageState and not the *pageContentOutput to the template.
121121
res, err := executeToString(ctx, pco.po.p.s.GetTemplateStore(), templ, pco.po.p)
122122
if err != nil {
123-
return "", pco.po.p.wrapError(fmt.Errorf("failed to execute template %s: %w", templ.Template.Name(), err))
123+
return "", pco.po.p.wrapError(fmt.Errorf("failed to execute template %s: %w", templ.Name(), err))
124124
}
125125
return template.HTML(res), nil
126126
}
@@ -323,7 +323,7 @@ func (pco *pageContentOutput) initRenderHooks() error {
323323
return false
324324
}
325325

326-
if ignoreInternal && candidate.SubCategory == tplimpl.SubCategoryEmbedded {
326+
if ignoreInternal && candidate.SubCategory() == tplimpl.SubCategoryEmbedded {
327327
// Don't consider the internal hook templates.
328328
return false
329329
}

Diff for: hugolib/site_render.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func pageRenderer(
168168

169169
s.Log.Trace(
170170
func() string {
171-
return fmt.Sprintf("rendering outputFormat %q kind %q using layout %q to %q", p.pageOutput.f.Name, p.Kind(), templ.Template.Name(), targetPath)
171+
return fmt.Sprintf("rendering outputFormat %q kind %q using layout %q to %q", p.pageOutput.f.Name, p.Kind(), templ.Name(), targetPath)
172172
},
173173
)
174174

Diff for: resources/resource_factories/create/create.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ type Client struct {
5555
remoteResourceLogger logg.LevelLogger
5656
}
5757

58-
type contextKey string
58+
type contextKey uint8
59+
60+
const (
61+
contextKeyResourceID contextKey = iota
62+
)
5963

6064
// New creates a new Client with the given specification.
6165
func New(rs *resources.Spec) *Client {
6266
fileCache := rs.FileCaches.GetResourceCache()
63-
resourceIDDispatcher := hcontext.NewContextDispatcher[string](contextKey("resourceID"))
67+
resourceIDDispatcher := hcontext.NewContextDispatcher[string](contextKeyResourceID)
6468
httpCacheConfig := rs.Cfg.GetConfigSection("httpCacheCompiled").(hhttpcache.ConfigCompiled)
6569
var remoteResourceChecker *tasks.RunEvery
6670
if rs.Cfg.Watching() && !httpCacheConfig.IsPollingDisabled() {

Diff for: tpl/partials/partials.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ func (ns *Namespace) include(ctx context.Context, name string, dataList ...any)
157157
if len(dataList) > 0 {
158158
data = dataList[0]
159159
}
160-
name, desc := ns.deps.TemplateStore.TemplateDescriptorFromPath(name)
161-
v := ns.deps.TemplateStore.LookupPartial(name, desc)
160+
v := ns.deps.TemplateStore.LookupPartial(name)
162161
if v == nil {
163162
return includeResult{err: fmt.Errorf("partial %q not found", name)}
164163
}
@@ -199,7 +198,7 @@ func (ns *Namespace) include(ctx context.Context, name string, dataList ...any)
199198
}
200199

201200
return includeResult{
202-
name: templ.Template.Name(),
201+
name: templ.Name(),
203202
result: result,
204203
}
205204
}

Diff for: tpl/template.go

+61-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package tpl
1616

1717
import (
1818
"context"
19+
"slices"
1920
"strings"
2021
"sync"
2122
"unicode"
@@ -41,7 +42,17 @@ type RenderingContext struct {
4142
SiteOutIdx int
4243
}
4344

44-
type contextKey string
45+
type (
46+
contextKey uint8
47+
)
48+
49+
const (
50+
contextKeyDependencyManagerScopedProvider contextKey = iota
51+
contextKeyDependencyScope
52+
contextKeyPage
53+
contextKeyIsInGoldmark
54+
cntextKeyCurrentTemplateInfo
55+
)
4556

4657
// Context manages values passed in the context to templates.
4758
var Context = struct {
@@ -50,11 +61,13 @@ var Context = struct {
5061
DependencyScope hcontext.ContextDispatcher[int]
5162
Page hcontext.ContextDispatcher[page]
5263
IsInGoldmark hcontext.ContextDispatcher[bool]
64+
CurrentTemplate hcontext.ContextDispatcher[*CurrentTemplateInfo]
5365
}{
54-
DependencyManagerScopedProvider: hcontext.NewContextDispatcher[identity.DependencyManagerScopedProvider](contextKey("DependencyManagerScopedProvider")),
55-
DependencyScope: hcontext.NewContextDispatcher[int](contextKey("DependencyScope")),
56-
Page: hcontext.NewContextDispatcher[page](contextKey("Page")),
57-
IsInGoldmark: hcontext.NewContextDispatcher[bool](contextKey("IsInGoldmark")),
66+
DependencyManagerScopedProvider: hcontext.NewContextDispatcher[identity.DependencyManagerScopedProvider](contextKeyDependencyManagerScopedProvider),
67+
DependencyScope: hcontext.NewContextDispatcher[int](contextKeyDependencyScope),
68+
Page: hcontext.NewContextDispatcher[page](contextKeyPage),
69+
IsInGoldmark: hcontext.NewContextDispatcher[bool](contextKeyIsInGoldmark),
70+
CurrentTemplate: hcontext.NewContextDispatcher[*CurrentTemplateInfo](cntextKeyCurrentTemplateInfo),
5871
}
5972

6073
func init() {
@@ -130,3 +143,46 @@ type DeferredExecution struct {
130143
Executed bool
131144
Result string
132145
}
146+
147+
type CurrentTemplateInfoOps interface {
148+
CurrentTemplateInfoCommonOps
149+
Base() CurrentTemplateInfoCommonOps
150+
}
151+
152+
type CurrentTemplateInfoCommonOps interface {
153+
// Template name.
154+
Name() string
155+
// Template source filename.
156+
// Will be empty for internal templates.
157+
Filename() string
158+
}
159+
160+
// CurrentTemplateInfo as returned in templates.Current.
161+
type CurrentTemplateInfo struct {
162+
Parent *CurrentTemplateInfo
163+
CurrentTemplateInfoOps
164+
}
165+
166+
// CurrentTemplateInfos is a slice of CurrentTemplateInfo.
167+
type CurrentTemplateInfos []*CurrentTemplateInfo
168+
169+
// Reverse creates a copy of the slice and reverses it.
170+
func (c CurrentTemplateInfos) Reverse() CurrentTemplateInfos {
171+
if len(c) == 0 {
172+
return c
173+
}
174+
r := make(CurrentTemplateInfos, len(c))
175+
copy(r, c)
176+
slices.Reverse(r)
177+
return r
178+
}
179+
180+
// Ancestors returns the ancestors of the current template.
181+
func (ti *CurrentTemplateInfo) Ancestors() CurrentTemplateInfos {
182+
var ancestors []*CurrentTemplateInfo
183+
for ti.Parent != nil {
184+
ti = ti.Parent
185+
ancestors = append(ancestors, ti)
186+
}
187+
return ancestors
188+
}

Diff for: tpl/templates/templates.go

+5
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,8 @@ func (ns *Namespace) DoDefer(ctx context.Context, id string, optsv any) string {
102102

103103
return id
104104
}
105+
106+
// Get information about the currently executing template.
107+
func (ns *Namespace) Current(ctx context.Context) *tpl.CurrentTemplateInfo {
108+
return tpl.Context.CurrentTemplate.Get(ctx)
109+
}

Diff for: tpl/templates/templates_integration_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package templates_test
1515

1616
import (
17+
"path/filepath"
1718
"testing"
1819

1920
"github.com/gohugoio/hugo/hugolib"
@@ -127,3 +128,41 @@ Try printf: {{ (try (printf "hello %s" "world")).Value }}
127128
"Try printf: hello world",
128129
)
129130
}
131+
132+
func TestTemplatesCurrent(t *testing.T) {
133+
t.Parallel()
134+
135+
files := `
136+
-- hugo.toml --
137+
-- layouts/baseof.html --
138+
baseof: {{ block "main" . }}{{ end }}
139+
-- layouts/all.html --
140+
{{ define "main" }}
141+
all.current: {{ templates.Current.Name }}
142+
all.current.filename: {{ templates.Current.Filename }}
143+
all.base: {{ with templates.Current.Base }}{{ .Name }}{{ end }}|
144+
all.parent: {{ with .Parent }}Name: {{ .Name }}{{ end }}|
145+
{{ partial "p1.html" . }}
146+
{{ end }}
147+
-- layouts/_partials/p1.html --
148+
p1.current: {{ with templates.Current }}Name: {{ .Name }}|{{ with .Parent }}Parent.Name: {{ .Name }}{{ end }}{{ end }}|
149+
p1.current.Ancestors: {{ with templates.Current }}{{ range .Ancestors }}{{ .Name }}|{{ end }}{{ end }}
150+
{{ partial "p2.html" . }}
151+
-- layouts/_partials/p2.html --
152+
p2.current: {{ with templates.Current }}Name: {{ .Name }}|{{ with .Parent }}Parent.Name: {{ .Name }}{{ end }}{{ end }}|
153+
p2.current.Ancestors: {{ with templates.Current }}{{ range .Ancestors }}{{ .Name }}|{{ end }}{{ end }}
154+
p3.current.Ancestors.Reverse: {{ with templates.Current }}{{ range .Ancestors.Reverse }}{{ .Name }}|{{ end }}{{ end }}
155+
156+
`
157+
b := hugolib.Test(t, files)
158+
159+
b.AssertFileContent("public/index.html",
160+
"all.current: all.html",
161+
filepath.FromSlash("all.current.filename: /layouts/all.html"),
162+
"all.base: baseof.html",
163+
"all.parent: |",
164+
"p1.current: Name: _partials/p1.html|Parent.Name: all.html|",
165+
"p1.current.Ancestors: all.html|",
166+
"p2.current.Ancestors: _partials/p1.html|all.html",
167+
)
168+
}

Diff for: tpl/tplimpl/templates.go

+16-16
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ func (t *templateNamespace) readTemplateInto(templ *TemplInfo) error {
2525
if err != nil {
2626
return err
2727
}
28-
templ.Content = removeLeadingBOM(string(b))
29-
if !templ.NoBaseOf {
30-
templ.NoBaseOf = !needsBaseTemplate(templ.Content)
28+
templ.content = removeLeadingBOM(string(b))
29+
if !templ.noBaseOf {
30+
templ.noBaseOf = !needsBaseTemplate(templ.content)
3131
}
3232
return nil
3333
}(); err != nil {
@@ -43,7 +43,7 @@ var embeddedTemplatesAliases = map[string][]string{
4343
}
4444

4545
func (t *templateNamespace) parseTemplate(ti *TemplInfo) error {
46-
if !ti.NoBaseOf || ti.Category == CategoryBaseof {
46+
if !ti.noBaseOf || ti.category == CategoryBaseof {
4747
// Delay parsing until we have the base template.
4848
return nil
4949
}
@@ -62,18 +62,18 @@ func (t *templateNamespace) parseTemplate(ti *TemplInfo) error {
6262

6363
if ti.D.IsPlainText {
6464
prototype := t.parseText
65-
templ, err = prototype.New(name).Parse(ti.Content)
65+
templ, err = prototype.New(name).Parse(ti.content)
6666
if err != nil {
6767
return err
6868
}
6969
} else {
7070
prototype := t.parseHTML
71-
templ, err = prototype.New(name).Parse(ti.Content)
71+
templ, err = prototype.New(name).Parse(ti.content)
7272
if err != nil {
7373
return err
7474
}
7575

76-
if ti.SubCategory == SubCategoryEmbedded {
76+
if ti.subCategory == SubCategoryEmbedded {
7777
// In Hugo 0.146.0 we moved the internal templates around.
7878
// For the "_internal/twitter_cards.html" style templates, they
7979
// were moved to the _partials directory.
@@ -111,17 +111,17 @@ func (t *templateNamespace) applyBaseTemplate(overlay *TemplInfo, base keyTempla
111111
Base: base.Info,
112112
}
113113

114-
base.Info.Overlays = append(base.Info.Overlays, overlay)
114+
base.Info.overlays = append(base.Info.overlays, overlay)
115115

116116
var templ tpl.Template
117117
if overlay.D.IsPlainText {
118118
tt := texttemplate.Must(t.parseText.Clone()).New(overlay.PathInfo.PathNoLeadingSlash())
119119
var err error
120-
tt, err = tt.Parse(base.Info.Content)
120+
tt, err = tt.Parse(base.Info.content)
121121
if err != nil {
122122
return err
123123
}
124-
tt, err = tt.Parse(overlay.Content)
124+
tt, err = tt.Parse(overlay.content)
125125
if err != nil {
126126
return err
127127
}
@@ -130,11 +130,11 @@ func (t *templateNamespace) applyBaseTemplate(overlay *TemplInfo, base keyTempla
130130
} else {
131131
tt := htmltemplate.Must(t.parseHTML.CloneShallow()).New(overlay.PathInfo.PathNoLeadingSlash())
132132
var err error
133-
tt, err = tt.Parse(base.Info.Content)
133+
tt, err = tt.Parse(base.Info.content)
134134
if err != nil {
135135
return err
136136
}
137-
tt, err = tt.Parse(overlay.Content)
137+
tt, err = tt.Parse(overlay.content)
138138
if err != nil {
139139
return err
140140
}
@@ -146,17 +146,17 @@ func (t *templateNamespace) applyBaseTemplate(overlay *TemplInfo, base keyTempla
146146

147147
tb.Template = &TemplInfo{
148148
Template: templ,
149-
Base: base.Info,
149+
base: base.Info,
150150
PathInfo: overlay.PathInfo,
151151
Fi: overlay.Fi,
152152
D: overlay.D,
153-
NoBaseOf: true,
153+
noBaseOf: true,
154154
}
155155

156-
variants := overlay.BaseVariants.Get(base.Key)
156+
variants := overlay.baseVariants.Get(base.Key)
157157
if variants == nil {
158158
variants = make(map[TemplateDescriptor]*TemplWithBaseApplied)
159-
overlay.BaseVariants.Insert(base.Key, variants)
159+
overlay.baseVariants.Insert(base.Key, variants)
160160
}
161161
variants[base.Info.D] = tb
162162
return nil

0 commit comments

Comments
 (0)