mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
Revert "markup/tableofcontents: Skip empty TOC levels"
This reverts commit 7d4af7a179.
Closes #14898
This commit is contained in:
@@ -305,101 +305,6 @@ title: home
|
||||
b.AssertFileExists("public/index.html", true)
|
||||
}
|
||||
|
||||
func TestTableOfContentsSkippedHeadingLevelsIssue7128(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
filesTemplate := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
TOC_CONFIG
|
||||
-- layouts/page.html --
|
||||
{{ .TableOfContents }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
## Extra-Curriculars
|
||||
|
||||
#### Heading Four Here without Heading Three Before It
|
||||
|
||||
## Technology
|
||||
|
||||
#### Heading Four Here without Heading Three Before It
|
||||
`
|
||||
|
||||
t.Run("default end level", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := strings.ReplaceAll(filesTemplate, "TOC_CONFIG", "")
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContentExact("public/p1/index.html", `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#extra-curriculars">Extra-Curriculars</a></li>
|
||||
<li><a href="#technology">Technology</a></li>
|
||||
</ul>
|
||||
</nav>`)
|
||||
})
|
||||
|
||||
t.Run("include skipped level", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := strings.ReplaceAll(filesTemplate, "TOC_CONFIG", `
|
||||
[markup.tableOfContents]
|
||||
startLevel = 2
|
||||
endLevel = -1`)
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContentExact("public/p1/index.html", `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#extra-curriculars">Extra-Curriculars</a>
|
||||
<ul>
|
||||
<li><a href="#heading-four-here-without-heading-three-before-it">Heading Four Here without Heading Three Before It</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#technology">Technology</a>
|
||||
<ul>
|
||||
<li><a href="#heading-four-here-without-heading-three-before-it-1">Heading Four Here without Heading Three Before It</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`)
|
||||
})
|
||||
|
||||
t.Run("starts at h4", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
|
||||
[markup.tableOfContents]
|
||||
startLevel = 2
|
||||
endLevel = -1
|
||||
-- layouts/page.html --
|
||||
{{ .TableOfContents }}
|
||||
-- content/p1.md --
|
||||
---
|
||||
title: p1
|
||||
---
|
||||
#### Heading Four First
|
||||
|
||||
###### Heading Six After Heading Four
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files)
|
||||
|
||||
b.AssertFileContentExact("public/p1/index.html", `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#heading-four-first">Heading Four First</a>
|
||||
<ul>
|
||||
<li><a href="#heading-six-after-heading-four">Heading Six After Heading Four</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`)
|
||||
})
|
||||
}
|
||||
|
||||
// Issue 12605
|
||||
func TestTableOfContentsWithGoldmarkExtras(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -195,13 +195,23 @@ func (b *tocBuilder) Build() {
|
||||
|
||||
func (b *tocBuilder) writeNav(h Headings) {
|
||||
b.s.WriteString("<nav id=\"TableOfContents\">")
|
||||
b.writeHeadings(1, 0, h)
|
||||
b.writeHeadings(1, 0, b.h)
|
||||
b.s.WriteString("</nav>")
|
||||
}
|
||||
|
||||
func (b *tocBuilder) writeHeadings(level, indent int, h Headings) {
|
||||
headings := b.collectHeadings(level, h)
|
||||
hasChildren := len(headings) > 0
|
||||
if level < b.startLevel {
|
||||
for _, h := range h {
|
||||
b.writeHeadings(level+1, indent, h.Headings)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if b.stopLevel != -1 && level > b.stopLevel {
|
||||
return
|
||||
}
|
||||
|
||||
hasChildren := len(h) > 0
|
||||
|
||||
if hasChildren {
|
||||
b.s.WriteString("\n")
|
||||
@@ -213,8 +223,8 @@ func (b *tocBuilder) writeHeadings(level, indent int, h Headings) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, h := range headings {
|
||||
b.writeHeading(h.level, indent+2, h.h)
|
||||
for _, h := range h {
|
||||
b.writeHeading(level+1, indent+2, h)
|
||||
}
|
||||
|
||||
if hasChildren {
|
||||
@@ -229,35 +239,13 @@ func (b *tocBuilder) writeHeadings(level, indent int, h Headings) {
|
||||
}
|
||||
}
|
||||
|
||||
type tocHeading struct {
|
||||
h *Heading
|
||||
level int
|
||||
}
|
||||
|
||||
func (b *tocBuilder) collectHeadings(level int, h Headings) []tocHeading {
|
||||
var out []tocHeading
|
||||
|
||||
for _, h := range h {
|
||||
if h.IsZero() || level < b.startLevel {
|
||||
out = append(out, b.collectHeadings(level+1, h.Headings)...)
|
||||
continue
|
||||
}
|
||||
|
||||
if b.stopLevel != -1 && level > b.stopLevel {
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, tocHeading{h: h, level: level})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func (b *tocBuilder) writeHeading(level, indent int, h *Heading) {
|
||||
b.indent(indent)
|
||||
b.s.WriteString("<li>")
|
||||
b.s.WriteString("<a href=\"#" + h.ID + "\">" + h.Title + "</a>")
|
||||
b.writeHeadings(level+1, indent, h.Headings)
|
||||
if !h.IsZero() {
|
||||
b.s.WriteString("<a href=\"#" + h.ID + "\">" + h.Title + "</a>")
|
||||
}
|
||||
b.writeHeadings(level, indent, h.Headings)
|
||||
b.s.WriteString("</li>\n")
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
package tableofcontents
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
@@ -128,9 +127,21 @@ func TestTocMissingParent(t *testing.T) {
|
||||
got := string(tocHTML)
|
||||
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h2">H2</a></li>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
<li>
|
||||
<ul>
|
||||
<li><a href="#h2">H2</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul>
|
||||
<li>
|
||||
<ul>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`, qt.Commentf(got))
|
||||
|
||||
@@ -147,159 +158,25 @@ func TestTocMissingParent(t *testing.T) {
|
||||
got = string(tocHTML)
|
||||
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
|
||||
<ol>
|
||||
<li><a href="#h2">H2</a></li>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
<li>
|
||||
<ol>
|
||||
<li><a href="#h2">H2</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
<ol>
|
||||
<li>
|
||||
<ol>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
<li><a href="#h3">H3</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>`, qt.Commentf(got))
|
||||
}
|
||||
|
||||
func TestTocMissingIntermediateLevels(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
type item struct {
|
||||
title string
|
||||
id string
|
||||
row int
|
||||
level int
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
items []item
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "h2 to h4",
|
||||
items: []item{
|
||||
{title: "H2", id: "h2", level: 1},
|
||||
{title: "H4", id: "h4", level: 3},
|
||||
},
|
||||
expected: `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h2">H2</a>
|
||||
<ul>
|
||||
<li><a href="#h4">H4</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`,
|
||||
},
|
||||
{
|
||||
name: "h2 to h5",
|
||||
items: []item{
|
||||
{title: "H2", id: "h2", level: 1},
|
||||
{title: "H5", id: "h5", level: 4},
|
||||
},
|
||||
expected: `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h2">H2</a>
|
||||
<ul>
|
||||
<li><a href="#h5">H5</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`,
|
||||
},
|
||||
{
|
||||
name: "h2 to h6",
|
||||
items: []item{
|
||||
{title: "H2", id: "h2", level: 1},
|
||||
{title: "H6", id: "h6", level: 5},
|
||||
},
|
||||
expected: `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h2">H2</a>
|
||||
<ul>
|
||||
<li><a href="#h6">H6</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`,
|
||||
},
|
||||
{
|
||||
name: "h3 to h5",
|
||||
items: []item{
|
||||
{title: "H3", id: "h3", level: 2},
|
||||
{title: "H5", id: "h5", level: 4},
|
||||
},
|
||||
expected: `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h3">H3</a>
|
||||
<ul>
|
||||
<li><a href="#h5">H5</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>`,
|
||||
},
|
||||
{
|
||||
name: "starts at h4",
|
||||
items: []item{
|
||||
{title: "H4", id: "h4", level: 3},
|
||||
},
|
||||
expected: `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h4">H4</a></li>
|
||||
</ul>
|
||||
</nav>`,
|
||||
},
|
||||
{
|
||||
name: "starts at h6",
|
||||
items: []item{
|
||||
{title: "H6", id: "h6", level: 5},
|
||||
},
|
||||
expected: `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h6">H6</a></li>
|
||||
</ul>
|
||||
</nav>`,
|
||||
},
|
||||
} {
|
||||
c.Run(test.name, func(c *qt.C) {
|
||||
toc := &Fragments{}
|
||||
for _, item := range test.items {
|
||||
toc.addAt(&Heading{Title: item.title, ID: item.id}, item.row, item.level)
|
||||
}
|
||||
|
||||
tocHTML, err := toc.ToHTML(2, -1, false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
got := string(tocHTML)
|
||||
c.Assert(got, qt.Equals, test.expected, qt.Commentf(got))
|
||||
c.Assert(got, qt.Not(qt.Contains), "<li>\n")
|
||||
c.Assert(hasListItemWithoutAnchor(got), qt.Equals, false)
|
||||
})
|
||||
}
|
||||
|
||||
toc := &Fragments{}
|
||||
toc.addAt(&Heading{Title: "H2", ID: "h2"}, 0, 1)
|
||||
toc.addAt(&Heading{Title: "H4", ID: "h4"}, 0, 3)
|
||||
|
||||
tocHTML, err := toc.ToHTML(2, 3, false)
|
||||
c.Assert(err, qt.IsNil)
|
||||
got := string(tocHTML)
|
||||
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
|
||||
<ul>
|
||||
<li><a href="#h2">H2</a></li>
|
||||
</ul>
|
||||
</nav>`, qt.Commentf(got))
|
||||
c.Assert(got, qt.Not(qt.Contains), "<li>\n")
|
||||
c.Assert(hasListItemWithoutAnchor(got), qt.Equals, false)
|
||||
}
|
||||
|
||||
func hasListItemWithoutAnchor(s string) bool {
|
||||
for {
|
||||
i := strings.Index(s, "<li>")
|
||||
if i == -1 {
|
||||
return false
|
||||
}
|
||||
s = s[i+len("<li>"):]
|
||||
if !strings.HasPrefix(strings.TrimLeft(s, " \n\t\r"), "<a ") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTocMisc(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user