Fix auto generated header ids so they don't contain e.g. hyperlink destinations (note)
This makes the header ids match the newly added dt ids. Also make sure newlines are preserved in hooks' `.PlainText`. Fixes #13405 Fixes #13410
This commit is contained in:
parent
a2ca95629a
commit
24cc25552f
6 changed files with 59 additions and 14 deletions
|
@ -350,3 +350,31 @@ Image: 
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 13410.
|
||||
func TestRenderHooksMultilineTitlePlainText(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: "p1"
|
||||
---
|
||||
|
||||
First line.
|
||||
Second line.
|
||||
----------------
|
||||
-- layouts/_default/_markup/render-heading.html --
|
||||
Plain text: {{ .PlainText }}|Text: {{ .Text }}|
|
||||
-- layouts/_default/single.html --
|
||||
Content: {{ .Content}}|
|
||||
}
|
||||
`
|
||||
b := Test(t, files)
|
||||
|
||||
b.AssertFileContent("public/p1/index.html",
|
||||
"Content: Plain text: First line.\nSecond line.|",
|
||||
"|Text: First line.\nSecond line.||\n",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ title: "p1"
|
|||
b := hugolib.Test(t, files)
|
||||
|
||||
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>",
|
||||
"<h2 id=\"hello-test\">\n Hello <a href=\"https://example.com\">Test</a>\n\n <a class=\"anchor\" href=\"#hello-test\">#</a>\n</h2>",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package attributes
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/goldmark/goldmark_config"
|
||||
"github.com/gohugoio/hugo/markup/goldmark/internal/render"
|
||||
"github.com/yuin/goldmark"
|
||||
|
@ -181,12 +183,17 @@ func (a *transformer) generateAutoID(n ast.Node, reader text.Reader, pc parser.C
|
|||
}
|
||||
|
||||
// Markdown settext headers can have multiple lines, use the last line for the ID.
|
||||
func textHeadingID(node *ast.Heading, reader text.Reader) []byte {
|
||||
var line []byte
|
||||
lastIndex := node.Lines().Len() - 1
|
||||
if lastIndex > -1 {
|
||||
lastLine := node.Lines().At(lastIndex)
|
||||
line = lastLine.Value(reader.Source())
|
||||
func textHeadingID(n *ast.Heading, reader text.Reader) []byte {
|
||||
text := render.TextPlain(n, reader.Source())
|
||||
if n.Lines().Len() > 1 {
|
||||
|
||||
// For multiline headings, Goldmark's extension for headings returns the last line.
|
||||
// We have a slightly different approach, but in most cases the end result should be the same.
|
||||
// Instead of looking at the text segments in Lines (see #13405 for issues with that),
|
||||
// we split the text above and use the last line.
|
||||
parts := strings.Split(text, "\n")
|
||||
text = parts[len(parts)-1]
|
||||
}
|
||||
return line
|
||||
|
||||
return []byte(text)
|
||||
}
|
||||
|
|
|
@ -47,10 +47,12 @@ foo [something](/a/b/) bar
|
|||
Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď
|
||||
: Testing accents.
|
||||
|
||||
Mutiline set text header
|
||||
Multiline set text header
|
||||
Second line
|
||||
---------------
|
||||
|
||||
## Example [hyperlink](https://example.com/) in a header
|
||||
|
||||
-- layouts/_default/single.html --
|
||||
{{ .Content }}|Identifiers: {{ .Fragments.Identifiers }}|
|
||||
`
|
||||
|
@ -68,7 +70,8 @@ Second line
|
|||
`<dt id="my-title-1">My Title</dt>`,
|
||||
`<dt id="term">良善天父</dt>`,
|
||||
`<dt id="a-a-a-a-a-a-c-c-c-c-c-c-c-c-d">Ā ā Ă ă Ą ą Ć ć Ĉ ĉ Ċ ċ Č č Ď</dt>`,
|
||||
`<h2 id="second-line">Mutiline set text header`,
|
||||
"|Identifiers: [a-a-a-a-a-a-c-c-c-c-c-c-c-c-d base-name base-name-1 foo-something-bar foobar my-title my-title-1 second-line term title-with-id title-with-id]|",
|
||||
`<h2 id="second-line">`,
|
||||
`<h2 id="example-hyperlink-in-a-header">`,
|
||||
"|Identifiers: [a-a-a-a-a-a-c-c-c-c-c-c-c-c-d base-name base-name-1 example-hyperlink-in-a-header foo-something-bar foobar my-title my-title-1 second-line term title-with-id title-with-id]|",
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2024 The Hugo Authors. All rights reserved.
|
||||
// Copyright 2025 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.
|
||||
|
@ -20,6 +20,7 @@ import (
|
|||
"sync"
|
||||
|
||||
bp "github.com/gohugoio/hugo/bufferpool"
|
||||
east "github.com/yuin/goldmark-emoji/ast"
|
||||
|
||||
htext "github.com/gohugoio/hugo/common/text"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
|
@ -282,6 +283,7 @@ func textPlainTo(c ast.Node, source []byte, buf *bytes.Buffer) {
|
|||
if c == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch c := c.(type) {
|
||||
case *ast.RawHTML:
|
||||
s := strings.TrimSpace(tpl.StripHTML(string(c.Segments.Value(source))))
|
||||
|
@ -290,6 +292,11 @@ func textPlainTo(c ast.Node, source []byte, buf *bytes.Buffer) {
|
|||
buf.Write(c.Value)
|
||||
case *ast.Text:
|
||||
buf.Write(c.Segment.Value(source))
|
||||
if c.HardLineBreak() || c.SoftLineBreak() {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
case *east.Emoji:
|
||||
buf.WriteString(string(c.ShortName))
|
||||
default:
|
||||
textPlainTo(c.FirstChild(), source, buf)
|
||||
}
|
||||
|
|
|
@ -239,12 +239,12 @@ title: p7 (emoji)
|
|||
|
||||
// image
|
||||
b.AssertFileContent("public/p3/index.html", `
|
||||
<li><a href="#an-image-kittenajpg">An image <img src="a.jpg" alt="kitten" /></a></li>
|
||||
<li><a href="#an-image-kitten">An image <img src="a.jpg" alt="kitten" /></a></li>
|
||||
`)
|
||||
|
||||
// raw html
|
||||
b.AssertFileContent("public/p4/index.html", `
|
||||
<li><a href="#some-spanrawspan-html">Some <span>raw</span> HTML</a></li>
|
||||
<li><a href="#some-raw-html">Some <span>raw</span> HTML</a></li>
|
||||
`)
|
||||
|
||||
// typographer
|
||||
|
|
Loading…
Add table
Reference in a new issue