mirror of
https://github.com/gohugoio/hugo.git
synced 2025-03-23 20:03:02 +00:00
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
This commit is contained in:
parent
77c7059ff8
commit
b2a827c52c
3 changed files with 69 additions and 24 deletions
|
@ -47,8 +47,7 @@ import (
|
||||||
// Provider is the package entry point.
|
// Provider is the package entry point.
|
||||||
var Provider converter.ProviderProvider = provide{}
|
var Provider converter.ProviderProvider = provide{}
|
||||||
|
|
||||||
type provide struct {
|
type provide struct{}
|
||||||
}
|
|
||||||
|
|
||||||
func (p provide) New(cfg converter.ProviderConfig) (converter.Provider, error) {
|
func (p provide) New(cfg converter.ProviderConfig) (converter.Provider, error) {
|
||||||
md := newMarkdown(cfg)
|
md := newMarkdown(cfg)
|
||||||
|
@ -199,10 +198,21 @@ func (b *bufWriter) Flush() error {
|
||||||
|
|
||||||
type renderContext struct {
|
type renderContext struct {
|
||||||
*bufWriter
|
*bufWriter
|
||||||
pos int
|
positions []int
|
||||||
renderContextData
|
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 {
|
type renderContextData interface {
|
||||||
RenderContext() converter.RenderContext
|
RenderContext() converter.RenderContext
|
||||||
DocumentContext() converter.DocumentContext
|
DocumentContext() converter.DocumentContext
|
||||||
|
|
|
@ -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) {
|
func BenchmarkSiteWithRenderHooks(b *testing.B) {
|
||||||
files := `
|
files := `
|
||||||
-- config.toml --
|
-- config.toml --
|
||||||
|
|
|
@ -144,16 +144,13 @@ func (r *hookedRenderer) renderAttributesForNode(w util.BufWriter, node ast.Node
|
||||||
renderAttributes(w, false, node.Attributes()...)
|
renderAttributes(w, false, node.Attributes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
|
|
||||||
// Attributes with special meaning that does not make sense to render in HTML.
|
// Attributes with special meaning that does not make sense to render in HTML.
|
||||||
attributeExcludes = map[string]bool{
|
var attributeExcludes = map[string]bool{
|
||||||
"hl_lines": true,
|
"hl_lines": true,
|
||||||
"hl_style": true,
|
"hl_style": true,
|
||||||
"linenos": true,
|
"linenos": true,
|
||||||
"linenostart": true,
|
"linenostart": true,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func renderAttributes(w util.BufWriter, skipClass bool, attributes ...ast.Attribute) {
|
func renderAttributes(w util.BufWriter, skipClass bool, attributes ...ast.Attribute) {
|
||||||
for _, attr := range attributes {
|
for _, attr := range attributes {
|
||||||
|
@ -197,12 +194,13 @@ func (r *hookedRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
|
||||||
|
|
||||||
if entering {
|
if entering {
|
||||||
// Store the current pos so we can capture the rendered text.
|
// 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
|
return ast.WalkContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
text := ctx.Buffer.Bytes()[ctx.pos:]
|
pos := ctx.popPos()
|
||||||
ctx.Buffer.Truncate(ctx.pos)
|
text := ctx.Buffer.Bytes()[pos:]
|
||||||
|
ctx.Buffer.Truncate(pos)
|
||||||
|
|
||||||
err := h.ImageRenderer.RenderLink(
|
err := h.ImageRenderer.RenderLink(
|
||||||
w,
|
w,
|
||||||
|
@ -263,12 +261,13 @@ func (r *hookedRenderer) renderLink(w util.BufWriter, source []byte, node ast.No
|
||||||
|
|
||||||
if entering {
|
if entering {
|
||||||
// Store the current pos so we can capture the rendered text.
|
// 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
|
return ast.WalkContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
text := ctx.Buffer.Bytes()[ctx.pos:]
|
pos := ctx.popPos()
|
||||||
ctx.Buffer.Truncate(ctx.pos)
|
text := ctx.Buffer.Bytes()[pos:]
|
||||||
|
ctx.Buffer.Truncate(pos)
|
||||||
|
|
||||||
err := h.LinkRenderer.RenderLink(
|
err := h.LinkRenderer.RenderLink(
|
||||||
w,
|
w,
|
||||||
|
@ -395,12 +394,13 @@ func (r *hookedRenderer) renderHeading(w util.BufWriter, source []byte, node ast
|
||||||
|
|
||||||
if entering {
|
if entering {
|
||||||
// Store the current pos so we can capture the rendered text.
|
// 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
|
return ast.WalkContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
text := ctx.Buffer.Bytes()[ctx.pos:]
|
pos := ctx.popPos()
|
||||||
ctx.Buffer.Truncate(ctx.pos)
|
text := ctx.Buffer.Bytes()[pos:]
|
||||||
|
ctx.Buffer.Truncate(pos)
|
||||||
// All ast.Heading nodes are guaranteed to have an attribute called "id"
|
// All ast.Heading nodes are guaranteed to have an attribute called "id"
|
||||||
// that is an array of bytes that encode a valid string.
|
// that is an array of bytes that encode a valid string.
|
||||||
anchori, _ := n.AttributeString("id")
|
anchori, _ := n.AttributeString("id")
|
||||||
|
@ -440,8 +440,7 @@ func (r *hookedRenderer) renderHeadingDefault(w util.BufWriter, source []byte, n
|
||||||
return ast.WalkContinue, nil
|
return ast.WalkContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type links struct {
|
type links struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// Extend implements goldmark.Extender.
|
// Extend implements goldmark.Extender.
|
||||||
func (e *links) Extend(m goldmark.Markdown) {
|
func (e *links) Extend(m goldmark.Markdown) {
|
||||||
|
|
Loading…
Reference in a new issue