Skip to content

Commit b529859

Browse files
jmooringbep
authored andcommitted
markup/tableofcontents: Cast Fragments.ToHTML args to int
Closes #13107
1 parent 487bb96 commit b529859

File tree

5 files changed

+120
-22
lines changed

5 files changed

+120
-22
lines changed

hugolib/page__content.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -730,16 +730,15 @@ func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfCont
730730
isHTML := cp.po.p.m.pageConfig.ContentMediaType.IsHTML()
731731

732732
if !isHTML {
733-
createAndSetToC := func(tocProvider converter.TableOfContentsProvider) {
733+
createAndSetToC := func(tocProvider converter.TableOfContentsProvider) error {
734734
cfg := p.s.ContentSpec.Converters.GetMarkupConfig()
735735
ct.tableOfContents = tocProvider.TableOfContents()
736-
ct.tableOfContentsHTML = template.HTML(
737-
ct.tableOfContents.ToHTML(
738-
cfg.TableOfContents.StartLevel,
739-
cfg.TableOfContents.EndLevel,
740-
cfg.TableOfContents.Ordered,
741-
),
736+
ct.tableOfContentsHTML, err = ct.tableOfContents.ToHTML(
737+
cfg.TableOfContents.StartLevel,
738+
cfg.TableOfContents.EndLevel,
739+
cfg.TableOfContents.Ordered,
742740
)
741+
return err
743742
}
744743

745744
// If the converter supports doing the parsing separately, we do that.

markup/goldmark/convert_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,8 @@ unsafe = true
208208

209209
toc, ok := b.(converter.TableOfContentsProvider)
210210
c.Assert(ok, qt.Equals, true)
211-
tocString := string(toc.TableOfContents().ToHTML(1, 2, false))
212-
c.Assert(tocString, qt.Contains, "TableOfContents")
211+
tocHTML, _ := toc.TableOfContents().ToHTML(1, 2, false)
212+
c.Assert(string(tocHTML), qt.Contains, "TableOfContents")
213213
}
214214

215215
func TestConvertAutoIDAsciiOnly(t *testing.T) {

markup/tableofcontents/tableofcontents.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
package tableofcontents
1515

1616
import (
17+
"fmt"
1718
"html/template"
1819
"sort"
1920
"strings"
2021

2122
"github.com/gohugoio/hugo/common/collections"
23+
"github.com/spf13/cast"
2224
)
2325

2426
// Empty is an empty ToC.
@@ -133,19 +135,30 @@ func (toc *Fragments) addAt(h *Heading, row, level int) {
133135
}
134136

135137
// ToHTML renders the ToC as HTML.
136-
func (toc *Fragments) ToHTML(startLevel, stopLevel int, ordered bool) template.HTML {
138+
func (toc *Fragments) ToHTML(startLevel, stopLevel any, ordered bool) (template.HTML, error) {
137139
if toc == nil {
138-
return ""
140+
return "", nil
139141
}
142+
143+
iStartLevel, err := cast.ToIntE(startLevel)
144+
if err != nil {
145+
return "", fmt.Errorf("startLevel: %w", err)
146+
}
147+
148+
iStopLevel, err := cast.ToIntE(stopLevel)
149+
if err != nil {
150+
return "", fmt.Errorf("stopLevel: %w", err)
151+
}
152+
140153
b := &tocBuilder{
141154
s: strings.Builder{},
142155
h: toc.Headings,
143-
startLevel: startLevel,
144-
stopLevel: stopLevel,
156+
startLevel: iStartLevel,
157+
stopLevel: iStopLevel,
145158
ordered: ordered,
146159
}
147160
b.Build()
148-
return template.HTML(b.s.String())
161+
return template.HTML(b.s.String()), nil
149162
}
150163

151164
func (toc Fragments) walk(fn func(*Heading)) {

markup/tableofcontents/tableofcontents_integration_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package tableofcontents_test
1515

1616
import (
17+
"strings"
1718
"testing"
1819

1920
"github.com/gohugoio/hugo/hugolib"
@@ -43,3 +44,80 @@ disableKinds = ['page','rss','section','sitemap','taxonomy','term']
4344
"heading-l5|5|Heading L5",
4445
)
4546
}
47+
48+
// Issue #13107
49+
func TestToHTMLArgTypes(t *testing.T) {
50+
t.Parallel()
51+
52+
files := `
53+
-- hugo.toml --
54+
disableKinds = ['home','section','rss','sitemap','taxonomy','term']
55+
-- layouts/_default/single.html --
56+
{{ .Fragments.ToHTML .Params.toc.startLevel .Params.toc.endLevel false }}
57+
-- content/json.md --
58+
{
59+
"title": "json",
60+
"params": {
61+
"toc": {
62+
"startLevel": 2,
63+
"endLevel": 4
64+
}
65+
}
66+
}
67+
CONTENT
68+
-- content/toml.md --
69+
+++
70+
title = 'toml'
71+
[params.toc]
72+
startLevel = 2
73+
endLevel = 4
74+
+++
75+
CONTENT
76+
-- content/yaml.md --
77+
---
78+
title: yaml
79+
params:
80+
toc:
81+
startLevel: 2
82+
endLevel: 4
83+
---
84+
CONTENT
85+
`
86+
87+
content := `
88+
# Level One
89+
## Level Two
90+
### Level Three
91+
#### Level Four
92+
##### Level Five
93+
###### Level Six
94+
`
95+
96+
want := `
97+
<nav id="TableOfContents">
98+
<ul>
99+
<li><a href="#level-two">Level Two</a>
100+
<ul>
101+
<li><a href="#level-three">Level Three</a>
102+
<ul>
103+
<li><a href="#level-four">Level Four</a></li>
104+
</ul>
105+
</li>
106+
</ul>
107+
</li>
108+
</ul>
109+
</nav>
110+
`
111+
112+
files = strings.ReplaceAll(files, "CONTENT", content)
113+
114+
b := hugolib.Test(t, files)
115+
b.AssertFileContentEquals("public/json/index.html", strings.TrimSpace(want))
116+
b.AssertFileContentEquals("public/toml/index.html", strings.TrimSpace(want))
117+
b.AssertFileContentEquals("public/yaml/index.html", strings.TrimSpace(want))
118+
119+
files = strings.ReplaceAll(files, `2`, `"x"`)
120+
121+
b, _ = hugolib.TestE(t, files)
122+
b.AssertLogMatches(`error calling ToHTML: startLevel: unable to cast "x" of type string`)
123+
}

markup/tableofcontents/tableofcontents_test.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func TestToc(t *testing.T) {
4545
toc.addAt(&Heading{Title: "1-H3-1", ID: "1-h2-2"}, 0, 2)
4646
toc.addAt(&Heading{Title: "Heading 2", ID: "h1-2"}, 1, 0)
4747

48-
got := string(toc.ToHTML(1, -1, false))
48+
tocHTML, _ := toc.ToHTML(1, -1, false)
49+
got := string(tocHTML)
4950
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
5051
<ul>
5152
<li><a href="#h1-1">Heading 1</a>
@@ -62,15 +63,17 @@ func TestToc(t *testing.T) {
6263
</ul>
6364
</nav>`, qt.Commentf(got))
6465

65-
got = string(toc.ToHTML(1, 1, false))
66+
tocHTML, _ = toc.ToHTML(1, 1, false)
67+
got = string(tocHTML)
6668
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
6769
<ul>
6870
<li><a href="#h1-1">Heading 1</a></li>
6971
<li><a href="#h1-2">Heading 2</a></li>
7072
</ul>
7173
</nav>`, qt.Commentf(got))
7274

73-
got = string(toc.ToHTML(1, 2, false))
75+
tocHTML, _ = toc.ToHTML(1, 2, false)
76+
got = string(tocHTML)
7477
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
7578
<ul>
7679
<li><a href="#h1-1">Heading 1</a>
@@ -83,15 +86,17 @@ func TestToc(t *testing.T) {
8386
</ul>
8487
</nav>`, qt.Commentf(got))
8588

86-
got = string(toc.ToHTML(2, 2, false))
89+
tocHTML, _ = toc.ToHTML(2, 2, false)
90+
got = string(tocHTML)
8791
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
8892
<ul>
8993
<li><a href="#1-h2-1">1-H2-1</a></li>
9094
<li><a href="#1-h2-2">1-H2-2</a></li>
9195
</ul>
9296
</nav>`, qt.Commentf(got))
9397

94-
got = string(toc.ToHTML(1, -1, true))
98+
tocHTML, _ = toc.ToHTML(1, -1, true)
99+
got = string(tocHTML)
95100
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
96101
<ol>
97102
<li><a href="#h1-1">Heading 1</a>
@@ -118,7 +123,8 @@ func TestTocMissingParent(t *testing.T) {
118123
toc.addAt(&Heading{Title: "H3", ID: "h3"}, 1, 2)
119124
toc.addAt(&Heading{Title: "H3", ID: "h3"}, 1, 2)
120125

121-
got := string(toc.ToHTML(1, -1, false))
126+
tocHTML, _ := toc.ToHTML(1, -1, false)
127+
got := string(tocHTML)
122128
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
123129
<ul>
124130
<li>
@@ -139,15 +145,17 @@ func TestTocMissingParent(t *testing.T) {
139145
</ul>
140146
</nav>`, qt.Commentf(got))
141147

142-
got = string(toc.ToHTML(3, 3, false))
148+
tocHTML, _ = toc.ToHTML(3, 3, false)
149+
got = string(tocHTML)
143150
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
144151
<ul>
145152
<li><a href="#h3">H3</a></li>
146153
<li><a href="#h3">H3</a></li>
147154
</ul>
148155
</nav>`, qt.Commentf(got))
149156

150-
got = string(toc.ToHTML(1, -1, true))
157+
tocHTML, _ = toc.ToHTML(1, -1, true)
158+
got = string(tocHTML)
151159
c.Assert(got, qt.Equals, `<nav id="TableOfContents">
152160
<ol>
153161
<li>

0 commit comments

Comments
 (0)