Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listitem render hook #9858

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ae364aa
ListItem Render Hook
alexanderhansen Apr 26, 2022
acc3263
Update Tests
alexanderhansen May 2, 2022
4a659ee
Merge branch 'gohugoio:master' into listitem-render-hook
alexanderhansen May 2, 2022
106caac
ListItem Render Hook
alexanderhansen Apr 26, 2022
e751003
Update Tests
alexanderhansen May 2, 2022
b143fa6
Merge branch 'listitem-render-hook' of https://github.com/alexanderha…
alexanderhansen May 2, 2022
5e0eba9
List Render Hook
alexanderhansen May 2, 2022
5130a3d
With ordered parameter
alexanderhansen May 2, 2022
c025e3f
ListItem has no attributes
alexanderhansen May 2, 2022
156e671
Test for attributes
alexanderhansen May 2, 2022
5757d1d
First and Last sibling check for listitems
alexanderhansen May 5, 2022
1c954b9
Borrowed from Goldmark
alexanderhansen May 5, 2022
0de16a5
ListItem Render Hook
alexanderhansen Apr 26, 2022
b04f772
Update Tests
alexanderhansen May 2, 2022
595c696
List Render Hook
alexanderhansen May 2, 2022
9d3616a
With ordered parameter
alexanderhansen May 2, 2022
5e38325
ListItem has no attributes
alexanderhansen May 2, 2022
6ccad18
Test for attributes
alexanderhansen May 2, 2022
433d9cf
First and Last sibling check for listitems
alexanderhansen May 5, 2022
600e5a5
Borrowed from Goldmark
alexanderhansen May 5, 2022
4266674
Merge branch 'listitem-render-hook' of https://github.com/alexanderha…
alexanderhansen May 5, 2022
9199981
docs
alexanderhansen May 5, 2022
db2a4ca
Delete devcontainer.json
alexanderhansen May 5, 2022
3cb278b
Merge branch 'gohugoio:master' into listitem-render-hook
alexanderhansen Aug 3, 2022
c85708b
Merge branch 'gohugoio:master' into listitem-render-hook
alexanderhansen Dec 8, 2022
398be2d
Merge branch 'gohugoio:master' into listitem-render-hook
alexanderhansen Apr 6, 2023
3b2b6b1
Add Context to Renderer
alexanderhansen Apr 6, 2023
967447f
Merge branch 'gohugoio:master' into listitem-render-hook
alexanderhansen Apr 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions docs/content/en/templates/render-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ The hook kinds currently supported are:
* `link`
* `heading`
* `codeblock`{{< new-in "0.93.0" >}}
* `list`{{< new-in "0.99.0" >}}
* `listitem`{{< new-in "0.99.0" >}}

You can define [Output-Format-](/templates/output-formats) and [language-](/content-management/multilingual/)specific templates if needed. Your `layouts` folder may look like this:

Expand Down Expand Up @@ -179,3 +181,98 @@ Page

Position
: Useful in error logging as it prints the filename and position (linenumber, column), e.g. `{{ errorf "error in code block: %s" .Position }}`.

## Render Hooks for Lists and List Items

{{< new-in "0.99.0" >}}

You can add a hook template for lists and list items e.g. for different output types

```goat { class="black f7" }
layouts
└── _default
└── _markup
└── render-listitem.html
└── render-listitem.json
└── render-list.html
└── render-list.json
```

The `render-list` template will receive this context:

Page
: The [Page](/variables/page/) being rendered.

Text
: The rendered (HTML) list content.

PlainText
: The plain variant of the above.

IsOrdered (bool)
: If this is an ordered list.

Parent
: The Parent node of the list.

Attributes (map) {{< new-in "0.82.0" >}}
: A map of attributes (e.g. `id`, `class`)


The `render-listitem` template will receive this context:

Page
: The [Page](/variables/page/) being rendered.

Text
: The rendered (HTML) list content.

PlainText
: The plain variant of the above.

IsFirst (bool)
: If this is the first item in the list.

IsLast (bool)
: If this is the last item in the list.

Parent
: The Parent node of the item, the list node.

### ListItem rendered as JSON-LD example:

```md
1. Do This
2. Then That
```

Here is a code example for how the render-listitem.json template could look:

{{< code file="layouts/_default/_markup/render-list.html" >}}
{{- if eq .Parent.IsOrdered true -}}
{
"@type": "HowToStep",
"text": "{{ .Text | plainify}}"
}{{ if not .IsLast }},{{ end }}{{ print "\n"}}
{{- else -}}
{{- if not .IsLast -}}
{{ printf "\"%s\",\n" .Text | plainify}}
{{- else -}}
{{ printf "\"%s\"" .Text | plainify}}
{{- end -}}
{{- end -}}
{{< /code >}}


The rendered html will be

