]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix panic when passthrough elements are used in headings
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 8 Apr 2026 11:30:14 +0000 (13:30 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 8 Apr 2026 13:03:05 +0000 (15:03 +0200)
Fixes #14677

Co-Authored-By: xingzihai <1315258019@qq.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
markup/goldmark/convert.go
markup/goldmark/toc.go
markup/goldmark/toc_integration_test.go

index f4519dff45f6f3f65b7b8d1d853763d73ffdb3c1..8ed16b0c88f4bce271692d675fdfa530b3871550 100644 (file)
@@ -122,6 +122,12 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
                )
        }
 
+       if cfg.Extensions.Passthrough.Enable {
+               tocRendererOptions = append(tocRendererOptions,
+                       renderer.WithNodeRenderers(util.Prioritized(&tocPassthroughRenderer{}, 90)),
+               )
+       }
+
        var (
                extensions = []goldmark.Extender{
                        hugocontext.New(pcfg.Logger),
index 3fb53f875c2f05d24cd0ac69099fc3a71f2484e5..dd653c9e22d6235ff7f31a2b2b67e1db29b5e945 100644 (file)
@@ -24,6 +24,7 @@ import (
        emojiAst "github.com/yuin/goldmark-emoji/ast"
 
        "github.com/gohugoio/hugo-goldmark-extensions/extras"
+       "github.com/gohugoio/hugo-goldmark-extensions/passthrough"
        "github.com/gohugoio/hugo/markup/tableofcontents"
 
        "github.com/yuin/goldmark"
@@ -116,7 +117,9 @@ func (t *tocTransformer) Transform(n *ast.Document, reader text.Reader, pc parse
                        ast.KindAutoLink,
                        ast.KindRawHTML,
                        ast.KindText,
-                       ast.KindString:
+                       ast.KindString,
+                       passthrough.KindPassthroughInline,
+                       passthrough.KindPassthroughBlock:
                        err := t.r.Render(&headingText, reader.Source(), n)
                        if err != nil {
                                return s, err
@@ -171,6 +174,30 @@ func newTOCSanitizerPolicy() *bluemonday.Policy {
        return p
 }
 
+// tocPassthroughRenderer renders passthrough nodes as raw text for the TOC.
+type tocPassthroughRenderer struct{}
+
+func (r *tocPassthroughRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+       reg.Register(passthrough.KindPassthroughInline, r.render)
+       reg.Register(passthrough.KindPassthroughBlock, r.render)
+}
+
+func (r *tocPassthroughRenderer) render(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+       if !entering {
+               return ast.WalkContinue, nil
+       }
+       switch nn := node.(type) {
+       case *passthrough.PassthroughInline:
+               w.Write(nn.Segment.Value(src))
+       case *passthrough.PassthroughBlock:
+               for i := range nn.Lines().Len() {
+                       line := nn.Lines().At(i)
+                       w.Write(line.Value(src))
+               }
+       }
+       return ast.WalkSkipChildren, nil
+}
+
 var whiteSpaceRe = regexp.MustCompile(`\s+`)
 
 // sanitizeTOCHeadingTitle sanitizes s for use as a TOC heading title.
index 715828253d46e164c2be8600b154df34db0df03d..105e1a7448c2108879aa1797fd16cdf7ab6c531c 100644 (file)
@@ -364,3 +364,37 @@ title: home
                `<li><a href="#1st">1^st^</a></li>`,
        )
 }
+
+// Issue 14677
+func TestTableOfContentsWithPassthrough(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+[markup.goldmark.extensions.passthrough]
+  enable = true
+[markup.goldmark.extensions.passthrough.delimiters]
+  inline = [['$', '$']]
+-- content/_index.md --
+---
+title: home
+---
+## Heading with **$a$**
+
+## Heading with $b$
+
+## Heading with *$x + y$*
+
+-- layouts/home.html --
+{{ .TableOfContents }}
+`
+
+       b := hugolib.Test(t, files)
+
+       b.AssertFileContent("public/index.html",
+               `<li><a href="#heading-with">Heading with <strong>$a$</strong></a></li>`,
+               `<li><a href="#heading-with-1">Heading with $b$</a></li>`,
+               `<li><a href="#heading-with-2">Heading with <em>$x + y$</em></a></li>`,
+       )
+}