Skip to content

Commit

Permalink
New Page Param ".TableOfContentsCollection"
Browse files Browse the repository at this point in the history
Add new Page Param ".TableOfContentsCollection" with goldmark
This enables users to customize table of contents as they like
You can be used it in shortcodes but have to use with `{{< >}}`
(as non-markdown shortcode)

Addresses gohugoio#7095, gohugoio#6811, gohugoio#6081, gohugoio#225
  • Loading branch information
satotake committed Apr 4, 2020
1 parent 4a39564 commit 3aa7117
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 15 deletions.
28 changes: 23 additions & 5 deletions hugolib/page__per_output.go
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/gohugoio/hugo/tpl"

"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/gohugoio/hugo/output"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
Expand Down Expand Up @@ -101,12 +102,13 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
var hasShortcodeVariants bool

f := po.f
cp.contentPlaceholders, hasShortcodeVariants, err = p.shortcodeState.renderShortcodesForPage(p, f)
cp.contentPlaceholders, hasShortcodeVariants, err = p.shortcodeState.prerenderShortcodesForPage(p, f)
if err != nil {
return err
}

enableReuse := !(hasShortcodeVariants || cp.renderHooksHaveVariants)
// enableReuse := !(hasShortcodeVariants || cp.renderHooksHaveVariants || hasShortcodeVariants2)

