Skip to content

Commit 7c19c19

Browse files
committed
Allow partials to work as decorators
Fixes #13193
1 parent 555443b commit 7c19c19

File tree

18 files changed

+774
-47
lines changed

18 files changed

+774
-47
lines changed

‎cache/dynacache/dynacache.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func New(opts Options) *Cache {
6969

7070
infol := opts.Log.InfoCommand("dynacache")
7171

72-
evictedIdentities := collections.NewStack[KeyIdentity]()
72+
evictedIdentities := collections.NewStackThreadSafe[KeyIdentity]()
7373

7474
onEvict := func(k, v any) {
7575
if !opts.Watching {
@@ -129,7 +129,7 @@ type Cache struct {
129129
partitions map[string]PartitionManager
130130

131131
onEvict func(k, v any)
132-
evictedIdentities *collections.Stack[KeyIdentity]
132+
evictedIdentities *collections.StackThreadSafe[KeyIdentity]
133133

134134
opts Options
135135
infol logg.LevelLogger

‎common/collections/stack.go‎

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,24 @@ import (
2121
"github.com/gohugoio/hugo/common/hiter"
2222
)
2323

24-
// Stack is a simple LIFO stack that is safe for concurrent use.
25-
type Stack[T any] struct {
24+
// StackThreadSafe is a simple LIFO stack that is safe for concurrent use.
25+
type StackThreadSafe[T any] struct {
2626
items []T
2727
zero T
2828
mu sync.RWMutex
2929
}
3030

31-
func NewStack[T any]() *Stack[T] {
32-
return &Stack[T]{}
31+
func NewStackThreadSafe[T any]() *StackThreadSafe[T] {
32+
return &StackThreadSafe[T]{}
3333
}
3434

35-
func (s *Stack[T]) Push(item T) {
35+
func (s *StackThreadSafe[T]) Push(item T) {
3636
s.mu.Lock()
3737
defer s.mu.Unlock()
3838
s.items = append(s.items, item)
3939
}
4040

41-
func (s *Stack[T]) Pop() (T, bool) {
41+
func (s *StackThreadSafe[T]) Pop() (T, bool) {
4242
s.mu.Lock()
4343
defer s.mu.Unlock()
4444
if len(s.items) == 0 {
@@ -49,7 +49,7 @@ func (s *Stack[T]) Pop() (T, bool) {
4949
return item, true
5050
}
5151

52-
func (s *Stack[T]) Peek() (T, bool) {
52+
func (s *StackThreadSafe[T]) Peek() (T, bool) {
5353
s.mu.RLock()
5454
defer s.mu.RUnlock()
5555
if len(s.items) == 0 {
@@ -58,26 +58,26 @@ func (s *Stack[T]) Peek() (T, bool) {
5858
return s.items[len(s.items)-1], true
5959
}
6060

61-
func (s *Stack[T]) Len() int {
61+
func (s *StackThreadSafe[T]) Len() int {
6262
s.mu.RLock()
6363
defer s.mu.RUnlock()
6464
return len(s.items)
6565
}
6666

6767
// All returns all items in the stack, from bottom to top.
68-
func (s *Stack[T]) All() iter.Seq2[int, T] {
68+
func (s *StackThreadSafe[T]) All() iter.Seq2[int, T] {
6969
return hiter.Lock2(slices.All(s.items), s.mu.RLock, s.mu.RUnlock)
7070
}
7171

72-
func (s *Stack[T]) Drain() []T {
72+
func (s *StackThreadSafe[T]) Drain() []T {
7373
s.mu.Lock()
7474
defer s.mu.Unlock()
7575
items := s.items
7676
s.items = nil
7777
return items
7878
}
7979

80-
func (s *Stack[T]) DrainMatching(predicate func(T) bool) []T {
80+
func (s *StackThreadSafe[T]) DrainMatching(predicate func(T) bool) []T {
8181
s.mu.Lock()
8282
defer s.mu.Unlock()
8383
var items []T
@@ -89,3 +89,47 @@ func (s *Stack[T]) DrainMatching(predicate func(T) bool) []T {
8989
}
9090
return items
9191
}
92+
93+
// Stack is a simple LIFO stack that is not safe for concurrent use.
94+
type Stack[T any] struct {
95+
items []T
96+
zero T
97+
}
98+
99+
func NewStack[T any]() *Stack[T] {
100+
return &Stack[T]{}
101+
}
102+
103+
func (s *Stack[T]) Push(item T) {
104+
s.items = append(s.items, item)
105+
}
106+
107+
func (s *Stack[T]) Pop() (T, bool) {
108+
if len(s.items) == 0 {
109+
return s.zero, false
110+
}
111+
item := s.items[len(s.items)-1]
112+
s.items = s.items[:len(s.items)-1]
113+
return item, true
114+
}
115+
116+
func (s *Stack[T]) Peek() (T, bool) {
117+
if len(s.items) == 0 {
118+
return s.zero, false
119+
}
120+
return s.items[len(s.items)-1], true
121+
}
122+
123+
func (s *Stack[T]) Len() int {
124+
return len(s.items)
125+
}
126+
127+
func (s *Stack[T]) All() iter.Seq2[int, T] {
128+
return slices.All(s.items)
129+
}
130+
131+
func (s *Stack[T]) Drain() []T {
132+
items := s.items
133+
s.items = nil
134+
return items
135+
}

‎common/collections/stack_test.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func TestNewStack(t *testing.T) {
1010
t.Parallel()
1111
c := qt.New(t)
1212

13-
s := NewStack[int]()
13+
s := NewStackThreadSafe[int]()
1414

1515
c.Assert(s, qt.IsNotNil)
1616
}
@@ -19,7 +19,7 @@ func TestStackBasic(t *testing.T) {
1919
t.Parallel()
2020
c := qt.New(t)
2121

22-
s := NewStack[int]()
22+
s := NewStackThreadSafe[int]()
2323

2424
c.Assert(s.Len(), qt.Equals, 0)
2525

@@ -50,7 +50,7 @@ func TestStackDrain(t *testing.T) {
5050
t.Parallel()
5151
c := qt.New(t)
5252

53-
s := NewStack[string]()
53+
s := NewStackThreadSafe[string]()
5454
s.Push("a")
5555
s.Push("b")
5656

@@ -64,7 +64,7 @@ func TestStackDrainMatching(t *testing.T) {
6464
t.Parallel()
6565
c := qt.New(t)
6666

67-
s := NewStack[int]()
67+
s := NewStackThreadSafe[int]()
6868
s.Push(1)
6969
s.Push(2)
7070
s.Push(3)

‎common/maps/map.go‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ func (m *Map[K, T]) Set(key K, value T) {
7373
m.mu.Unlock()
7474
}
7575

76+
// Delete deletes the given key from the map.
77+
// It returns true if the key was found and deleted, false otherwise.
78+
func (m *Map[K, T]) Delete(key K) bool {
79+
m.mu.Lock()
80+
defer m.mu.Unlock()
81+
if _, found := m.m[key]; found {
82+
delete(m.m, key)
83+
return true
84+
}
85+
return false
86+
}
87+
7688
// WithWriteLock executes the given function with a write lock on the map.
7789
func (m *Map[K, T]) WithWriteLock(f func(m map[K]T) error) error {
7890
m.mu.Lock()

‎hugolib/alias.go‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"github.com/gohugoio/hugo/output"
3030
"github.com/gohugoio/hugo/publisher"
3131
"github.com/gohugoio/hugo/resources/page"
32-
"github.com/gohugoio/hugo/tpl"
3332
"github.com/gohugoio/hugo/tpl/tplimpl"
3433
)
3534

@@ -76,7 +75,7 @@ func (a aliasHandler) renderAlias(permalink string, p page.Page, matrix sitesmat
7675
p,
7776
}
7877

79-
ctx := tpl.Context.Page.Set(context.Background(), p)
78+
ctx := a.ts.PrepareTopLevelRenderCtx(context.Background(), p)
8079

8180
buffer := new(bytes.Buffer)
8281
err := a.ts.ExecuteWithContext(ctx, t, buffer, data)

‎hugolib/doctree/simpletree.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ func (tree *SimpleTree[T]) All() iter.Seq2[string, T] {
117117
}
118118
}
119119

120+
func (tree *SimpleTree[T]) Len() int {
121+
return tree.tree.Len()
122+
}
123+
120124
// NewSimpleThreadSafeTree creates a new SimpleTree.
121125
func NewSimpleThreadSafeTree[T any]() *SimpleThreadSafeTree[T] {
122126
return &SimpleThreadSafeTree[T]{tree: radix.New[T](), mu: new(sync.RWMutex)}

‎hugolib/doctree/support.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ type WalkContext[T any] struct {
224224
events []*Event[T]
225225

226226
hooksPostInit sync.Once
227-
hooksPost *collections.Stack[func() error]
227+
hooksPost *collections.StackThreadSafe[func() error]
228228
}
229229

230230
type eventHandlers[T any] map[string][]func(*Event[T])
@@ -261,9 +261,9 @@ func (ctx *WalkContext[T]) HandleEvents() error {
261261
return nil
262262
}
263263

264-
func (ctx *WalkContext[T]) HooksPost() *collections.Stack[func() error] {
264+
func (ctx *WalkContext[T]) HooksPost() *collections.StackThreadSafe[func() error] {
265265
ctx.hooksPostInit.Do(func() {
266-
ctx.hooksPost = collections.NewStack[func() error]()
266+
ctx.hooksPost = collections.NewStackThreadSafe[func() error]()
267267
})
268268
return ctx.hooksPost
269269
}

‎hugolib/integrationtest_builder.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"testing"
2020

2121
"github.com/bep/logg"
22+
"github.com/yuin/goldmark/util"
2223

2324
qt "github.com/frankban/quicktest"
2425
"github.com/fsnotify/fsnotify"
@@ -419,7 +420,7 @@ func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...s
419420
content := strings.TrimSpace(s.FileContent(filename))
420421

421422
for _, m := range matches {
422-
cm := qt.Commentf("File: %s Expect: %s Got: %s", filename, m, content)
423+
cm := qt.Commentf("File: %s Expect:\n%s Got:\n%s\nWith Space Visuals:\n%s", filename, m, content, util.VisualizeSpaces([]byte(content)))
423424
lines := strings.SplitSeq(m, "\n")
424425
for match := range lines {
425426
match = strings.TrimSpace(match)

‎hugolib/site.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,7 @@ func (s *Site) renderAndWritePage(statCounter *uint64, targetPath string, p *pag
16101610
of := p.outputFormat()
16111611
p.incrRenderState()
16121612

1613-
ctx := tpl.Context.Page.Set(context.Background(), p)
1613+
ctx := s.TemplateStore.PrepareTopLevelRenderCtx(context.Background(), p)
16141614
ctx = tpl.Context.DependencyManagerScopedProvider.Set(ctx, p)
16151615

16161616
if err := s.renderForTemplate(ctx, p.Kind(), of.Name, d, renderBuffer, templ); err != nil {

‎tpl/partials/partials.go‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ func (ns *Namespace) lookup(name string) (*tplimpl.TemplInfo, error) {
145145
// include is a helper function that lookups and executes the named partial.
146146
// Returns the final template name and the rendered output.
147147
func (ns *Namespace) doInclude(ctx context.Context, key string, templ *tplimpl.TemplInfo, dataList ...any) includeResult {
148+
if templ.ParseInfo.HasPartialInner {
149+
stack := tpl.Context.PartialDecoratorIDStack.Get(ctx)
150+
if stack != nil {
151+
if id, ok := stack.Peek(); ok {
152+
// Signal that inner exists.
153+
id.Bool = true
154+
}
155+
}
156+
}
148157
var data any
149158
if len(dataList) > 0 {
150159
data = dataList[0]

0 commit comments

Comments
 (0)