```js
{
"@type": "HowToStep",
"text": "Do This"
},
{
"@type": "HowToStep",
"text": "Then That"
}
```
28 changes: 26 additions & 2 deletions hugolib/content_render_hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ Inner Block: {{ .Inner | .Page.RenderString (dict "display" "block" ) }}
b.WithTemplatesAdded("_default/_markup/render-image.html", `IMAGE: {{ .Page.Title }}||{{ .Destination | safeURL }}|Title: {{ .Title | safeHTML }}|Text: {{ .Text | safeHTML }}|END`)
b.WithTemplatesAdded("_default/_markup/render-heading.html", `HEADING: {{ .Page.Title }}||Level: {{ .Level }}|Anchor: {{ .Anchor | safeURL }}|Text: {{ .Text | safeHTML }}|Attributes: {{ .Attributes }}|END`)
b.WithTemplatesAdded("docs/_markup/render-heading.html", `Docs Level: {{ .Level }}|END`)

b.WithTemplatesAdded("_default/_markup/render-list.html", `LIST: {{ .Text | safeHTML }}|{{ .Attributes }}|{{ .IsOrdered }} `)
b.WithTemplatesAdded("_default/_markup/render-listitem.html", `LISTITEM: {{ .Text | safeHTML }} {{ .IsFirst }} {{ .IsLast }} `)
b.WithContent("customview/p1.md", `---
title: Custom View
---
Expand Down Expand Up @@ -195,6 +196,21 @@ title: Doc With Heading

# Docs lvl 1

`, "blog/p9.md", `---
title: With List Items
---
- Dog
- Cat
- Mouse **Fat**
- Bird
{.animal}
`, "blog/p10.md", `---
title: With Ordered List
---
1. Car
2. Boat
3. Plane
{.transportation}
`,
)

Expand All @@ -209,7 +225,7 @@ title: No Template
}
counters := &testCounters{}
b.Build(BuildCfg{testCounters: counters})
b.Assert(int(counters.contentRenderCounter), qt.Equals, 45)
b.Assert(int(counters.contentRenderCounter), qt.Equals, 47)

b.AssertFileContent("public/blog/p1/index.html", `
Cool Page|https://www.google.com|Title: Google's Homepage|Text: First Link|END
Expand Down Expand Up @@ -264,6 +280,14 @@ SHORT3|

// https://github.com/gohugoio/hugo/issues/7349
b.AssertFileContent("public/docs/p8/index.html", "Docs Level: 1")

b.AssertFileContent("public/blog/p9/index.html", "LISTITEM: Dog")
b.AssertFileContent("public/blog/p9/index.html", "LISTITEM: Cat")
b.AssertFileContent("public/blog/p9/index.html", "LISTITEM: Mouse <strong>Fat</strong>")
b.AssertFileContent("public/blog/p9/index.html", "class:animal")

b.AssertFileContent("public/blog/p10/index.html", "LIST:")
b.AssertFileContent("public/blog/p10/index.html", "class:transportation")
}

func TestRenderHooksDeleteTemplate(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions hugolib/page__per_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,10 @@ func (p *pageContentOutput) initRenderHooks() error {
layoutDescriptor.KindVariants = lang
}
}
case hooks.ListItemRendererType:
layoutDescriptor.Kind = "render-listitem"
case hooks.ListRendererType:
layoutDescriptor.Kind = "render-list"
}

getHookTemplate := func(f output.Format) (tpl.Template, bool) {
Expand Down
8 changes: 8 additions & 0 deletions hugolib/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -1810,6 +1810,14 @@ func (hr hookRendererTemplate) RenderCodeblock(cctx context.Context, w hugio.Fle
return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
}

func (hr hookRendererTemplate) RenderListItem(cctx context.Context, w io.Writer, ctx hooks.ListItemContext) error {
return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
}

func (hr hookRendererTemplate) RenderList(cctx context.Context, w io.Writer, ctx hooks.ListContext) error {
return hr.templateHandler.ExecuteWithContext(cctx, hr.templ, w, ctx)
}

func (hr hookRendererTemplate) ResolvePosition(ctx any) text.Position {
return hr.resolvePosition(ctx)
}
Expand Down
31 changes: 31 additions & 0 deletions markup/converter/hooks/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,35 @@ type HeadingRenderer interface {
identity.Provider
}

type ListItemContext interface {
Page() interface{}
Text() hstring.RenderedString
PlainText() string
IsFirst() bool
IsLast() bool
Parent() interface{}
}

type ListItemRenderer interface {
RenderListItem(cctx context.Context, w io.Writer, ctx ListItemContext) error
identity.Provider
}

type ListContext interface {
Page() interface{}
Text() hstring.RenderedString
PlainText() string
IsOrdered() bool
Parent() interface{}

AttributesProvider
}

type ListRenderer interface {
RenderList(cctx context.Context, w io.Writer, ctx ListContext) error
identity.Provider
}

// ElementPositionResolver provides a way to resolve the start Position
// of a markdown element in the original source document.
// This may be both slow and approximate, so should only be
Expand All @@ -139,6 +168,8 @@ const (
ImageRendererType
HeadingRendererType
CodeBlockRendererType
ListItemRendererType
ListRendererType
)

type GetRendererFunc func(t RendererType, id any) any
Loading