if enableReuse {
// Reuse this for the other output formats.
Expand All @@ -132,28 +134,38 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err

if tocProvider, ok := r.(converter.TableOfContentsProvider); ok {
cfg := p.s.ContentSpec.Converters.GetMarkupConfig()
toc := tocProvider.TableOfContents()
cp.tableOfContents = template.HTML(
tocProvider.TableOfContents().ToHTML(
toc.ToHTML(
cfg.TableOfContents.StartLevel,
cfg.TableOfContents.EndLevel,
cfg.TableOfContents.Ordered,
),
)
cp.tableOfContentsHeaders = toc.Headers
} else {
tmpContent, tmpTableOfContents := helpers.ExtractTOC(cp.workContent)
cp.tableOfContents = helpers.BytesToHTML(tmpTableOfContents)
cp.workContent = tmpContent
}
}

err = p.shortcodeState.postrenderShortcodesForPage(p, f, cp.contentPlaceholders)

if err != nil {
return err
}

if cp.placeholdersEnabled {
// ToC was accessed via .Page.TableOfContents in the shortcode,
// at a time when the ToC wasn't ready.
cp.contentPlaceholders[tocShortcodePlaceholder] = string(cp.tableOfContents)
}

if p.cmap.hasNonMarkdownShortcode || cp.placeholdersEnabled {

// There are one or more replacement tokens to be replaced.

cp.workContent, err = replaceShortcodeTokens(cp.workContent, cp.contentPlaceholders)
if err != nil {
return err
Expand Down Expand Up @@ -260,9 +272,10 @@ type pageContentOutput struct {
contentPlaceholders map[string]string

// Content sections
content template.HTML
summary template.HTML
tableOfContents template.HTML
content template.HTML
summary template.HTML
tableOfContents template.HTML
tableOfContentsHeaders tableofcontents.Headers

truncated bool

Expand Down Expand Up @@ -332,6 +345,11 @@ func (p *pageContentOutput) TableOfContents() template.HTML {
return p.tableOfContents
}

func (p *pageContentOutput) TableOfContentsCollection() tableofcontents.Headers {
p.p.s.initInit(p.initMain, p.p)
return p.tableOfContentsHeaders
}

func (p *pageContentOutput) Truncated() bool {
if p.p.truncated {
return true
Expand Down
51 changes: 41 additions & 10 deletions hugolib/shortcode.go
Expand Up @@ -411,8 +411,7 @@ func (s *shortcodeHandler) hasShortcodes() bool {
return s != nil && len(s.shortcodes) > 0
}

func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format) (map[string]string, bool, error) {

func (s *shortcodeHandler) prerenderShortcodesForPage(p *pageState, f output.Format) (map[string]string, bool, error) {
rendered := make(map[string]string)

tplVariants := tpl.TemplateVariants{
Expand All @@ -423,19 +422,52 @@ func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format
var hasVariants bool

for _, v := range s.shortcodes {
s, more, err := renderShortcode(0, s.s, tplVariants, v, nil, p)
if err != nil {
err = p.parseError(_errors.Wrapf(err, "failed to render shortcode %q", v.name), p.source.parsed.Input(), v.pos)
return nil, false, err
}
hasVariants = hasVariants || more
rendered[v.placeholder] = s
if !v.insertPlaceholder() {
s, more, err := renderShortcode(0, s.s, tplVariants, v, nil, p)
if err != nil {
err = p.parseError(_errors.Wrapf(err, "failed to render shortcode %q", v.name), p.source.parsed.Input(), v.pos)
return nil, false, err
}
hasVariants = hasVariants || more
rendered[v.placeholder] = s

} else {
if !v.isInline {
_, found, more := s.s.Tmpl().LookupVariant(v.name, tplVariants)
if !found {
err := fmt.Errorf("Unable to locate template for shortcode %q in page %q", v.name, p.File().Path())
return nil, false, err
}
hasVariants = hasVariants || more
}
rendered[v.placeholder] = ""
}
}

return rendered, hasVariants, nil
}

// rendering after preparation of ToC
func (s *shortcodeHandler) postrenderShortcodesForPage(p *pageState, f output.Format, rendered map[string]string) error {
tplVariants := tpl.TemplateVariants{
Language: p.Language().Lang,
OutputFormat: f,
}

for _, v := range s.shortcodes {
if v.insertPlaceholder() {
s, _, err := renderShortcode(0, s.s, tplVariants, v, nil, p)
if err != nil {
err = p.parseError(_errors.Wrapf(err, "failed to render shortcode %q", v.name), p.source.parsed.Input(), v.pos)
return err
}
rendered[v.placeholder] = s
}
}

return nil
}

var errShortCodeIllegalState = errors.New("Illegal shortcode state")

func (s *shortcodeHandler) parseError(err error, input []byte, pos int) error {
Expand Down Expand Up @@ -592,7 +624,6 @@ Loop:
// Replace prefixed shortcode tokens with the real content.
// Note: This function will rewrite the input slice.
func replaceShortcodeTokens(source []byte, replacements map[string]string) ([]byte, error) {

if len(replacements) == 0 {
return source, nil
}
Expand Down
5 changes: 5 additions & 0 deletions hugolib/shortcode_page.go
Expand Up @@ -16,6 +16,7 @@ package hugolib
import (
"html/template"

"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/gohugoio/hugo/resources/page"
)

Expand Down Expand Up @@ -55,6 +56,10 @@ func (p *pageForShortcode) TableOfContents() template.HTML {
return p.toc
}

func (p *pageForShortcode) TableOfContentsCollection() tableofcontents.Headers {
return p.p.cp.tableOfContentsHeaders
}

// This is what is sent into the content render hooks (link, image).
type pageForRenderHooks struct {
page.PageWithoutContent
Expand Down
22 changes: 22 additions & 0 deletions hugolib/shortcode_test.go
Expand Up @@ -149,6 +149,28 @@ func TestShortcodeRelated(t *testing.T) {
CheckShortCodeMatch(t, "{{< a >}}", "0", wt)
}

func TestShortcodeTableOfContentsCollection(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
tem.AddTemplate(
"_internal/shortcodes/toc.html",
`{{ range $h1 := .Page.TableOfContentsCollection }}{{ $h1.ID }}{{ $h1.HTML }}{{ end }}`,
)
return nil
}

CheckShortCodeMatch(t, `# head1
# head2
{{< toc >}}
`, `
<h1 id="head1">head1</h1>
<h1 id="head2">head2</h1>`, wt)
}

func TestShortcodeInnerMarkup(t *testing.T) {
t.Parallel()
wt := func(tem tpl.TemplateManager) error {
Expand Down
6 changes: 6 additions & 0 deletions markup/tableofcontents/tableofcontents.go
Expand Up @@ -14,6 +14,7 @@
package tableofcontents

import (
"html/template"
"strings"
)

Expand All @@ -28,6 +29,11 @@ type Header struct {
Headers Headers
}

// HTML is produces safe, escaped HTML output of Text
func (h Header) HTML() template.HTML {
return template.HTML(h.Text)
}

// IsZero is true when no ID or Text is set.
func (h Header) IsZero() bool {
return h.ID == "" && h.Text == ""
Expand Down
2 changes: 2 additions & 0 deletions resources/page/page.go
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/gohugoio/hugo/compare"
"github.com/gohugoio/hugo/hugofs/files"

"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/gohugoio/hugo/navigation"
"github.com/gohugoio/hugo/related"
"github.com/gohugoio/hugo/resources/resource"
Expand Down Expand Up @@ -311,6 +312,7 @@ type SitesProvider interface {
// TableOfContentsProvider provides the table of contents for a Page.
type TableOfContentsProvider interface {
TableOfContents() template.HTML
TableOfContentsCollection() tableofcontents.Headers
}

// TranslationsProvider provides access to any translations.
Expand Down
5 changes: 5 additions & 0 deletions resources/page/page_nop.go
Expand Up @@ -32,6 +32,7 @@ import (

"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/related"
"github.com/gohugoio/hugo/resources/resource"
Expand Down Expand Up @@ -445,6 +446,10 @@ func (p *nopPage) TableOfContents() template.HTML {
return ""
}

func (p *nopPage) TableOfContentsCollection() tableofcontents.Headers {
return []tableofcontents.Header{}
}

func (p *nopPage) Title() string {
return ""
}
Expand Down
5 changes: 5 additions & 0 deletions resources/page/testhelpers_test.go
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/markup/tableofcontents"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/related"

Expand Down Expand Up @@ -525,6 +526,10 @@ func (p *testPage) TableOfContents() template.HTML {
panic("not implemented")
}

func (p *testPage) TableOfContentsCollection() tableofcontents.Headers {
panic("not implemented")
}

func (p *testPage) Title() string {
return p.title
}
Expand Down

0 comments on commit 3aa7117

Please sign in to comment.