Skip to content

Commit ade7ec8

Browse files
committed
Add Page.RenderShortcodes
A layouts/shortcodes/include.html shortcode may look like this: ```html {{ $p := site.GetPage (.Get 0) }} {{ $p.RenderShortcodes }} ``` Fixes #7297
1 parent 8fa8ce3 commit ade7ec8

14 files changed

+434
-89
lines changed

hugolib/page.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ func (pa pageSiteAdapter) GetPage(ref string) (page.Page, error) {
114114
}
115115

116116
type pageState struct {
117+
// Incremented for each new page created.
118+
// Note that this will change between builds for a given Page.
119+
id int
120+
117121
// This slice will be of same length as the number of global slice of output
118122
// formats (for all sites).
119123
pageOutputs []*pageOutput
@@ -772,7 +776,7 @@ Loop:
772776
currShortcode.pos = it.Pos()
773777
currShortcode.length = iter.Current().Pos() - it.Pos()
774778
if currShortcode.placeholder == "" {
775-
currShortcode.placeholder = createShortcodePlaceholder("s", currShortcode.ordinal)
779+
currShortcode.placeholder = createShortcodePlaceholder("s", p.id, currShortcode.ordinal)
776780
}
777781

778782
if currShortcode.name != "" {
@@ -784,7 +788,7 @@ Loop:
784788
currShortcode.params = s
785789
}
786790

787-
currShortcode.placeholder = createShortcodePlaceholder("s", ordinal)
791+
currShortcode.placeholder = createShortcodePlaceholder("s", p.id, ordinal)
788792
ordinal++
789793
s.shortcodes = append(s.shortcodes, currShortcode)
790794

hugolib/page__new.go

+5
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,19 @@ import (
3131
"github.com/gohugoio/hugo/resources/page"
3232
)
3333

34+
var pageIdCounter atomic.Int64
35+
3436
func newPageBase(metaProvider *pageMeta) (*pageState, error) {
3537
if metaProvider.s == nil {
3638
panic("must provide a Site")
3739
}
3840

41+
id := int(pageIdCounter.Add(1))
42+
3943
s := metaProvider.s
4044

4145
ps := &pageState{
46+
id: id,
4247
pageOutput: nopPageOutput,
4348
pageOutputTemplateVariationsState: atomic.NewUint32(0),
4449
pageCommon: &pageCommon{

hugolib/page__output.go

+2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ type pageOutput struct {
8686
page.ContentProvider
8787
page.PageRenderProvider
8888
page.TableOfContentsProvider
89+
page.RenderShortcodesProvider
8990

9091
// May be nil.
9192
cp *pageContentOutput
@@ -99,6 +100,7 @@ func (p *pageOutput) initContentProvider(cp *pageContentOutput) {
99100
p.ContentProvider = cp
100101
p.PageRenderProvider = cp
101102
p.TableOfContentsProvider = cp
103+
p.RenderShortcodesProvider = cp
102104
p.cp = cp
103105

104106
}

hugolib/page__per_output.go

+81
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,30 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
103103
return err
104104
}
105105

106+
ctxCallback := func(cp2 *pageContentOutput) {
107+
cp.p.cmap.hasNonMarkdownShortcode = cp.p.cmap.hasNonMarkdownShortcode || cp2.p.cmap.hasNonMarkdownShortcode
108+
// Merge content placeholders
109+
for k, v := range cp2.contentPlaceholders {
110+
cp.contentPlaceholders[k] = v
111+
}
112+
113+
if p.s.watching() {
114+
for _, s := range cp2.p.shortcodeState.shortcodes {
115+
for _, templ := range s.templs {
116+
dependencyTracker.Add(templ.(identity.Manager))
117+
}
118+
}
119+
}
120+
121+
// Transfer shortcode names so HasShortcode works for shortcodes from included pages.
122+
cp.p.shortcodeState.transferNames(cp2.p.shortcodeState)
123+
if cp2.p.pageOutputTemplateVariationsState.Load() == 2 {
124+
cp.p.pageOutputTemplateVariationsState.Store(2)
125+
}
126+
}
127+
128+
ctx = tpl.SetCallbackFunctionInContext(ctx, ctxCallback)
129+
106130
var hasVariants bool
107131
cp.workContent, hasVariants, err = p.contentToRender(ctx, p.source.parsed, p.cmap, cp.contentPlaceholders)
108132
if err != nil {
@@ -350,6 +374,63 @@ func (p *pageContentOutput) Fragments(ctx context.Context) *tableofcontents.Frag
350374
return p.tableOfContents
351375
}
352376

377+
func (p *pageContentOutput) RenderShortcodes(ctx context.Context) (template.HTML, error) {
378+
p.p.s.initInit(ctx, p.initToC, p.p)
379+
source := p.p.source.parsed.Input()
380+
renderedShortcodes := p.contentPlaceholders
381+
var insertPlaceholders bool
382+
var hasVariants bool
383+
var cb func(*pageContentOutput)
384+
if v := tpl.GetCallbackFunctionFromContext(ctx); v != nil {
385+
if fn, ok := v.(func(*pageContentOutput)); ok {
386+
insertPlaceholders = true
387+
cb = fn
388+
}
389+
}
390+
c := make([]byte, 0, len(source)+(len(source)/10))
391+
for _, it := range p.p.cmap.items {
392+
switch v := it.(type) {
393+
case pageparser.Item:
394+
c = append(c, source[v.Pos():v.Pos()+len(v.Val(source))]...)
395+
case pageContentReplacement:
396+
// Ignore.
397+
case *shortcode:
398+
if !insertPlaceholders || !v.insertPlaceholder() {
399+
// Insert the rendered shortcode.
400+
renderedShortcode, found := renderedShortcodes[v.placeholder]
401+
if !found {
402+
// This should never happen.
403+
panic(fmt.Sprintf("rendered shortcode %q not found", v.placeholder))
404+
}
405+
406+
b, more, err := renderedShortcode.renderShortcode(ctx)
407+
if err != nil {
408+
return "", fmt.Errorf("failed to render shortcode: %w", err)
409+
}
410+
hasVariants = hasVariants || more
411+
c = append(c, []byte(b)...)
412+
413+
} else {
414+
// Insert the placeholder so we can insert the content after
415+
// markdown processing.
416+
c = append(c, []byte(v.placeholder)...)
417+
}
418+
default:
419+
panic(fmt.Sprintf("unknown item type %T", it))
420+
}
421+
}
422+
423+
if hasVariants {
424+
p.p.pageOutputTemplateVariationsState.Store(2)
425+
}
426+
427+
if cb != nil {
428+
cb(p)
429+
}
430+
431+
return helpers.BytesToHTML(c), nil
432+
}
433+
353434
func (p *pageContentOutput) TableOfContents(ctx context.Context) template.HTML {
354435
p.p.s.initInit(ctx, p.initToC, p.p)
355436
return p.tableOfContentsHTML

hugolib/page_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -1998,7 +1998,6 @@ func TestRenderWithoutArgument(t *testing.T) {
19981998
IntegrationTestConfig{
19991999
T: t,
20002000
TxtarString: files,
2001-
Running: true,
20022001
},
20032002
).BuildE()
20042003

0 commit comments

Comments
 (0)