From b2a827c52c91d9219306b5c996074d2e1ced5342 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 16 Feb 2022 13:44:09 +0100 Subject: [PATCH] markup/goldmark: Fix mangling of headers/links in render hooks MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ```bash name old time/op new time/op delta SiteWithRenderHooks-10 11.9ms ± 1% 11.9ms ± 1% ~ (p=0.486 n=4+4) name old alloc/op new alloc/op delta SiteWithRenderHooks-10 11.2MB ± 0% 11.3MB ± 0% +0.16% (p=0.029 n=4+4) name old allocs/op new allocs/op delta SiteWithRenderHooks-10 145k ± 0% 145k ± 0% +0.14% (p=0.029 n=4+4) ``` Fixes #9504 --- markup/goldmark/convert.go | 16 ++++++++--- markup/goldmark/integration_test.go | 36 +++++++++++++++++++++++++ markup/goldmark/render_hooks.go | 41 ++++++++++++++--------------- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go index dcaf8d3e..c547fe1e 100644 --- a/markup/goldmark/convert.go +++ b/markup/goldmark/convert.go @@ -47,8 +47,7 @@ import ( // Provider is the package entry point. var Provider converter.ProviderProvider = provide{} -type provide struct { -} +type provide struct{} func (p provide) New(cfg converter.ProviderConfig) (converter.Provider, error) { md := newMarkdown(cfg) @@ -199,10 +198,21 @@ func (b *bufWriter) Flush() error { type renderContext struct { *bufWriter - pos int + 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 diff --git a/markup/goldmark/integration_test.go b/markup/goldmark/integration_test.go index fd90a682..0f47f4ad 100644 --- a/markup/goldmark/integration_test.go +++ b/markup/goldmark/integration_test.go @@ -61,6 +61,42 @@ foo `) } +// Issue 9504 +func TestLinkInTitle(t *testing.T) { + t.Parallel() + + files := ` +-- config.toml -- +-- content/p1.md -- +--- +title: "p1" +--- +## Hello [Test](https://example.com) +-- layouts/_default/single.html -- +{{ .Content }} +-- layouts/_default/_markup/render-heading.html -- + + {{ .Text | safeHTML }} + # + +-- layouts/_default/_markup/render-link.html -- +{{ .Text | safeHTML }} + +` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: false, + }, + ).Build() + + b.AssertFileContent("public/p1/index.html", + "

\n Hello Test\n\n #\n

", + ) +} + func BenchmarkSiteWithRenderHooks(b *testing.B) { files := ` -- config.toml -- diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go index e6d959ab..5c600204 100644 --- a/markup/goldmark/render_hooks.go +++ b/markup/goldmark/render_hooks.go @@ -144,16 +144,13 @@ func (r *hookedRenderer) renderAttributesForNode(w util.BufWriter, node ast.Node renderAttributes(w, false, node.Attributes()...) } -var ( - - // Attributes with special meaning that does not make sense to render in HTML. - attributeExcludes = map[string]bool{ - "hl_lines": true, - "hl_style": true, - "linenos": true, - "linenostart": true, - } -) +// 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 { @@ -197,12 +194,13 @@ 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.pos = ctx.Buffer.Len() + ctx.pushPos(ctx.Buffer.Len()) return ast.WalkContinue, nil } - text := ctx.Buffer.Bytes()[ctx.pos:] - ctx.Buffer.Truncate(ctx.pos) + pos := ctx.popPos() + text := ctx.Buffer.Bytes()[pos:] + ctx.Buffer.Truncate(pos) err := h.ImageRenderer.RenderLink( w, @@ -263,12 +261,13 @@ 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.pos = ctx.Buffer.Len() + ctx.pushPos(ctx.Buffer.Len()) return ast.WalkContinue, nil } - text := ctx.Buffer.Bytes()[ctx.pos:] - ctx.Buffer.Truncate(ctx.pos) + pos := ctx.popPos() + text := ctx.Buffer.Bytes()[pos:] + ctx.Buffer.Truncate(pos) err := h.LinkRenderer.RenderLink( w, @@ -395,12 +394,13 @@ 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.pos = ctx.Buffer.Len() + ctx.pushPos(ctx.Buffer.Len()) return ast.WalkContinue, nil } - text := ctx.Buffer.Bytes()[ctx.pos:] - ctx.Buffer.Truncate(ctx.pos) + pos := ctx.popPos() + text := ctx.Buffer.Bytes()[pos:] + ctx.Buffer.Truncate(pos) // All ast.Heading nodes are guaranteed to have an attribute called "id" // that is an array of bytes that encode a valid string. anchori, _ := n.AttributeString("id") @@ -440,8 +440,7 @@ func (r *hookedRenderer) renderHeadingDefault(w util.BufWriter, source []byte, n return ast.WalkContinue, nil } -type links struct { -} +type links struct{} // Extend implements goldmark.Extender. func (e *links) Extend(m goldmark.Markdown) { -- 2.30.2