summaryrefslogtreecommitdiffstats
path: root/markup
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-02-16 13:44:09 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-02-16 17:46:30 +0100
commitb2a827c52c91d9219306b5c996074d2e1ced5342 (patch)
tree014c859f5a0ea31ea41d4a64d13398a974f2b4a1 /markup
parent77c7059ff832870c3920e87a87969b815e429a8a (diff)
markup/goldmark: Fix mangling of headers/links in render hooks
```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
Diffstat (limited to 'markup')
-rw-r--r--markup/goldmark/convert.go16
-rw-r--r--markup/goldmark/integration_test.go36
-rw-r--r--markup/goldmark/render_hooks.go41
3 files changed, 69 insertions, 24 deletions
diff --git a/markup/goldmark/convert.go b/markup/goldmark/convert.go
index dcaf8d3e1..c547fe1e0 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 fd90a6824..0f47f4ada 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 --
+<h{{ .Level }} id="{{ .Anchor | safeURL }}">
+ {{ .Text | safeHTML }}
+ <a class="anchor" href="#{{ .Anchor | safeURL }}">#</a>
+</h{{ .Level }}>
+-- layouts/_default/_markup/render-link.html --
+<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}>{{ .Text | safeHTML }}</a>
+
+`
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ NeedsOsFS: false,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/p1/index.html",
+ "<h2 id=\"hello-testhttpsexamplecom\">\n Hello <a href=\"https://example.com\">Test</a>\n\n <a class=\"anchor\" href=\"#hello-testhttpsexamplecom\">#</a>\n</h2>",
+ )
+}
+
func BenchmarkSiteWithRenderHooks(b *testing.B) {
files := `
-- config.toml --
diff --git a/markup/goldmark/render_hooks.go b/markup/goldmark/render_hooks.go
index e6d959abf..5c600204c 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) {