Skip to content

Commit

Permalink
Revise the fix for shortcode vs output format nilpointer
Browse files Browse the repository at this point in the history
We do lazy initialization and (potentially) reuse of an output format's rendered content. We do this evaluation when we
start a new rendering a new output format. There are, however, situation where these borders gets crossed (e.g.
accessing content from another output format). We have a check for this in place for most cases, but not the content
rendering of inner markdown blocks inside shortcodes. This patch applies that same logic to the newly introduced
RenderContent method (which is not available from the templates).

Fixes #10391
  • Loading branch information
bep committed Oct 26, 2022
1 parent e5d2a8f commit 631d768
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 17 deletions.
1 change: 1 addition & 0 deletions hugolib/page.go
Expand Up @@ -909,6 +909,7 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error {
}
return cp, nil
})
p.pageOutput.contentRenderer = lcp
p.pageOutput.ContentProvider = lcp
p.pageOutput.TableOfContentsProvider = lcp
p.pageOutput.PageRenderProvider = lcp
Expand Down
3 changes: 3 additions & 0 deletions hugolib/page__output.go
Expand Up @@ -81,6 +81,7 @@ type pageOutput struct {

// These interface provides the functionality that is specific for this
// output format.
contentRenderer page.ContentRenderer
pagePerOutputProviders
page.ContentProvider
page.TableOfContentsProvider
Expand All @@ -94,10 +95,12 @@ func (p *pageOutput) initContentProvider(cp *pageContentOutput) {
if cp == nil {
return
}
p.contentRenderer = cp
p.ContentProvider = cp
p.TableOfContentsProvider = cp
p.PageRenderProvider = cp
p.cp = cp

}

func (p *pageOutput) enablePlaceholders() {
Expand Down
6 changes: 3 additions & 3 deletions hugolib/page__per_output.go
Expand Up @@ -123,7 +123,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
isHTML := cp.p.m.markup == "html"

if !isHTML {
r, err := cp.renderContent(cp.workContent, true)
r, err := po.contentRenderer.RenderContent(cp.workContent, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -183,7 +183,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
}
}
} else if cp.p.m.summary != "" {
b, err := cp.renderContent([]byte(cp.p.m.summary), false)
b, err := po.contentRenderer.RenderContent([]byte(cp.p.m.summary), false)
if err != nil {
return err
}
Expand Down Expand Up @@ -629,7 +629,7 @@ func (p *pageContentOutput) setAutoSummary() error {
return nil
}

func (cp *pageContentOutput) renderContent(content []byte, renderTOC bool) (converter.Result, error) {
func (cp *pageContentOutput) RenderContent(content []byte, renderTOC bool) (converter.Result, error) {
if err := cp.initRenderHooks(); err != nil {
return nil, err
}
Expand Down
13 changes: 1 addition & 12 deletions hugolib/shortcode.go
Expand Up @@ -381,19 +381,8 @@ func renderShortcode(
// Pre Hugo 0.55 this was the behaviour even for the outer-most
// shortcode.
if sc.doMarkup && (level > 0 || sc.configVersion() == 1) {

cp := p.pageOutput.cp
if cp == nil {
var err error
cp, err = newPageContentOutput(p, p.pageOutput)
if err != nil {
return "", false, err
}
p.pageOutput.initContentProvider(cp)
}

var err error
b, err := p.pageOutput.cp.renderContent([]byte(inner), false)
b, err := p.pageOutput.contentRenderer.RenderContent([]byte(inner), false)
if err != nil {
return "", false, err
}
Expand Down
6 changes: 6 additions & 0 deletions resources/page/page.go
Expand Up @@ -19,6 +19,7 @@ import (
"html/template"

"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/converter"

"github.com/bep/gitmap"
"github.com/gohugoio/hugo/config"
Expand Down Expand Up @@ -105,6 +106,11 @@ type ContentProvider interface {
Len() int
}

// ContentRenderer provides the content rendering methods for some content.
type ContentRenderer interface {
RenderContent(content []byte, renderTOC bool) (converter.Result, error)
}

// FileProvider provides the source file.
type FileProvider interface {
File() source.File
Expand Down
16 changes: 15 additions & 1 deletion resources/page/page_lazy_contentprovider.go
Expand Up @@ -17,13 +17,22 @@ import (
"html/template"

"github.com/gohugoio/hugo/lazy"
"github.com/gohugoio/hugo/markup/converter"
)

// OutputFormatContentProvider represents the method set that is "outputFormat aware" and that we
// provide lazy initialization for in case they get invoked outside of their normal rendering context, e.g. via .Translations.
// Note that this set is currently not complete, but should cover the most common use cases.
// For the others, the implementation will be from the page.NoopPage.
type OutputFormatContentProvider interface {
OutputFormatPageContentProvider

// for internal use.
ContentRenderer
}

// OutputFormatPageContentProvider holds the exported methods from Page that are "outputFormat aware".
type OutputFormatPageContentProvider interface {
ContentProvider
TableOfContentsProvider
PageRenderProvider
Expand All @@ -46,7 +55,7 @@ type LazyContentProvider struct {
func NewLazyContentProvider(f func() (OutputFormatContentProvider, error)) *LazyContentProvider {
lcp := LazyContentProvider{
init: lazy.New(),
cp: NopPage,
cp: NopCPageContentRenderer,
}
lcp.init.Add(func() (any, error) {
cp, err := f()
Expand Down Expand Up @@ -122,3 +131,8 @@ func (lcp *LazyContentProvider) TableOfContents() template.HTML {
lcp.init.Do()
return lcp.cp.TableOfContents()
}

func (lcp *LazyContentProvider) RenderContent(content []byte, renderTOC bool) (converter.Result, error) {
lcp.init.Do()
return lcp.cp.RenderContent(content, renderTOC)
}
19 changes: 18 additions & 1 deletion resources/page/page_nop.go
Expand Up @@ -16,10 +16,12 @@
package page

import (
"bytes"
"html/template"
"time"

"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/markup/converter"

"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/tpl"
Expand All @@ -41,7 +43,15 @@ import (
)

var (
NopPage Page = new(nopPage)
NopPage Page = new(nopPage)
NopContentRenderer ContentRenderer = new(nopContentRenderer)
NopCPageContentRenderer = struct {
OutputFormatPageContentProvider
ContentRenderer
}{
NopPage,
NopContentRenderer,
}
NilPage *nopPage
)

Expand Down Expand Up @@ -513,3 +523,10 @@ func (p *nopPage) WordCount() int {
func (p *nopPage) GetIdentity() identity.Identity {
return identity.NewPathIdentity("content", "foo/bar.md")
}

type nopContentRenderer int

func (r *nopContentRenderer) RenderContent(content []byte, renderTOC bool) (converter.Result, error) {
b := &bytes.Buffer{}
return b, nil
}

0 comments on commit 631d768

Please sign in to comment.