32echo "l1";\n33",
+ )
+}
diff --git a/markup/goldmark/codeblocks/render.go b/markup/goldmark/codeblocks/render.go
new file mode 100644
index 00000000000..59d142e23d0
--- /dev/null
+++ b/markup/goldmark/codeblocks/render.go
@@ -0,0 +1,159 @@
+// Copyright 2022 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package codeblocks
+
+import (
+ "bytes"
+ "fmt"
+
+ "github.com/gohugoio/hugo/markup/converter/hooks"
+ "github.com/gohugoio/hugo/markup/goldmark/internal/render"
+ "github.com/gohugoio/hugo/markup/internal/attributes"
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/renderer"
+ "github.com/yuin/goldmark/text"
+ "github.com/yuin/goldmark/util"
+)
+
+type (
+ diagrams struct{}
+ htmlRenderer struct{}
+)
+
+func New() goldmark.Extender {
+ return &diagrams{}
+}
+
+func (e *diagrams) Extend(m goldmark.Markdown) {
+ m.Parser().AddOptions(
+ parser.WithASTTransformers(
+ util.Prioritized(&Transformer{}, 100),
+ ),
+ )
+ m.Renderer().AddOptions(renderer.WithNodeRenderers(
+ util.Prioritized(newHTMLRenderer(), 100),
+ ))
+}
+
+func newHTMLRenderer() renderer.NodeRenderer {
+ r := &htmlRenderer{}
+ return r
+}
+
+func (r *htmlRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+ reg.Register(KindCodeBlock, r.renderCodeBlock)
+}
+
+func (r *htmlRenderer) renderCodeBlock(w util.BufWriter, src []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
+ ctx := w.(*render.Context)
+
+ if entering {
+ return ast.WalkContinue, nil
+ }
+
+ n := node.(*codeBlock)
+ lang := string(n.b.Language(src))
+ ordinal := n.ordinal
+
+ var buff bytes.Buffer
+
+ l := n.b.Lines().Len()
+ for i := 0; i < l; i++ {
+ line := n.b.Lines().At(i)
+ buff.Write(line.Value(src))
+ }
+ text := buff.String()
+
+ var info []byte
+ if n.b.Info != nil {
+ info = n.b.Info.Segment.Value(src)
+ }
+ attrs := getAttributes(n.b, info)
+
+ v := ctx.RenderContext().GetRenderer(hooks.CodeBlockRendererType, lang)
+ if v == nil {
+ return ast.WalkStop, fmt.Errorf("no code renderer found for %q", lang)
+ }
+
+ cr := v.(hooks.CodeBlockRenderer)
+
+ err := cr.RenderCodeblock(
+ w,
+ codeBlockContext{
+ page: ctx.DocumentContext().Document,
+ lang: lang,
+ code: text,
+ ordinal: ordinal,
+ AttributesHolder: attributes.New(attrs, attributes.AttributesOwnerCodeBlock),
+ },
+ )
+
+ ctx.AddIdentity(cr)
+
+ return ast.WalkContinue, err
+}
+
+type codeBlockContext struct {
+ page interface{}
+ lang string
+ code string
+ ordinal int
+ *attributes.AttributesHolder
+}
+
+func (c codeBlockContext) Page() interface{} {
+ return c.page
+}
+
+func (c codeBlockContext) Lang() string {
+ return c.lang
+}
+
+func (c codeBlockContext) Code() string {
+ return c.code
+}
+
+func (c codeBlockContext) Ordinal() int {
+ return c.ordinal
+}
+
+func getAttributes(node *ast.FencedCodeBlock, infostr []byte) []ast.Attribute {
+ if node.Attributes() != nil {
+ return node.Attributes()
+ }
+ if infostr != nil {
+ attrStartIdx := -1
+
+ for idx, char := range infostr {
+ if char == '{' {
+ attrStartIdx = idx
+ break
+ }
+ }
+
+ if attrStartIdx > 0 {
+ n := ast.NewTextBlock() // dummy node for storing attributes
+ attrStr := infostr[attrStartIdx:]
+ if attrs, hasAttr := parser.ParseAttributes(text.NewReader(attrStr)); hasAttr {
+ for _, attr := range attrs {
+ n.SetAttribute(attr.Name, attr.Value)
+ }
+ return n.Attributes()
+ }
+ }
+ }
+ return nil
+}
diff --git a/markup/goldmark/codeblocks/transform.go b/markup/goldmark/codeblocks/transform.go
new file mode 100644
index 00000000000..791e99a5c3c
--- /dev/null
+++ b/markup/goldmark/codeblocks/transform.go
@@ -0,0 +1,53 @@
+package codeblocks
+
+import (
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/text"
+)
+
+// Kind is the kind of an Hugo code block.
+var KindCodeBlock = ast.NewNodeKind("HugoCodeBlock")
+
+// Its raw contents are the plain text of the code block.
+type codeBlock struct {
+ ast.BaseBlock
+ ordinal int
+ b *ast.FencedCodeBlock
+}
+
+func (*codeBlock) Kind() ast.NodeKind { return KindCodeBlock }
+
+func (*codeBlock) IsRaw() bool { return true }
+
+func (b *codeBlock) Dump(src []byte, level int) {
+}
+
+type Transformer struct{}
+
+// Transform transforms the provided Markdown AST.
+func (*Transformer) Transform(doc *ast.Document, reader text.Reader, pctx parser.Context) {
+ var codeBlocks []*ast.FencedCodeBlock
+
+ ast.Walk(doc, func(node ast.Node, enter bool) (ast.WalkStatus, error) {
+ if !enter {
+ return ast.WalkContinue, nil
+ }
+
+ cb, ok := node.(*ast.FencedCodeBlock)
+ if !ok {
+ return ast.WalkContinue, nil
+ }
+
+ codeBlocks = append(codeBlocks, cb)
+ return ast.WalkContinue, nil
+ })
+
+ for i, cb := range codeBlocks {
+ b := &codeBlock{b: cb, ordinal: i}
+ parent := cb.Parent()
+ if parent != nil {
+ parent.ReplaceChild(parent, cb, b)
+ }
+ }
+}
diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go
index c547fe1e0d1..bced48f4636 100644
--- a/markup/goldmark/convert.go
+++ b/markup/goldmark/convert.go
@@ -17,12 +17,12 @@ package goldmark
import (
"bytes"
"fmt"
- "math/bits"
"path/filepath"
"runtime/debug"
+ "github.com/gohugoio/hugo/markup/goldmark/codeblocks"
"github.com/gohugoio/hugo/markup/goldmark/internal/extensions/attributes"
- "github.com/yuin/goldmark/ast"
+ "github.com/gohugoio/hugo/markup/goldmark/internal/render"
"github.com/gohugoio/hugo/identity"
@@ -32,16 +32,13 @@ import (
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/markup/converter"
- "github.com/gohugoio/hugo/markup/highlight"
"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/yuin/goldmark"
- hl "github.com/yuin/goldmark-highlighting"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
- "github.com/yuin/goldmark/util"
)
// Provider is the package entry point.
@@ -104,7 +101,7 @@ func newMarkdown(pcfg converter.ProviderConfig) goldmark.Markdown {
)
if mcfg.Highlight.CodeFences {
- extensions = append(extensions, newHighlighting(mcfg.Highlight))
+ extensions = append(extensions, newCodeblocksExtender())
}
if cfg.Extensions.Table {
@@ -178,65 +175,6 @@ func (c converterResult) GetIdentities() identity.Identities {
return c.ids
}
-type bufWriter struct {
- *bytes.Buffer
-}
-
-const maxInt = 1<<(bits.UintSize-1) - 1
-
-func (b *bufWriter) Available() int {
- return maxInt
-}
-
-func (b *bufWriter) Buffered() int {
- return b.Len()
-}
-
-func (b *bufWriter) Flush() error {
- return nil
-}
-
-type renderContext struct {
- *bufWriter
- positions []int
- renderContextData
-}
-
-func (ctx *renderContext) pushPos(n int) {
- ctx.positions = append(ctx.positions, n)
-}
-
-func (ctx *renderContext) popPos() int {
- i := len(ctx.positions) - 1
- p := ctx.positions[i]
- ctx.positions = ctx.positions[:i]
- return p
-}
-
-type renderContextData interface {
- RenderContext() converter.RenderContext
- DocumentContext() converter.DocumentContext
- AddIdentity(id identity.Provider)
-}
-
-type renderContextDataHolder struct {
- rctx converter.RenderContext
- dctx converter.DocumentContext
- ids identity.Manager
-}
-
-func (ctx *renderContextDataHolder) RenderContext() converter.RenderContext {
- return ctx.rctx
-}
-
-func (ctx *renderContextDataHolder) DocumentContext() converter.DocumentContext {
- return ctx.dctx
-}
-
-func (ctx *renderContextDataHolder) AddIdentity(id identity.Provider) {
- ctx.ids.Add(id)
-}
-
var converterIdentity = identity.KeyValueIdentity{Key: "goldmark", Value: "converter"}
func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (result converter.Result, err error) {
@@ -251,7 +189,7 @@ func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (result convert
}
}()
- buf := &bufWriter{Buffer: &bytes.Buffer{}}
+ buf := &render.BufWriter{Buffer: &bytes.Buffer{}}
result = buf
pctx := c.newParserContext(ctx)
reader := text.NewReader(ctx.Src)
@@ -261,15 +199,15 @@ func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (result convert
parser.WithContext(pctx),
)
- rcx := &renderContextDataHolder{
- rctx: ctx,
- dctx: c.ctx,
- ids: identity.NewManager(converterIdentity),
+ rcx := &render.RenderContextDataHolder{
+ Rctx: ctx,
+ Dctx: c.ctx,
+ IDs: identity.NewManager(converterIdentity),
}
- w := &renderContext{
- bufWriter: buf,
- renderContextData: rcx,
+ w := &render.Context{
+ BufWriter: buf,
+ ContextData: rcx,
}
if err := c.md.Renderer().Render(w, ctx.Src, doc); err != nil {
@@ -278,7 +216,7 @@ func (c *goldmarkConverter) Convert(ctx converter.RenderContext) (result convert
return converterResult{
Result: buf,
- ids: rcx.ids.GetIdentities(),
+ ids: rcx.IDs.GetIdentities(),
toc: pctx.TableOfContents(),
}, nil
}
@@ -310,62 +248,7 @@ func (p *parserContext) TableOfContents() tableofcontents.Root {
return tableofcontents.Root{}
}
-func newHighlighting(cfg highlight.Config) goldmark.Extender {
- return hl.NewHighlighting(
- hl.WithStyle(cfg.Style),
- hl.WithGuessLanguage(cfg.GuessSyntax),
- hl.WithCodeBlockOptions(highlight.GetCodeBlockOptions()),
- hl.WithFormatOptions(
- cfg.ToHTMLOptions()...,
- ),
-
- hl.WithWrapperRenderer(func(w util.BufWriter, ctx hl.CodeBlockContext, entering bool) {
- var language string
- if l, hasLang := ctx.Language(); hasLang {
- language = string(l)
- }
-
- if ctx.Highlighted() {
- if entering {
- writeDivStart(w, ctx)
- } else {
- writeDivEnd(w)
- }
- } else {
- if entering {
- highlight.WritePreStart(w, language, "")
- } else {
- highlight.WritePreEnd(w)
- }
- }
- }),
- )
-}
-
-func writeDivStart(w util.BufWriter, ctx hl.CodeBlockContext) {
- w.WriteString(`")
-}
-
-func writeDivEnd(w util.BufWriter) {
- w.WriteString("
")
+func newCodeblocksExtender() goldmark.Extender {
+ // TODO1 check w.WriteString(`")
result = convertForConfig(c, cfg, `echo "Hugo Rocks!"`, "unknown")
- c.Assert(result, qt.Equals, "echo "Hugo Rocks!"\n
")
+ c.Assert(result, qt.Equals, "echo "Hugo Rocks!"\n
")
})
c.Run("Highlight lines, default config", func(c *qt.C) {
diff --git a/markup/goldmark/integration_test.go b/markup/goldmark/integration_test.go
index 4ace04f756b..4a3aefe86d6 100644
--- a/markup/goldmark/integration_test.go
+++ b/markup/goldmark/integration_test.go
@@ -132,6 +132,84 @@ title: "p1"
)
}
+// TODO1
+func TestHighlight(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+[markup]
+[markup.highlight]
+anchorLineNos = false
+codeFences = true
+guessSyntax = false
+hl_Lines = ''
+lineAnchors = ''
+lineNoStart = 1
+lineNos = false
+lineNumbersInTable = true
+noClasses = false
+style = 'monokai'
+tabWidth = 4
+-- layouts/_default/single.html --
+{{ .Content }}
+-- content/p1.md --
+---
+title: "p1"
+---
+
+## Code Fences
+
+§§§bash
+LINE1
+§§§
+
+## Code Fences No Lexer
+
+§§A§moo
+LINE1
+§§A§
+
+## Code Fences Simple Attributes
+
+§§A§bash { .myclass id="myid" }
+LINE1
+§§A§
+
+## Code Fences Line Numbers
+
+§§§bash {linenos=table,hl_lines=[8,"15-17"],linenostart=199}
+LINE1
+LINE2
+LINE3
+LINE4
+LINE5
+LINE6
+LINE7
+LINE8
+§§§
+
+
+
+`
+
+ // Code fences
+ files = strings.ReplaceAll(files, "§§§", "```")
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/p1/index.html",
+ //"",
+ //"Code Fences No Lexer\nLINE1\n
",
+ "lnt",
+ )
+}
+
func BenchmarkRenderHooks(b *testing.B) {
files := `
-- config.toml --
diff --git a/markup/goldmark/internal/render/context.go b/markup/goldmark/internal/render/context.go
new file mode 100644
index 00000000000..b18983ef3b5
--- /dev/null
+++ b/markup/goldmark/internal/render/context.go
@@ -0,0 +1,81 @@
+// Copyright 2022 The Hugo Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package render
+
+import (
+ "bytes"
+ "math/bits"
+
+ "github.com/gohugoio/hugo/identity"
+ "github.com/gohugoio/hugo/markup/converter"
+)
+
+type BufWriter struct {
+ *bytes.Buffer
+}
+
+const maxInt = 1<<(bits.UintSize-1) - 1
+
+func (b *BufWriter) Available() int {
+ return maxInt
+}
+
+func (b *BufWriter) Buffered() int {
+ return b.Len()
+}
+
+func (b *BufWriter) Flush() error {
+ return nil
+}
+
+type Context struct {
+ *BufWriter
+ positions []int
+ ContextData
+}
+
+func (ctx *Context) PushPos(n int) {
+ ctx.positions = append(ctx.positions, n)
+}
+
+func (ctx *Context) PopPos() int {
+ i := len(ctx.positions) - 1
+ p := ctx.positions[i]
+ ctx.positions = ctx.positions[:i]
+ return p
+}
+
+type ContextData interface {
+ RenderContext() converter.RenderContext
+ DocumentContext() converter.DocumentContext
+ AddIdentity(id identity.Provider)
+}
+
+type RenderContextDataHolder struct {
+ Rctx converter.RenderContext
+ Dctx converter.DocumentContext
+ IDs identity.Manager
+}
+
+func (ctx *RenderContextDataHolder) RenderContext() converter.RenderContext {
+ return ctx.Rctx
+}
+
+func (ctx *RenderContextDataHolder) DocumentContext() converter.DocumentContext {
+ return ctx.Dctx
+}
+
+func (ctx *RenderContextDataHolder) AddIdentity(id identity.Provider) {
+ ctx.IDs.Add(id)
+}
diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go
index 1862c212543..ef6245a0fbd 100644
--- a/markup/goldmark/render_hooks.go
+++ b/markup/goldmark/render_hooks.go
@@ -16,11 +16,10 @@ package goldmark
import (
"bytes"
"strings"
- "sync"
-
- "github.com/spf13/cast"
"github.com/gohugoio/hugo/markup/converter/hooks"
+ "github.com/gohugoio/hugo/markup/goldmark/internal/render"
+ "github.com/gohugoio/hugo/markup/internal/attributes"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
@@ -44,28 +43,6 @@ func newLinks() goldmark.Extender {
return &links{}
}
-type attributesHolder struct {
- // What we get from Goldmark.
- astAttributes []ast.Attribute
-
- // What we send to the the render hooks.
- attributesInit sync.Once
- attributes map[string]string
-}
-
-func (a *attributesHolder) Attributes() map[string]string {
- a.attributesInit.Do(func() {
- a.attributes = make(map[string]string)
- for _, attr := range a.astAttributes {
- if strings.HasPrefix(string(attr.Name), "on") {
- continue
- }
- a.attributes[string(attr.Name)] = string(util.EscapeHTML(attr.Value.([]byte)))
- }
- })
- return a.attributes
-}
-
type linkContext struct {
page interface{}
destination string
@@ -104,7 +81,7 @@ type headingContext struct {
anchor string
text string
plainText string
- *attributesHolder
+ *attributes.AttributesHolder
}
func (ctx headingContext) Page() interface{} {
@@ -143,52 +120,17 @@ func (r *hookedRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer)
reg.Register(ast.KindHeading, r.renderHeading)
}
-func (r *hookedRenderer) renderAttributesForNode(w util.BufWriter, node ast.Node) {
- renderAttributes(w, false, node.Attributes()...)
-}
-
-// Attributes with special meaning that does not make sense to render in HTML.
-var attributeExcludes = map[string]bool{
- "hl_lines": true,
- "hl_style": true,
- "linenos": true,
- "linenostart": true,
-}
-
-func renderAttributes(w util.BufWriter, skipClass bool, attributes ...ast.Attribute) {
- for _, attr := range attributes {
- if skipClass && bytes.Equal(attr.Name, []byte("class")) {
- continue
- }
-
- a := strings.ToLower(string(attr.Name))
- if attributeExcludes[a] || strings.HasPrefix(a, "on") {
- continue
- }
-
- _, _ = w.WriteString(" ")
- _, _ = w.Write(attr.Name)
- _, _ = w.WriteString(`="`)
-
- switch v := attr.Value.(type) {
- case []byte:
- _, _ = w.Write(util.EscapeHTML(v))
- default:
- w.WriteString(cast.ToString(v))
- }
-
- _ = w.WriteByte('"')
- }
-}
-
func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Image)
- var h hooks.Renderers
+ var lr hooks.LinkRenderer
- ctx, ok := w.(*renderContext)
+ ctx, ok := w.(*render.Context)
if ok {
- h = ctx.RenderContext().RenderHooks
- ok = h.ImageRenderer != nil
+ h := ctx.RenderContext().GetRenderer(hooks.ImageRendererType, nil)
+ ok = h != nil
+ if ok {
+ lr = h.(hooks.LinkRenderer)
+ }
}
if !ok {
@@ -197,15 +139,15 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
if entering {
// Store the current pos so we can capture the rendered text.
- ctx.pushPos(ctx.Buffer.Len())
+ ctx.PushPos(ctx.Buffer.Len())
return ast.WalkContinue, nil
}
- pos := ctx.popPos()
+ pos := ctx.PopPos()
text := ctx.Buffer.Bytes()[pos:]
ctx.Buffer.Truncate(pos)
- err := h.ImageRenderer.RenderLink(
+ err := lr.RenderLink(
w,
linkContext{
page: ctx.DocumentContext().Document,
@@ -216,7 +158,7 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
},
)
- ctx.AddIdentity(h.ImageRenderer)
+ ctx.AddIdentity(lr)
return ast.WalkContinue, err
}
@@ -250,12 +192,15 @@ func (r *hookedRenderer) renderImageDefault(w util.BufWriter, source []byte, nod
func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Link)
- var h hooks.Renderers
+ var lr hooks.LinkRenderer
- ctx, ok := w.(*renderContext)
+ ctx, ok := w.(*render.Context)
if ok {
- h = ctx.RenderContext().RenderHooks
- ok = h.LinkRenderer != nil
+ h := ctx.RenderContext().GetRenderer(hooks.LinkRendererType, nil)
+ ok = h != nil
+ if ok {
+ lr = h.(hooks.LinkRenderer)
+ }
}
if !ok {
@@ -264,15 +209,15 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No
if entering {
// Store the current pos so we can capture the rendered text.
- ctx.pushPos(ctx.Buffer.Len())
+ ctx.PushPos(ctx.Buffer.Len())
return ast.WalkContinue, nil
}
- pos := ctx.popPos()
+ pos := ctx.PopPos()
text := ctx.Buffer.Bytes()[pos:]
ctx.Buffer.Truncate(pos)
- err := h.LinkRenderer.RenderLink(
+ err := lr.RenderLink(
w,
linkContext{
page: ctx.DocumentContext().Document,
@@ -286,7 +231,7 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No
// TODO(bep) I have a working branch that fixes these rather confusing identity types,
// but for now it's important that it's not .GetIdentity() that's added here,
// to make sure we search the entire chain on changes.
- ctx.AddIdentity(h.LinkRenderer)
+ ctx.AddIdentity(lr)
return ast.WalkContinue, err
}
@@ -319,12 +264,15 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as
}
n := node.(*ast.AutoLink)
- var h hooks.Renderers
+ var lr hooks.LinkRenderer
- ctx, ok := w.(*renderContext)
+ ctx, ok := w.(*render.Context)
if ok {
- h = ctx.RenderContext().RenderHooks
- ok = h.LinkRenderer != nil
+ h := ctx.RenderContext().GetRenderer(hooks.LinkRendererType, nil)
+ ok = h != nil
+ if ok {
+ lr = h.(hooks.LinkRenderer)
+ }
}
if !ok {
@@ -337,7 +285,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as
url = "mailto:" + url
}
- err := h.LinkRenderer.RenderLink(
+ err := lr.RenderLink(
w,
linkContext{
page: ctx.DocumentContext().Document,
@@ -350,7 +298,7 @@ func (r *hookedRenderer) renderAutoLink(w util.BufWriter, source []byte, node as
// TODO(bep) I have a working branch that fixes these rather confusing identity types,
// but for now it's important that it's not .GetIdentity() that's added here,
// to make sure we search the entire chain on changes.
- ctx.AddIdentity(h.LinkRenderer)
+ ctx.AddIdentity(lr)
return ast.WalkContinue, err
}
@@ -383,12 +331,15 @@ func (r *hookedRenderer) renderAutoLinkDefault(w util.BufWriter, source []byte,
func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.Heading)
- var h hooks.Renderers
+ var hr hooks.HeadingRenderer
- ctx, ok := w.(*renderContext)
+ ctx, ok := w.(*render.Context)
if ok {
- h = ctx.RenderContext().RenderHooks
- ok = h.HeadingRenderer != nil
+ h := ctx.RenderContext().GetRenderer(hooks.HeadingRendererType, nil)
+ ok = h != nil
+ if ok {
+ hr = h.(hooks.HeadingRenderer)
+ }
}
if !ok {
@@ -397,11 +348,11 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
if entering {
// Store the current pos so we can capture the rendered text.
- ctx.pushPos(ctx.Buffer.Len())
+ ctx.PushPos(ctx.Buffer.Len())
return ast.WalkContinue, nil
}
- pos := ctx.popPos()
+ pos := ctx.PopPos()
text := ctx.Buffer.Bytes()[pos:]
ctx.Buffer.Truncate(pos)
// All ast.Heading nodes are guaranteed to have an attribute called "id"
@@ -409,7 +360,7 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
anchori, _ := n.AttributeString("id")
anchor := anchori.([]byte)
- err := h.HeadingRenderer.RenderHeading(
+ err := hr.RenderHeading(
w,
headingContext{
page: ctx.DocumentContext().Document,
@@ -417,11 +368,11 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
anchor: string(anchor),
text: string(text),
plainText: string(n.Text(source)),
- attributesHolder: &attributesHolder{astAttributes: n.Attributes()},
+ AttributesHolder: attributes.New(n.Attributes(), attributes.AttributesOwnerGeneral),
},
)
- ctx.AddIdentity(h.HeadingRenderer)
+ ctx.AddIdentity(hr)
return ast.WalkContinue, err
}
@@ -432,7 +383,7 @@ func (r *hookedRenderer) renderHeadingDefault(w util.BufWriter, source []byte, n
_, _ = w.WriteString("')
} else {
diff --git a/markup/goldmark/toc_test.go b/markup/goldmark/toc_test.go
index f8fcf79d4d9..6e080bf468d 100644
--- a/markup/goldmark/toc_test.go
+++ b/markup/goldmark/toc_test.go
@@ -18,6 +18,7 @@ import (
"strings"
"testing"
+ "github.com/gohugoio/hugo/markup/converter/hooks"
"github.com/gohugoio/hugo/markup/markup_config"
"github.com/gohugoio/hugo/common/loggers"
@@ -27,6 +28,8 @@ import (
qt "github.com/frankban/quicktest"
)
+var nopGetRenderer = func(t hooks.RendererType, id interface{}) interface{} { return nil }
+
func TestToc(t *testing.T) {
c := qt.New(t)
@@ -58,7 +61,7 @@ And then some.
c.Assert(err, qt.IsNil)
conv, err := p.New(converter.DocumentContext{})
c.Assert(err, qt.IsNil)
- b, err := conv.Convert(converter.RenderContext{Src: []byte(content), RenderTOC: true})
+ b, err := conv.Convert(converter.RenderContext{Src: []byte(content), RenderTOC: true, GetRenderer: nopGetRenderer})
c.Assert(err, qt.IsNil)
got := b.(converter.TableOfContentsProvider).TableOfContents().ToHTML(2, 3, false)
c.Assert(got, qt.Equals, `