mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
d2cfaede5b
commit
9e904d756b
10 changed files with 315 additions and 111 deletions
|
@ -64,11 +64,12 @@ func Puts(s string) string {
|
||||||
|
|
||||||
// VisitLinesAfter calls the given function for each line, including newlines, in the given string.
|
// VisitLinesAfter calls the given function for each line, including newlines, in the given string.
|
||||||
func VisitLinesAfter(s string, fn func(line string)) {
|
func VisitLinesAfter(s string, fn func(line string)) {
|
||||||
high := strings.Index(s, "\n")
|
high := strings.IndexRune(s, '\n')
|
||||||
for high != -1 {
|
for high != -1 {
|
||||||
fn(s[:high+1])
|
fn(s[:high+1])
|
||||||
s = s[high+1:]
|
s = s[high+1:]
|
||||||
high = strings.Index(s, "\n")
|
|
||||||
|
high = strings.IndexRune(s, '\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
if s != "" {
|
if s != "" {
|
||||||
|
|
|
@ -59,3 +59,18 @@ line 3`
|
||||||
c.Assert(collected, qt.DeepEquals, []string{"line 1\n", "line 2\n", "\n", "line 3"})
|
c.Assert(collected, qt.DeepEquals, []string{"line 1\n", "line 2\n", "\n", "line 3"})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkVisitLinesAfter(b *testing.B) {
|
||||||
|
const lines = `line 1
|
||||||
|
line 2
|
||||||
|
|
||||||
|
line 3`
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
VisitLinesAfter(lines, func(s string) {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ func (m *pageMap) newPageFromContentNode(n *contentNode, parentBucket *pagesMapB
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ps.shortcodeState = newShortcodeHandler(ps, ps.s, nil)
|
ps.shortcodeState = newShortcodeHandler(ps, ps.s)
|
||||||
|
|
||||||
if err := ps.mapContent(parentBucket, metaProvider); err != nil {
|
if err := ps.mapContent(parentBucket, metaProvider); err != nil {
|
||||||
return nil, ps.wrapError(err)
|
return nil, ps.wrapError(err)
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
qt "github.com/frankban/quicktest"
|
qt "github.com/frankban/quicktest"
|
||||||
"github.com/gohugoio/hugo/common/loggers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRenderHookEditNestedPartial(t *testing.T) {
|
func TestRenderHookEditNestedPartial(t *testing.T) {
|
||||||
|
@ -428,72 +427,3 @@ Image:
|
||||||
<p>html-image: image.jpg|Text: Hello<br> Goodbye|Plain: Hello GoodbyeEND</p>
|
<p>html-image: image.jpg|Text: Hello<br> Goodbye|Plain: Hello GoodbyeEND</p>
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderString(t *testing.T) {
|
|
||||||
b := newTestSitesBuilder(t)
|
|
||||||
|
|
||||||
b.WithTemplates("index.html", `
|
|
||||||
{{ $p := site.GetPage "p1.md" }}
|
|
||||||
{{ $optBlock := dict "display" "block" }}
|
|
||||||
{{ $optOrg := dict "markup" "org" }}
|
|
||||||
RSTART:{{ "**Bold Markdown**" | $p.RenderString }}:REND
|
|
||||||
RSTART:{{ "**Bold Block Markdown**" | $p.RenderString $optBlock }}:REND
|
|
||||||
RSTART:{{ "/italic org mode/" | $p.RenderString $optOrg }}:REND
|
|
||||||
RSTART:{{ "## Header2" | $p.RenderString }}:REND
|
|
||||||
|
|
||||||
|
|
||||||
`, "_default/_markup/render-heading.html", "Hook Heading: {{ .Level }}")
|
|
||||||
|
|
||||||
b.WithContent("p1.md", `---
|
|
||||||
title: "p1"
|
|
||||||
---
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
|
|
||||||
b.Build(BuildCfg{})
|
|
||||||
|
|
||||||
b.AssertFileContent("public/index.html", `
|
|
||||||
RSTART:<strong>Bold Markdown</strong>:REND
|
|
||||||
RSTART:<p><strong>Bold Block Markdown</strong></p>
|
|
||||||
RSTART:<em>italic org mode</em>:REND
|
|
||||||
RSTART:Hook Heading: 2:REND
|
|
||||||
`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/gohugoio/hugo/issues/6882
|
|
||||||
func TestRenderStringOnListPage(t *testing.T) {
|
|
||||||
renderStringTempl := `
|
|
||||||
{{ .RenderString "**Hello**" }}
|
|
||||||
`
|
|
||||||
b := newTestSitesBuilder(t)
|
|
||||||
b.WithContent("mysection/p1.md", `FOO`)
|
|
||||||
b.WithTemplates(
|
|
||||||
"index.html", renderStringTempl,
|
|
||||||
"_default/list.html", renderStringTempl,
|
|
||||||
"_default/single.html", renderStringTempl,
|
|
||||||
)
|
|
||||||
|
|
||||||
b.Build(BuildCfg{})
|
|
||||||
|
|
||||||
for _, filename := range []string{
|
|
||||||
"index.html",
|
|
||||||
"mysection/index.html",
|
|
||||||
"categories/index.html",
|
|
||||||
"tags/index.html",
|
|
||||||
"mysection/p1/index.html",
|
|
||||||
} {
|
|
||||||
b.AssertFileContent("public/"+filename, `<strong>Hello</strong>`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue 9433
|
|
||||||
func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
logger := loggers.NewWarningLogger()
|
|
||||||
b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
|
|
||||||
disableKinds = ["page", "section", "taxonomy", "term"]
|
|
||||||
`)
|
|
||||||
b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
|
|
||||||
b.BuildE(BuildCfg{})
|
|
||||||
b.Assert(int(logger.LogCounters().WarnCounter.Count()), qt.Equals, 0)
|
|
||||||
}
|
|
||||||
|
|
|
@ -336,7 +336,7 @@ func (p *pageState) HasShortcode(name string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.shortcodeState.nameSet[name]
|
return p.shortcodeState.hasName(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pageState) Site() page.Site {
|
func (p *pageState) Site() page.Site {
|
||||||
|
@ -610,13 +610,30 @@ func (p *pageState) getContentConverter() converter.Converter {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pageState) mapContent(bucket *pagesMapBucket, meta *pageMeta) error {
|
func (p *pageState) mapContent(bucket *pagesMapBucket, meta *pageMeta) error {
|
||||||
s := p.shortcodeState
|
p.cmap = &pageContentMap{
|
||||||
|
|
||||||
rn := &pageContentMap{
|
|
||||||
items: make([]any, 0, 20),
|
items: make([]any, 0, 20),
|
||||||
}
|
}
|
||||||
|
|
||||||
iter := p.source.parsed.Iterator()
|
return p.mapContentForResult(
|
||||||
|
p.source.parsed,
|
||||||
|
p.shortcodeState,
|
||||||
|
p.cmap,
|
||||||
|
meta.markup,
|
||||||
|
func(m map[string]interface{}) error {
|
||||||
|
return meta.setMetadata(bucket, p, m)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pageState) mapContentForResult(
|
||||||
|
result pageparser.Result,
|
||||||
|
s *shortcodeHandler,
|
||||||
|
rn *pageContentMap,
|
||||||
|
markup string,
|
||||||
|
withFrontMatter func(map[string]any) error,
|
||||||
|
) error {
|
||||||
|
|
||||||
|
iter := result.Iterator()
|
||||||
|
|
||||||
fail := func(err error, i pageparser.Item) error {
|
fail := func(err error, i pageparser.Item) error {
|
||||||
if fe, ok := err.(herrors.FileError); ok {
|
if fe, ok := err.(herrors.FileError); ok {
|
||||||
|
@ -660,9 +677,11 @@ Loop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := meta.setMetadata(bucket, p, m); err != nil {
|
if withFrontMatter != nil {
|
||||||
|
if err := withFrontMatter(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
frontMatterSet = true
|
frontMatterSet = true
|
||||||
|
|
||||||
|
@ -697,7 +716,7 @@ Loop:
|
||||||
p.source.posBodyStart = posBody
|
p.source.posBodyStart = posBody
|
||||||
p.source.hasSummaryDivider = true
|
p.source.hasSummaryDivider = true
|
||||||
|
|
||||||
if meta.markup != "html" {
|
if markup != "html" {
|
||||||
// The content will be rendered by Goldmark or similar,
|
// The content will be rendered by Goldmark or similar,
|
||||||
// and we need to track the summary.
|
// and we need to track the summary.
|
||||||
rn.AddReplacement(internalSummaryDividerPre, it)
|
rn.AddReplacement(internalSummaryDividerPre, it)
|
||||||
|
@ -720,7 +739,7 @@ Loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
if currShortcode.name != "" {
|
if currShortcode.name != "" {
|
||||||
s.nameSet[currShortcode.name] = true
|
s.addName(currShortcode.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currShortcode.params == nil {
|
if currShortcode.params == nil {
|
||||||
|
@ -752,16 +771,14 @@ Loop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !frontMatterSet {
|
if !frontMatterSet && withFrontMatter != nil {
|
||||||
// Page content without front matter. Assign default front matter from
|
// Page content without front matter. Assign default front matter from
|
||||||
// cascades etc.
|
// cascades etc.
|
||||||
if err := meta.setMetadata(bucket, p, nil); err != nil {
|
if err := withFrontMatter(nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.cmap = rn
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,12 @@ type pageContent struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the content to be processed by Goldmark or similar.
|
// returns the content to be processed by Goldmark or similar.
|
||||||
func (p pageContent) contentToRender(renderedShortcodes map[string]string) []byte {
|
func (p pageContent) contentToRender(parsed pageparser.Result, pm *pageContentMap, renderedShortcodes map[string]string) []byte {
|
||||||
source := p.source.parsed.Input()
|
source := parsed.Input()
|
||||||
|
|
||||||
c := make([]byte, 0, len(source)+(len(source)/10))
|
c := make([]byte, 0, len(source)+(len(source)/10))
|
||||||
|
|
||||||
for _, it := range p.cmap.items {
|
for _, it := range pm.items {
|
||||||
switch v := it.(type) {
|
switch v := it.(type) {
|
||||||
case pageparser.Item:
|
case pageparser.Item:
|
||||||
c = append(c, source[v.Pos:v.Pos+len(v.Val)]...)
|
c = append(c, source[v.Pos:v.Pos+len(v.Val)]...)
|
||||||
|
|
|
@ -25,9 +25,11 @@ import (
|
||||||
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/herrors"
|
||||||
"github.com/gohugoio/hugo/common/text"
|
"github.com/gohugoio/hugo/common/text"
|
||||||
"github.com/gohugoio/hugo/common/types/hstring"
|
"github.com/gohugoio/hugo/common/types/hstring"
|
||||||
"github.com/gohugoio/hugo/identity"
|
"github.com/gohugoio/hugo/identity"
|
||||||
|
"github.com/gohugoio/hugo/parser/pageparser"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ func newPageContentOutput(p *pageState, po *pageOutput) (*pageContentOutput, err
|
||||||
p.pageOutputTemplateVariationsState.Store(2)
|
p.pageOutputTemplateVariationsState.Store(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
cp.workContent = p.contentToRender(cp.contentPlaceholders)
|
cp.workContent = p.contentToRender(p.source.parsed, p.cmap, cp.contentPlaceholders)
|
||||||
|
|
||||||
isHTML := cp.p.m.markup == "html"
|
isHTML := cp.p.m.markup == "html"
|
||||||
|
|
||||||
|
@ -332,11 +334,12 @@ func (p *pageContentOutput) WordCount() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
|
func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
|
||||||
|
defer herrors.Recover()
|
||||||
if len(args) < 1 || len(args) > 2 {
|
if len(args) < 1 || len(args) > 2 {
|
||||||
return "", errors.New("want 1 or 2 arguments")
|
return "", errors.New("want 1 or 2 arguments")
|
||||||
}
|
}
|
||||||
|
|
||||||
var s string
|
var contentToRender string
|
||||||
opts := defaultRenderStringOpts
|
opts := defaultRenderStringOpts
|
||||||
sidx := 1
|
sidx := 1
|
||||||
|
|
||||||
|
@ -353,16 +356,16 @@ func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contentToRender := args[sidx]
|
contentToRenderv := args[sidx]
|
||||||
|
|
||||||
if _, ok := contentToRender.(hstring.RenderedString); ok {
|
if _, ok := contentToRenderv.(hstring.RenderedString); ok {
|
||||||
// This content is already rendered, this is potentially
|
// This content is already rendered, this is potentially
|
||||||
// a infinite recursion.
|
// a infinite recursion.
|
||||||
return "", errors.New("text is already rendered, repeating it may cause infinite recursion")
|
return "", errors.New("text is already rendered, repeating it may cause infinite recursion")
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s, err = cast.ToStringE(contentToRender)
|
contentToRender, err = cast.ToStringE(contentToRenderv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -381,20 +384,79 @@ func (p *pageContentOutput) RenderString(args ...any) (template.HTML, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := p.renderContentWithConverter(conv, []byte(s), false)
|
var rendered []byte
|
||||||
|
|
||||||
|
if strings.Contains(contentToRender, "{{") {
|
||||||
|
// Probably a shortcode.
|
||||||
|
parsed, err := pageparser.ParseMain(strings.NewReader(contentToRender), pageparser.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
pm := &pageContentMap{
|
||||||
|
items: make([]any, 0, 20),
|
||||||
|
}
|
||||||
|
s := newShortcodeHandler(p.p, p.p.s)
|
||||||
|
|
||||||
|
if err := p.p.mapContentForResult(
|
||||||
|
parsed,
|
||||||
|
s,
|
||||||
|
pm,
|
||||||
|
opts.Markup,
|
||||||
|
nil,
|
||||||
|
); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholders, hasShortcodeVariants, err := s.renderShortcodesForPage(p.p, p.f)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasShortcodeVariants {
|
||||||
|
p.p.pageOutputTemplateVariationsState.Store(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := p.renderContentWithConverter(conv, p.p.contentToRender(parsed, pm, placeholders), false)
|
||||||
|
if err != nil {
|
||||||
|
return "", p.p.wrapError(err)
|
||||||
|
}
|
||||||
|
rendered = b.Bytes()
|
||||||
|
|
||||||
|
if p.placeholdersEnabled {
|
||||||
|
// ToC was accessed via .Page.TableOfContents in the shortcode,
|
||||||
|
// at a time when the ToC wasn't ready.
|
||||||
|
if _, err := p.p.Content(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
placeholders[tocShortcodePlaceholder] = string(p.tableOfContents)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pm.hasNonMarkdownShortcode || p.placeholdersEnabled {
|
||||||
|
rendered, err = replaceShortcodeTokens(rendered, placeholders)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a consolidated view in $page.HasShortcode
|
||||||
|
p.p.shortcodeState.transferNames(s)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
c, err := p.renderContentWithConverter(conv, []byte(contentToRender), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", p.p.wrapError(err)
|
return "", p.p.wrapError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b := c.Bytes()
|
rendered = c.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
if opts.Display == "inline" {
|
if opts.Display == "inline" {
|
||||||
// We may have to rethink this in the future when we get other
|
// We may have to rethink this in the future when we get other
|
||||||
// renderers.
|
// renderers.
|
||||||
b = p.p.s.ContentSpec.TrimShortHTML(b)
|
rendered = p.p.s.ContentSpec.TrimShortHTML(rendered)
|
||||||
}
|
}
|
||||||
|
|
||||||
return template.HTML(string(b)), nil
|
return template.HTML(string(rendered)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pageContentOutput) RenderWithTemplateInfo(info tpl.Info, layout ...string) (template.HTML, error) {
|
func (p *pageContentOutput) RenderWithTemplateInfo(info tpl.Info, layout ...string) (template.HTML, error) {
|
||||||
|
|
162
hugolib/renderstring_test.go
Normal file
162
hugolib/renderstring_test.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
// Copyright 2022 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.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless requiredF by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package hugolib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
qt "github.com/frankban/quicktest"
|
||||||
|
"github.com/gohugoio/hugo/common/loggers"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenderString(t *testing.T) {
|
||||||
|
b := newTestSitesBuilder(t)
|
||||||
|
|
||||||
|
b.WithTemplates("index.html", `
|
||||||
|
{{ $p := site.GetPage "p1.md" }}
|
||||||
|
{{ $optBlock := dict "display" "block" }}
|
||||||
|
{{ $optOrg := dict "markup" "org" }}
|
||||||
|
RSTART:{{ "**Bold Markdown**" | $p.RenderString }}:REND
|
||||||
|
RSTART:{{ "**Bold Block Markdown**" | $p.RenderString $optBlock }}:REND
|
||||||
|
RSTART:{{ "/italic org mode/" | $p.RenderString $optOrg }}:REND
|
||||||
|
RSTART:{{ "## Header2" | $p.RenderString }}:REND
|
||||||
|
|
||||||
|
|
||||||
|
`, "_default/_markup/render-heading.html", "Hook Heading: {{ .Level }}")
|
||||||
|
|
||||||
|
b.WithContent("p1.md", `---
|
||||||
|
title: "p1"
|
||||||
|
---
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
|
||||||
|
b.AssertFileContent("public/index.html", `
|
||||||
|
RSTART:<strong>Bold Markdown</strong>:REND
|
||||||
|
RSTART:<p><strong>Bold Block Markdown</strong></p>
|
||||||
|
RSTART:<em>italic org mode</em>:REND
|
||||||
|
RSTART:Hook Heading: 2:REND
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/gohugoio/hugo/issues/6882
|
||||||
|
func TestRenderStringOnListPage(t *testing.T) {
|
||||||
|
renderStringTempl := `
|
||||||
|
{{ .RenderString "**Hello**" }}
|
||||||
|
`
|
||||||
|
b := newTestSitesBuilder(t)
|
||||||
|
b.WithContent("mysection/p1.md", `FOO`)
|
||||||
|
b.WithTemplates(
|
||||||
|
"index.html", renderStringTempl,
|
||||||
|
"_default/list.html", renderStringTempl,
|
||||||
|
"_default/single.html", renderStringTempl,
|
||||||
|
)
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
|
||||||
|
for _, filename := range []string{
|
||||||
|
"index.html",
|
||||||
|
"mysection/index.html",
|
||||||
|
"categories/index.html",
|
||||||
|
"tags/index.html",
|
||||||
|
"mysection/p1/index.html",
|
||||||
|
} {
|
||||||
|
b.AssertFileContent("public/"+filename, `<strong>Hello</strong>`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue 9433
|
||||||
|
func TestRenderStringOnPageNotBackedByAFile(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
logger := loggers.NewWarningLogger()
|
||||||
|
b := newTestSitesBuilder(t).WithLogger(logger).WithConfigFile("toml", `
|
||||||
|
disableKinds = ["page", "section", "taxonomy", "term"]
|
||||||
|
`)
|
||||||
|
b.WithTemplates("index.html", `{{ .RenderString "**Hello**" }}`).WithContent("p1.md", "")
|
||||||
|
b.BuildE(BuildCfg{})
|
||||||
|
b.Assert(int(logger.LogCounters().WarnCounter.Count()), qt.Equals, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderStringWithShortcode(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
filesTemplate := `
|
||||||
|
-- config.toml --
|
||||||
|
title = "Hugo Rocks!"
|
||||||
|
enableInlineShortcodes = true
|
||||||
|
-- content/p1/index.md --
|
||||||
|
---
|
||||||
|
title: "P1"
|
||||||
|
---
|
||||||
|
## First
|
||||||
|
-- layouts/shortcodes/mark1.md --
|
||||||
|
{{ .Inner }}
|
||||||
|
-- layouts/shortcodes/mark2.md --
|
||||||
|
1. Item Mark2 1
|
||||||
|
1. Item Mark2 2
|
||||||
|
1. Item Mark2 2-1
|
||||||
|
1. Item Mark2 3
|
||||||
|
-- layouts/shortcodes/myhthml.html --
|
||||||
|
Title: {{ .Page.Title }}
|
||||||
|
TableOfContents: {{ .Page.TableOfContents }}
|
||||||
|
Page Type: {{ printf "%T" .Page }}
|
||||||
|
-- layouts/_default/single.html --
|
||||||
|
{{ .RenderString "Markdown: {{% mark2 %}}|HTML: {{< myhthml >}}|Inline: {{< foo.inline >}}{{ site.Title }}{{< /foo.inline >}}|" }}
|
||||||
|
HasShortcode: mark2:{{ .HasShortcode "mark2" }}:true
|
||||||
|
HasShortcode: foo:{{ .HasShortcode "foo" }}:false
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
t.Run("Basic", func(t *testing.T) {
|
||||||
|
|
||||||
|
b := NewIntegrationTestBuilder(
|
||||||
|
IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: filesTemplate,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/p1/index.html",
|
||||||
|
"<p>Markdown: 1. Item Mark2 1</p>\n<ol>\n<li>Item Mark2 2\n<ol>\n<li>Item Mark2 2-1</li>\n</ol>\n</li>\n<li>Item Mark2 3|",
|
||||||
|
"<a href=\"#first\">First</a>", // ToC
|
||||||
|
`
|
||||||
|
HTML: Title: P1
|
||||||
|
Inline: Hugo Rocks!
|
||||||
|
HasShortcode: mark2:true:true
|
||||||
|
HasShortcode: foo:false:false
|
||||||
|
Page Type: *hugolib.pageForShortcode`,
|
||||||
|
)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Edit shortcode", func(t *testing.T) {
|
||||||
|
|
||||||
|
b := NewIntegrationTestBuilder(
|
||||||
|
IntegrationTestConfig{
|
||||||
|
T: t,
|
||||||
|
TxtarString: filesTemplate,
|
||||||
|
Running: true,
|
||||||
|
},
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
b.EditFiles("layouts/shortcodes/myhthml.html", "Edit shortcode").Build()
|
||||||
|
|
||||||
|
b.AssertFileContent("public/p1/index.html",
|
||||||
|
`Edit shortcode`,
|
||||||
|
)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -251,12 +251,13 @@ type shortcodeHandler struct {
|
||||||
|
|
||||||
// All the shortcode names in this set.
|
// All the shortcode names in this set.
|
||||||
nameSet map[string]bool
|
nameSet map[string]bool
|
||||||
|
nameSetMu sync.RWMutex
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
enableInlineShortcodes bool
|
enableInlineShortcodes bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newShortcodeHandler(p *pageState, s *Site, placeholderFunc func() string) *shortcodeHandler {
|
func newShortcodeHandler(p *pageState, s *Site) *shortcodeHandler {
|
||||||
sh := &shortcodeHandler{
|
sh := &shortcodeHandler{
|
||||||
p: p,
|
p: p,
|
||||||
s: s,
|
s: s,
|
||||||
|
@ -423,6 +424,28 @@ func (s *shortcodeHandler) hasShortcodes() bool {
|
||||||
return s != nil && len(s.shortcodes) > 0
|
return s != nil && len(s.shortcodes) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *shortcodeHandler) addName(name string) {
|
||||||
|
s.nameSetMu.Lock()
|
||||||
|
defer s.nameSetMu.Unlock()
|
||||||
|
s.nameSet[name] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shortcodeHandler) transferNames(in *shortcodeHandler) {
|
||||||
|
s.nameSetMu.Lock()
|
||||||
|
defer s.nameSetMu.Unlock()
|
||||||
|
for k := range in.nameSet {
|
||||||
|
s.nameSet[k] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *shortcodeHandler) hasName(name string) bool {
|
||||||
|
s.nameSetMu.RLock()
|
||||||
|
defer s.nameSetMu.RUnlock()
|
||||||
|
_, ok := s.nameSet[name]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format) (map[string]string, bool, error) {
|
func (s *shortcodeHandler) renderShortcodesForPage(p *pageState, f output.Format) (map[string]string, bool, error) {
|
||||||
rendered := make(map[string]string)
|
rendered := make(map[string]string)
|
||||||
|
|
||||||
|
@ -503,7 +526,7 @@ Loop:
|
||||||
nested, err := s.extractShortcode(nestedOrdinal, nextLevel, pt)
|
nested, err := s.extractShortcode(nestedOrdinal, nextLevel, pt)
|
||||||
nestedOrdinal++
|
nestedOrdinal++
|
||||||
if nested != nil && nested.name != "" {
|
if nested != nil && nested.name != "" {
|
||||||
s.nameSet[nested.name] = true
|
s.addName(nested.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -107,15 +107,9 @@ title: "Shortcodes Galore!"
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
c := qt.New(t)
|
c := qt.New(t)
|
||||||
|
|
||||||
counter := 0
|
|
||||||
placeholderFunc := func() string {
|
|
||||||
counter++
|
|
||||||
return fmt.Sprintf("HAHA%s-%dHBHB", shortcodePlaceholderPrefix, counter)
|
|
||||||
}
|
|
||||||
|
|
||||||
p, err := pageparser.ParseMain(strings.NewReader(test.input), pageparser.Config{})
|
p, err := pageparser.ParseMain(strings.NewReader(test.input), pageparser.Config{})
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
handler := newShortcodeHandler(nil, s, placeholderFunc)
|
handler := newShortcodeHandler(nil, s)
|
||||||
iter := p.Iterator()
|
iter := p.Iterator()
|
||||||
|
|
||||||
short, err := handler.extractShortcode(0, 0, iter)
|
short, err := handler.extractShortcode(0, 0, iter)
|
||||||
|
|
Loading…
Reference in a new issue