diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-02-11 16:20:24 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2023-02-21 17:56:41 +0100 |
commit | 90da7664bf1f3a0ca2e18144b5deacf532c6e3cf (patch) | |
tree | 78d8ac72ebb2ccee4ca4bbeeb9add3365c743e90 /hugolib | |
parent | 0afec0a9f4aace1f5f4af6822aeda6223ee3e3a9 (diff) |
Add page fragments support to Related
The main topic of this commit is that you can now index fragments (content heading identifiers) when calling `.Related`.
You can do this by:
* Configure one or more indices with type `fragments`
* The name of those index configurations maps to an (optional) front matter slice with fragment references. This allows you to link
page<->fragment and page<->page.
* This also will index all the fragments (heading identifiers) of the pages.
It's also possible to use type `fragments` indices in shortcode, e.g.:
```
{{ $related := site.RegularPages.Related .Page }}
```
But, and this is important, you need to include the shortcode using the `{{<` delimiter. Not doing so will create infinite loops and timeouts.
This commit also:
* Adds two new methods to Page: Fragments (can also be used to build ToC) and HeadingsFiltered (this is only used in Related Content with
index type `fragments` and `enableFilter` set to true.
* Consolidates all `.Related*` methods into one, which takes either a `Page` or an options map as its only argument.
* Add `context.Context` to all of the content related Page API. Turns out it wasn't strictly needed for this particular feature, but it will
soon become usefil, e.g. in #9339.
Closes #10711
Updates #9339
Updates #10725
Diffstat (limited to 'hugolib')
-rw-r--r-- | hugolib/content_factory.go | 3 | ||||
-rw-r--r-- | hugolib/content_map_page.go | 2 | ||||
-rw-r--r-- | hugolib/embedded_shortcodes_test.go | 3 | ||||
-rw-r--r-- | hugolib/hugo_sites.go | 14 | ||||
-rw-r--r-- | hugolib/hugo_sites_build.go | 2 | ||||
-rw-r--r-- | hugolib/hugo_sites_build_errors_test.go | 2 | ||||
-rw-r--r-- | hugolib/image_test.go | 153 | ||||
-rw-r--r-- | hugolib/language_content_dir_test.go | 3 | ||||
-rw-r--r-- | hugolib/page.go | 48 | ||||
-rw-r--r-- | hugolib/page__content.go | 14 | ||||
-rw-r--r-- | hugolib/page__menus.go | 7 | ||||
-rw-r--r-- | hugolib/page__new.go | 3 | ||||
-rw-r--r-- | hugolib/page__per_output.go | 219 | ||||
-rw-r--r-- | hugolib/page__position.go | 6 | ||||
-rw-r--r-- | hugolib/page_test.go | 53 | ||||
-rw-r--r-- | hugolib/shortcode.go | 101 | ||||
-rw-r--r-- | hugolib/shortcode_page.go | 41 | ||||
-rw-r--r-- | hugolib/shortcode_test.go | 28 | ||||
-rw-r--r-- | hugolib/site.go | 22 | ||||
-rw-r--r-- | hugolib/site_render.go | 3 | ||||
-rw-r--r-- | hugolib/site_test.go | 7 | ||||
-rw-r--r-- | hugolib/testhelpers_test.go | 3 |
22 files changed, 394 insertions, 343 deletions
diff --git a/hugolib/content_factory.go b/hugolib/content_factory.go index 017a0bc97..e22f46513 100644 --- a/hugolib/content_factory.go +++ b/hugolib/content_factory.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "fmt" "io" "path/filepath" @@ -83,7 +84,7 @@ func (f ContentFactory) ApplyArchetypeTemplate(w io.Writer, p page.Page, archety return fmt.Errorf("failed to parse archetype template: %s: %w", err, err) } - result, err := executeToString(ps.s.Tmpl(), templ, d) + result, err := executeToString(context.TODO(), ps.s.Tmpl(), templ, d) if err != nil { return fmt.Errorf("failed to execute archetype template: %s: %w", err, err) } diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index d8f28286c..70c5d6a27 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -171,7 +171,7 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB return nil, err } - ps.init.Add(func() (any, error) { + ps.init.Add(func(context.Context) (any, error) { pp, err := newPagePaths(s, ps, metaProvider) if err != nil { return nil, err diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go index 1707bcfa7..1e06494bf 100644 --- a/hugolib/embedded_shortcodes_test.go +++ b/hugolib/embedded_shortcodes_test.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "encoding/json" "fmt" "html/template" @@ -70,7 +71,7 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) { c.Assert(len(s.RegularPages()), qt.Equals, 1) - content, err := s.RegularPages()[0].Content() + content, err := s.RegularPages()[0].Content(context.Background()) c.Assert(err, qt.IsNil) output := cast.ToString(content) diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go index 569c27be5..cdc5d97fb 100644 --- a/hugolib/hugo_sites.go +++ b/hugolib/hugo_sites.go @@ -194,7 +194,7 @@ func (h *hugoSitesInit) Reset() { } func (h *HugoSites) Data() map[string]any { - if _, err := h.init.data.Do(); err != nil { + if _, err := h.init.data.Do(context.Background()); err != nil { h.SendError(fmt.Errorf("failed to load data: %w", err)) return nil } @@ -202,7 +202,7 @@ func (h *HugoSites) Data() map[string]any { } func (h *HugoSites) gitInfoForPage(p page.Page) (source.GitInfo, error) { - if _, err := h.init.gitInfo.Do(); err != nil { + if _, err := h.init.gitInfo.Do(context.Background()); err != nil { return source.GitInfo{}, err } @@ -214,7 +214,7 @@ func (h *HugoSites) gitInfoForPage(p page.Page) (source.GitInfo, error) { } func (h *HugoSites) codeownersForPage(p page.Page) ([]string, error) { - if _, err := h.init.gitInfo.Do(); err != nil { + if _, err := h.init.gitInfo.Do(context.Background()); err != nil { return nil, err } @@ -363,7 +363,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) { donec: make(chan bool), } - h.init.data.Add(func() (any, error) { + h.init.data.Add(func(context.Context) (any, error) { err := h.loadData(h.PathSpec.BaseFs.Data.Dirs) if err != nil { return nil, fmt.Errorf("failed to load data: %w", err) @@ -371,7 +371,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) { return nil, nil }) - h.init.layouts.Add(func() (any, error) { + h.init.layouts.Add(func(context.Context) (any, error) { for _, s := range h.Sites { if err := s.Tmpl().(tpl.TemplateManager).MarkReady(); err != nil { return nil, err @@ -380,7 +380,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) { return nil, nil }) - h.init.translations.Add(func() (any, error) { + h.init.translations.Add(func(context.Context) (any, error) { if len(h.Sites) > 1 { allTranslations := pagesToTranslationsMap(h.Sites) assignTranslationsToPages(allTranslations, h.Sites) @@ -389,7 +389,7 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) { return nil, nil }) - h.init.gitInfo.Add(func() (any, error) { + h.init.gitInfo.Add(func(context.Context) (any, error) { err := h.loadGitInfo() if err != nil { return nil, fmt.Errorf("failed to load Git info: %w", err) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 5eee564aa..66abf4f16 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -268,7 +268,7 @@ func (h *HugoSites) assemble(bcfg *BuildCfg) error { } func (h *HugoSites) render(config *BuildCfg) error { - if _, err := h.init.layouts.Do(); err != nil { + if _, err := h.init.layouts.Do(context.Background()); err != nil { return err } diff --git a/hugolib/hugo_sites_build_errors_test.go b/hugolib/hugo_sites_build_errors_test.go index ffbfe1c17..f42b44461 100644 --- a/hugolib/hugo_sites_build_errors_test.go +++ b/hugolib/hugo_sites_build_errors_test.go @@ -396,7 +396,7 @@ line 4 } -func TestErrorNestedShortocde(t *testing.T) { +func TestErrorNestedShortcode(t *testing.T) { t.Parallel() files := ` diff --git a/hugolib/image_test.go b/hugolib/image_test.go index ac18b9423..db1707c22 100644 --- a/hugolib/image_test.go +++ b/hugolib/image_test.go @@ -14,162 +14,9 @@ package hugolib import ( - "io" - "os" - "path/filepath" - "runtime" - "strings" "testing" - - "github.com/gohugoio/hugo/config" - "github.com/gohugoio/hugo/htesting" - - qt "github.com/frankban/quicktest" - "github.com/gohugoio/hugo/hugofs" ) -// We have many tests for the different resize operations etc. in the resource package, -// this is an integration test. -func TestImageOps(t *testing.T) { - c := qt.New(t) - // Make this a real as possible. - workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "image-resize") - c.Assert(err, qt.IsNil) - defer clean() - - newBuilder := func(timeout any) *sitesBuilder { - v := config.NewWithTestDefaults() - v.Set("workingDir", workDir) - v.Set("baseURL", "https://example.org") - v.Set("timeout", timeout) - - b := newTestSitesBuilder(t).WithWorkingDir(workDir) - b.Fs = hugofs.NewDefault(v) - b.WithViper(v) - b.WithContent("mybundle/index.md", ` ---- -title: "My bundle" ---- - -{{< imgproc >}} - -`) - - b.WithTemplatesAdded( - "shortcodes/imgproc.html", ` -{{ $img := resources.Get "images/sunset.jpg" }} -{{ $r := $img.Resize "129x239" }} -IMG SHORTCODE: {{ $r.RelPermalink }}/{{ $r.Width }} -`, - "index.html", ` -{{ $p := .Site.GetPage "mybundle" }} -{{ $img1 := resources.Get "images/sunset.jpg" }} -{{ $img2 := $p.Resources.GetMatch "sunset.jpg" }} -{{ $img3 := resources.GetMatch "images/*.jpg" }} -{{ $r := $img1.Resize "123x234" }} -{{ $r2 := $r.Resize "12x23" }} -{{ $b := $img2.Resize "345x678" }} -{{ $b2 := $b.Resize "34x67" }} -{{ $c := $img3.Resize "456x789" }} -{{ $fingerprinted := $img1.Resize "350x" | fingerprint }} - -{{ $images := slice $r $r2 $b $b2 $c $fingerprinted }} - -{{ range $i, $r := $images }} -{{ printf "Resized%d:" (add $i 1) }} {{ $r.Name }}|{{ $r.Width }}|{{ $r.Height }}|{{ $r.MediaType }}|{{ $r.RelPermalink }}| -{{ end }} - -{{ $blurryGrayscale1 := $r | images.Filter images.Grayscale (images.GaussianBlur 8) }} -BG1: {{ $blurryGrayscale1.RelPermalink }}/{{ $blurryGrayscale1.Width }} -{{ $blurryGrayscale2 := $r.Filter images.Grayscale (images.GaussianBlur 8) }} -BG2: {{ $blurryGrayscale2.RelPermalink }}/{{ $blurryGrayscale2.Width }} -{{ $blurryGrayscale2_2 := $r.Filter images.Grayscale (images.GaussianBlur 8) }} -BG2_2: {{ $blurryGrayscale2_2.RelPermalink }}/{{ $blurryGrayscale2_2.Width }} - -{{ $filters := slice images.Grayscale (images.GaussianBlur 9) }} -{{ $blurryGrayscale3 := $r | images.Filter $filters }} -BG3: {{ $blurryGrayscale3.RelPermalink }}/{{ $blurryGrayscale3.Width }} - -{{ $blurryGrayscale4 := $r.Filter $filters }} -BG4: {{ $blurryGrayscale4.RelPermalink }}/{{ $blurryGrayscale4.Width }} - -{{ $p.Content }} - -`) - - return b - } - - imageDir := filepath.Join(workDir, "assets", "images") - bundleDir := filepath.Join(workDir, "content", "mybundle") - - c.Assert(os.MkdirAll(imageDir, 0777), qt.IsNil) - c.Assert(os.MkdirAll(bundleDir, 0777), qt.IsNil) - src, err := os.Open("testdata/sunset.jpg") - c.Assert(err, qt.IsNil) - out, err := os.Create(filepath.Join(imageDir, "sunset.jpg")) - c.Assert(err, qt.IsNil) - _, err = io.Copy(out, src) - c.Assert(err, qt.IsNil) - out.Close() - - src.Seek(0, 0) - - out, err = os.Create(filepath.Join(bundleDir, "sunset.jpg")) - c.Assert(err, qt.IsNil) - _, err = io.Copy(out, src) - c.Assert(err, qt.IsNil) - out.Close() - src.Close() - - // First build it with a very short timeout to trigger errors. - b := newBuilder("10ns") - - imgExpect := ` -Resized1: images/sunset.jpg|123|234|image/jpeg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_123x234_resize_q75_box.jpg| -Resized2: images/sunset.jpg|12|23|image/jpeg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_ada4bb1a57f77a63306e3bd67286248e.jpg| -Resized3: sunset.jpg|345|678|image/jpeg|/mybundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_345x678_resize_q75_box.jpg| -Resized4: sunset.jpg|34|67|image/jpeg|/mybundle/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_44d8c928664d7c5a67377c6ec58425ce.jpg| -Resized5: images/sunset.jpg|456|789|image/jpeg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_456x789_resize_q75_box.jpg| -Resized6: images/sunset.jpg|350|219|image/jpeg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_350x0_resize_q75_box.a86fe88d894e5db613f6aa8a80538fefc25b20fa24ba0d782c057adcef616f56.jpg| -BG1: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_2ae8bb993431ec1aec40fe59927b46b4.jpg/123 -BG2: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_2ae8bb993431ec1aec40fe59927b46b4.jpg/123 -BG3: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_ed7740a90b82802261c2fbdb98bc8082.jpg/123 -BG4: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_ed7740a90b82802261c2fbdb98bc8082.jpg/123 -IMG SHORTCODE: /images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_129x239_resize_q75_box.jpg/129 -` - - assertImages := func() { - b.Helper() - b.AssertFileContent("public/index.html", imgExpect) - b.AssertImage(350, 219, "public/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_350x0_resize_q75_box.a86fe88d894e5db613f6aa8a80538fefc25b20fa24ba0d782c057adcef616f56.jpg") - b.AssertImage(129, 239, "public/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_129x239_resize_q75_box.jpg") - } - - err = b.BuildE(BuildCfg{}) - if runtime.GOOS != "windows" && !strings.Contains(runtime.GOARCH, "arm") && !htesting.IsGitHubAction() { - // TODO(bep) - c.Assert(err, qt.Not(qt.IsNil)) - } - - b = newBuilder(29000) - b.Build(BuildCfg{}) - - assertImages() - - // Truncate one image. - imgInCache := filepath.Join(workDir, "resources/_gen/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_ed7740a90b82802261c2fbdb98bc8082.jpg") - f, err := os.Create(imgInCache) - c.Assert(err, qt.IsNil) - f.Close() - - // Build it again to make sure we read images from file cache. - b = newBuilder("30s") - b.Build(BuildCfg{}) - - assertImages() -} - func TestImageResizeMultilingual(t *testing.T) { b := newTestSitesBuilder(t).WithConfigFile("toml", ` baseURL="https://example.org" diff --git a/hugolib/language_content_dir_test.go b/hugolib/language_content_dir_test.go index 57cdab67b..23809f4df 100644 --- a/hugolib/language_content_dir_test.go +++ b/hugolib/language_content_dir_test.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "fmt" "os" "path/filepath" @@ -245,7 +246,7 @@ Content. c.Assert(svP2.Language().Lang, qt.Equals, "sv") c.Assert(nnP2.Language().Lang, qt.Equals, "nn") - content, _ := nnP2.Content() + content, _ := nnP2.Content(context.Background()) contentStr := cast.ToString(content) c.Assert(contentStr, qt.Contains, "SVP3-REF: https://example.org/sv/sect/p-sv-3/") c.Assert(contentStr, qt.Contains, "SVP3-RELREF: /sv/sect/p-sv-3/") diff --git a/hugolib/page.go b/hugolib/page.go index 97f1ed351..40972d7c5 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -15,6 +15,7 @@ package hugolib import ( "bytes" + "context" "fmt" "path" "path/filepath" @@ -24,8 +25,10 @@ import ( "go.uber.org/atomic" "github.com/gohugoio/hugo/identity" + "github.com/gohugoio/hugo/related" "github.com/gohugoio/hugo/markup/converter" + "github.com/gohugoio/hugo/markup/tableofcontents" "github.com/gohugoio/hugo/tpl" @@ -148,6 +151,43 @@ func (p *pageState) GetIdentity() identity.Identity { return identity.NewPathIdentity(files.ComponentFolderContent, filepath.FromSlash(p.Pathc())) } +func (p *pageState) Fragments(ctx context.Context) *tableofcontents.Fragments { + p.s.initInit(ctx, p.cp.initToC, p) + if p.pageOutput.cp.tableOfContents == nil { + return tableofcontents.Empty + } + return p.pageOutput.cp.tableOfContents +} + +func (p *pageState) HeadingsFiltered(context.Context) tableofcontents.Headings { + return nil +} + +type pageHeadingsFiltered struct { + *pageState + headings tableofcontents.Headings +} + +func (p *pageHeadingsFiltered) HeadingsFiltered(context.Context) tableofcontents.Headings { + return p.headings +} + +func (p *pageHeadingsFiltered) page() page.Page { + return p.pageState +} + +// For internal use by the related content feature. +func (p *pageState) ApplyFilterToHeadings(ctx context.Context, fn func(*tableofcontents.Heading) bool) related.Document { + if p.pageOutput.cp.tableOfContents == nil { + return p + } + headings := p.pageOutput.cp.tableOfContents.Headings.FilterBy(fn) + return &pageHeadingsFiltered{ + pageState: p, + headings: headings, + } +} + func (p *pageState) GitInfo() source.GitInfo { return p.gitInfo } @@ -351,7 +391,7 @@ func (p *pageState) String() string { // IsTranslated returns whether this content file is translated to // other language(s). func (p *pageState) IsTranslated() bool { - p.s.h.init.translations.Do() + p.s.h.init.translations.Do(context.Background()) return len(p.translations) > 0 } @@ -375,13 +415,13 @@ func (p *pageState) TranslationKey() string { // AllTranslations returns all translations, including the current Page. func (p *pageState) AllTranslations() page.Pages { - p.s.h.init.translations.Do() + p.s.h.init.translations.Do(context.Background()) return p.allTranslations } // Translations returns the translations excluding the current Page. func (p *pageState) Translations() page.Pages { - p.s.h.init.translations.Do() + p.s.h.init.translations.Do(context.Background()) return p.translations } @@ -461,7 +501,7 @@ func (p *pageState) initOutputFormat(isRenderingSite bool, idx int) error { // Must be run after the site section tree etc. is built and ready. func (p *pageState) initPage() error { - if _, err := p.init.Do(); err != nil { + if _, err := p.init.Do(context.Background()); err != nil { return err } return nil diff --git a/hugolib/page__content.go b/hugolib/page__content.go index a721d1fce..89c38bd84 100644 --- a/hugolib/page__content.go +++ b/hugolib/page__content.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "fmt" "github.com/gohugoio/hugo/output" @@ -37,9 +38,9 @@ type pageContent struct { } // returns the content to be processed by Goldmark or similar. -func (p pageContent) contentToRender(parsed pageparser.Result, pm *pageContentMap, renderedShortcodes map[string]string) []byte { +func (p pageContent) contentToRender(ctx context.Context, parsed pageparser.Result, pm *pageContentMap, renderedShortcodes map[string]shortcodeRenderer) ([]byte, bool, error) { source := parsed.Input() - + var hasVariants bool c := make([]byte, 0, len(source)+(len(source)/10)) for _, it := range pm.items { @@ -57,7 +58,12 @@ func (p pageContent) contentToRender(parsed pageparser.Result, pm *pageContentMa panic(fmt.Sprintf("rendered shortcode %q not found", v.placeholder)) } - c = append(c, []byte(renderedShortcode)...) + b, more, err := renderedShortcode.renderShortcode(ctx) + if err != nil { + return nil, false, fmt.Errorf("failed to render shortcode: %w", err) + } + hasVariants = hasVariants || more + c = append(c, []byte(b)...) } else { // Insert the placeholder so we can insert the content after @@ -69,7 +75,7 @@ func (p pageContent) contentToRender(parsed pageparser.Result, pm *pageContentMa } } - return c + return c, hasVariants, nil } func (p pageContent) selfLayoutForOutput(f output.Format) string { diff --git a/hugolib/page__menus.go b/hugolib/page__menus.go index 49d392c2f..5bed2bc03 100644 --- a/hugolib/page__menus.go +++ b/hugolib/page__menus.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "sync" "github.com/gohugoio/hugo/navigation" @@ -29,13 +30,13 @@ type pageMenus struct { } func (p *pageMenus) HasMenuCurrent(menuID string, me *navigation.MenuEntry) bool { - p.p.s.init.menus.Do() + p.p.s.init.menus.Do(context.Background()) p.init() return p.q.HasMenuCurrent(menuID, me) } func (p *pageMenus) IsMenuCurrent(menuID string, inme *navigation.MenuEntry) bool { - p.p.s.init.menus.Do() + p.p.s.init.menus.Do(context.Background()) p.init() return p.q.IsMenuCurrent(menuID, inme) } @@ -43,7 +44,7 @@ func (p *pageMenus) IsMenuCurrent(menuID string, inme *navigation.MenuEntry) boo func (p *pageMenus) Menus() navigation.PageMenus { // There is a reverse dependency here. initMenus will, once, build the // site menus and update any relevant page. - p.p.s.init.menus.Do() + p.p.s.init.menus.Do(context.Background()) return p.menus() } diff --git a/hugolib/page__new.go b/hugolib/page__new.go index e52b9476b..3787cd2bd 100644 --- a/hugolib/page__new.go +++ b/hugolib/page__new.go @@ -14,6 +14,7 @@ package hugolib import ( + "context" "html/template" "strings" @@ -121,7 +122,7 @@ func newPageFromMeta( return nil, err } - ps.init.Add(func() (any, error) { + ps.init.Add(func(context.Context) (any, error) { pp, err := newPagePaths(metaProvider.s, ps, metaProvider) if err != nil { return nil, err diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index 97e9cc465..827a6b792 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -18,7 +18,6 @@ import ( "context" "fmt" "html/template" - "runtime/debug" "strings" "sync" "unicode/utf8" @@ -34,6 +33,7 @@ import ( "github.com/gohugoio/hugo/markup/converter/hooks" "github.com/gohugoio/hugo/markup/highlight/chromalexers" + "github.com/gohugoio/hugo/markup/tableofcontents" "github.com/gohugoio/hugo/markup/converter" @@ -87,43 +87,35 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err renderHooks: &renderHooks{}, } - initContent := func() (err error) { - p.s.h.IncrContentRender() - + initToC := func(ctx context.Context) (err error) { if p.cmap == nil { // Nothing to do. return nil } - defer func() { - // See https://github.com/gohugoio/hugo/issues/6210 - if r := recover(); r != nil { - err = fmt.Errorf("%s", r) - p.s.Log.Errorf("[BUG] Got panic:\n%s\n%s", r, string(debug.Stack())) - } - }() if err := po.cp.initRenderHooks(); err != nil { return err } - var hasShortcodeVariants bool - f := po.f - cp.contentPlaceholders, hasShortcodeVariants, err = p.shortcodeState.renderShortcodesForPage(p, f) + cp.contentPlaceholders, err = p.shortcodeState.prepareShortcodesForPage(ctx, p, f) if err != nil { return err } - if hasShortcodeVariants { + var hasVariants bool + cp.workContent, hasVariants, err = p.contentToRender(ctx, p.source.parsed, p.cmap, cp.contentPlaceholders) + if err != nil { + return err + } + if hasVariants { p.pageOutputTemplateVariationsState.Store(2) } - cp.workContent = p.contentToRender(p.source.parsed, p.cmap, cp.contentPlaceholders) - isHTML := cp.p.m.markup == "html" if !isHTML { - r, err := po.contentRenderer.RenderContent(cp.workContent, true) + r, err := po.contentRenderer.RenderContent(ctx, cp.workContent, true) if err != nil { return err } @@ -132,8 +124,9 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err if tocProvider, ok := r.(converter.TableOfContentsProvider); ok { cfg := p.s.ContentSpec.Converters.GetMarkupConfig() - cp.tableOfContents = template.HTML( - tocProvider.TableOfContents().ToHTML( + cp.tableOfContents = tocProvider.TableOfContents() + cp.tableOfContentsHTML = template.HTML( + cp.tableOfContents.ToHTML( cfg.TableOfContents.StartLevel, cfg.TableOfContents.EndLevel, cfg.TableOfContents.Ordered, @@ -141,26 +134,60 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err ) } else { tmpContent, tmpTableOfContents := helpers.ExtractTOC(cp.workContent) - cp.tableOfContents = helpers.BytesToHTML(tmpTableOfContents) + cp.tableOfContentsHTML = helpers.BytesToHTML(tmpTableOfContents) + cp.tableOfContents = tableofcontents.Empty cp.workContent = tmpContent } } - if cp.placeholdersEnabled { - // ToC was accessed via .Page.TableOfContents in the shortcode, - // at a time when the ToC wasn't ready. - cp.contentPlaceholders[tocShortcodePlaceholder] = string(cp.tableOfContents) + return nil + + } + + initContent := func(ctx context.Context) (err error) { + + p.s.h.IncrContentRender() + + if p.cmap == nil { + // Nothing to do. + return nil } if p.cmap.hasNonMarkdownShortcode || cp.placeholdersEnabled { // There are one or more replacement tokens to be replaced. - cp.workContent, err = replaceShortcodeTokens(cp.workContent, cp.contentPlaceholders) + var hasShortcodeVariants bool + tokenHandler := func(ctx context.Context, token string) ([]byte, error) { + if token == tocShortcodePlaceholder { + // The Page's TableOfContents was accessed in a shortcode. + if cp.tableOfContentsHTML == "" { + cp.p.s.initInit(ctx, cp.initToC, cp.p) + } + return []byte(cp.tableOfContentsHTML), nil |