summaryrefslogtreecommitdiffstats
path: root/tpl/tplimpl
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-02-17 13:04:00 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-02-24 18:59:50 +0100
commit08fdca9d9365eaf1e496a12e2af5e18617bd0e66 (patch)
tree6c6942d1b74a4160d93a997860bafd52b92025f5 /tpl/tplimpl
parent2c20f5bc00b604e72b3b7e401fbdbf9447fe3470 (diff)
Add Markdown diagrams and render hooks for code blocks
You can now create custom hook templates for code blocks, either one for all (`render-codeblock.html`) or for a given code language (e.g. `render-codeblock-go.html`). We also used this new hook to add support for diagrams in Hugo: * Goat (Go ASCII Tool) is built-in and enabled by default; just create a fenced code block with the language `goat` and start draw your Ascii diagrams. * Another popular alternative for diagrams in Markdown, Mermaid (supported by GitHub), can also be implemented with a simple template. See the Hugo documentation for more information. Updates #7765 Closes #9538 Fixes #9553 Fixes #8520 Fixes #6702 Fixes #9558
Diffstat (limited to 'tpl/tplimpl')
-rw-r--r--tpl/tplimpl/embedded/templates/_default/_markup/render-codeblock-goat.html1
-rw-r--r--tpl/tplimpl/template.go24
-rw-r--r--tpl/tplimpl/template_funcs.go1
-rw-r--r--tpl/tplimpl/template_funcs_test.go245
-rw-r--r--tpl/tplimpl/template_info_test.go58
5 files changed, 65 insertions, 264 deletions
diff --git a/tpl/tplimpl/embedded/templates/_default/_markup/render-codeblock-goat.html b/tpl/tplimpl/embedded/templates/_default/_markup/render-codeblock-goat.html
new file mode 100644
index 000000000..7c2b99f3f
--- /dev/null
+++ b/tpl/tplimpl/embedded/templates/_default/_markup/render-codeblock-goat.html
@@ -0,0 +1 @@
+adf
diff --git a/tpl/tplimpl/template.go b/tpl/tplimpl/template.go
index 44b486404..706719278 100644
--- a/tpl/tplimpl/template.go
+++ b/tpl/tplimpl/template.go
@@ -281,15 +281,10 @@ func (t *templateExec) UnusedTemplates() []tpl.FileInfo {
for _, ts := range t.main.templates {
ti := ts.info
- if strings.HasPrefix(ti.name, "_internal/") {
- continue
- }
- if strings.HasPrefix(ti.name, "partials/inline/pagination") {
- // TODO(bep) we need to fix this. These are internal partials, but
- // they may also be defined in the project, which currently could
- // lead to some false negatives.
+ if strings.HasPrefix(ti.name, "_internal/") || ti.realFilename == "" {
continue
}
+
if _, found := t.templateUsageTracker[ti.name]; !found {
unused = append(unused, ti)
}
@@ -740,6 +735,7 @@ func (t *templateHandler) extractIdentifiers(line string) []string {
}
//go:embed embedded/templates/*
+//go:embed embedded/templates/_default/*
var embededTemplatesFs embed.FS
func (t *templateHandler) loadEmbedded() error {
@@ -757,9 +753,19 @@ func (t *templateHandler) loadEmbedded() error {
// to write the templates to Go files.
templ := string(bytes.ReplaceAll(templb, []byte("\r\n"), []byte("\n")))
name := strings.TrimPrefix(filepath.ToSlash(path), "embedded/templates/")
+ templateName := name
- if err := t.AddTemplate(internalPathPrefix+name, templ); err != nil {
- return err
+ // For the render hooks it does not make sense to preseve the
+ // double _indternal double book-keeping,
+ // just add it if its now provided by the user.
+ if !strings.Contains(path, "_default/_markup") {
+ templateName = internalPathPrefix + name
+ }
+
+ if _, found := t.Lookup(templateName); !found {
+ if err := t.AddTemplate(templateName, templ); err != nil {
+ return err
+ }
}
if aliases, found := embeddedTemplatesAliases[name]; found {
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
index 831b846d0..8692b9ee2 100644
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -38,6 +38,7 @@ import (
_ "github.com/gohugoio/hugo/tpl/crypto"
_ "github.com/gohugoio/hugo/tpl/data"
_ "github.com/gohugoio/hugo/tpl/debug"
+ _ "github.com/gohugoio/hugo/tpl/diagrams"
_ "github.com/gohugoio/hugo/tpl/encoding"
_ "github.com/gohugoio/hugo/tpl/fmt"
_ "github.com/gohugoio/hugo/tpl/hugo"
diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go
index 6d2587bf7..cb1aa6feb 100644
--- a/tpl/tplimpl/template_funcs_test.go
+++ b/tpl/tplimpl/template_funcs_test.go
@@ -11,223 +11,74 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package tplimpl
+package tplimpl_test
import (
- "bytes"
- "context"
"fmt"
- "path/filepath"
- "reflect"
+ "strings"
"testing"
- "time"
- "github.com/gohugoio/hugo/modules"
+ "github.com/gohugoio/hugo/hugolib"
- "github.com/gohugoio/hugo/resources/page"
-
- qt "github.com/frankban/quicktest"
- "github.com/gohugoio/hugo/common/hugo"
- "github.com/gohugoio/hugo/common/loggers"
- "github.com/gohugoio/hugo/config"
- "github.com/gohugoio/hugo/deps"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/langs"
- "github.com/gohugoio/hugo/langs/i18n"
- "github.com/gohugoio/hugo/tpl"
"github.com/gohugoio/hugo/tpl/internal"
- "github.com/gohugoio/hugo/tpl/partials"
- "github.com/spf13/afero"
)
-var logger = loggers.NewErrorLogger()
-
-func newTestConfig() config.Provider {
- v := config.New()
- v.Set("contentDir", "content")
- v.Set("dataDir", "data")
- v.Set("i18nDir", "i18n")
- v.Set("layoutDir", "layouts")
- v.Set("archetypeDir", "archetypes")
- v.Set("assetDir", "assets")
- v.Set("resourceDir", "resources")
- v.Set("publishDir", "public")
-
- langs.LoadLanguageSettings(v, nil)
- mod, err := modules.CreateProjectModule(v)
- if err != nil {
- panic(err)
- }
- v.Set("allModules", modules.Modules{mod})
-
- return v
-}
-
-func newDepsConfig(cfg config.Provider) deps.DepsCfg {
- l := langs.NewLanguage("en", cfg)
- return deps.DepsCfg{
- Language: l,
- Site: page.NewDummyHugoSite(cfg),
- Cfg: cfg,
- Fs: hugofs.NewMem(l),
- Logger: logger,
- TemplateProvider: DefaultTemplateProvider,
- TranslationProvider: i18n.NewTranslationProvider(),
- }
-}
-
func TestTemplateFuncsExamples(t *testing.T) {
t.Parallel()
- c := qt.New(t)
-
- workingDir := "/home/hugo"
-
- v := newTestConfig()
-
- v.Set("workingDir", workingDir)
- v.Set("multilingual", true)
- v.Set("contentDir", "content")
- v.Set("assetDir", "assets")
- v.Set("baseURL", "http://mysite.com/hugo/")
- v.Set("CurrentContentLanguage", langs.NewLanguage("en", v))
-
- fs := hugofs.NewMem(v)
- afero.WriteFile(fs.Source, filepath.Join(workingDir, "files", "README.txt"), []byte("Hugo Rocks!"), 0755)
-
- depsCfg := newDepsConfig(v)
- depsCfg.Fs = fs
- d, err := deps.New(depsCfg)
- defer d.Close()
- c.Assert(err, qt.IsNil)
-
- var data struct {
- Title string
- Section string
- Hugo map[string]interface{}
- Params map[string]interface{}
- }
-
- data.Title = "**BatMan**"
- data.Section = "blog"
- data.Params = map[string]interface{}{"langCode": "en"}
- data.Hugo = map[string]interface{}{"Version": hugo.MustParseVersion("0.36.1").Version()}
+ files := `
+-- config.toml --
+disableKinds=["home", "section", "taxonomy", "term", "sitemap", "robotsTXT"]
+ignoreErrors = ["my-err-id"]
+[outputs]
+home=["HTML"]
+-- layouts/partials/header.html --
+<title>Hugo Rocks!</title>
+-- files/README.txt --
+Hugo Rocks!
+-- content/blog/hugo-rocks.md --
+---
+title: "**BatMan**"
+---
+`
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ NeedsOsFS: true,
+ },
+ ).Build()
+
+ d := b.H.Sites[0].Deps
+
+ var (
+ templates []string
+ expected []string
+ )
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
ns := nsf(d)
for _, mm := range ns.MethodMappings {
- for i, example := range mm.Examples {
- in, expected := example[0], example[1]
- d.WithTemplate = func(templ tpl.TemplateManager) error {
- c.Assert(templ.AddTemplate("test", in), qt.IsNil)
- c.Assert(templ.AddTemplate("partials/header.html", "<title>Hugo Rocks!</title>"), qt.IsNil)
- return nil
- }
- c.Assert(d.LoadResources(), qt.IsNil)
-
- var b bytes.Buffer
- templ, _ := d.Tmpl().Lookup("test")
- c.Assert(d.Tmpl().Execute(templ, &b, &data), qt.IsNil)
- if b.String() != expected {
- t.Fatalf("%s[%d]: got %q expected %q", ns.Name, i, b.String(), expected)
+ for _, example := range mm.Examples {
+ if strings.Contains(example[0], "errorf") {
+ // This will fail the build, so skip for now.
+ continue
}
+ templates = append(templates, example[0])
+ expected = append(expected, example[1])
}
}
}
-}
-
-// TODO(bep) it would be dandy to put this one into the partials package, but
-// we have some package cycle issues to solve first.
-func TestPartialCached(t *testing.T) {
- t.Parallel()
-
- c := qt.New(t)
-
- partial := `Now: {{ now.UnixNano }}`
- name := "testing"
-
- var data struct{}
-
- v := newTestConfig()
-
- config := newDepsConfig(v)
-
- config.WithTemplate = func(templ tpl.TemplateManager) error {
- err := templ.AddTemplate("partials/"+name, partial)
- if err != nil {
- return err
- }
-
- return nil
- }
-
- de, err := deps.New(config)
- c.Assert(err, qt.IsNil)
- defer de.Close()
- c.Assert(de.LoadResources(), qt.IsNil)
-
- ns := partials.New(de)
- res1, err := ns.IncludeCached(context.Background(), name, &data)
- c.Assert(err, qt.IsNil)
+ files += fmt.Sprintf("-- layouts/_default/single.html --\n%s\n", strings.Join(templates, "\n"))
+ b = hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ NeedsOsFS: true,
+ },
+ ).Build()
- for j := 0; j < 10; j++ {
- time.Sleep(2 * time.Nanosecond)
- res2, err := ns.IncludeCached(context.Background(), name, &data)
- c.Assert(err, qt.IsNil)
-
- if !reflect.DeepEqual(res1, res2) {
- t.Fatalf("cache mismatch")
- }
-
- res3, err := ns.IncludeCached(context.Background(), name, &data, fmt.Sprintf("variant%d", j))
- c.Assert(err, qt.IsNil)
-
- if reflect.DeepEqual(res1, res3) {
- t.Fatalf("cache mismatch")
- }
- }
-}
-
-func BenchmarkPartial(b *testing.B) {
- doBenchmarkPartial(b, func(ns *partials.Namespace) error {
- _, err := ns.Include(context.Background(), "bench1")
- return err
- })
-}
-
-func BenchmarkPartialCached(b *testing.B) {
- doBenchmarkPartial(b, func(ns *partials.Namespace) error {
- _, err := ns.IncludeCached(context.Background(), "bench1", nil)
- return err
- })
-}
-
-func doBenchmarkPartial(b *testing.B, f func(ns *partials.Namespace) error) {
- c := qt.New(b)
- config := newDepsConfig(config.New())
- config.WithTemplate = func(templ tpl.TemplateManager) error {
- err := templ.AddTemplate("partials/bench1", `{{ shuffle (seq 1 10) }}`)
- if err != nil {
- return err
- }
-
- return nil
- }
-
- de, err := deps.New(config)
- c.Assert(err, qt.IsNil)
- defer de.Close()
- c.Assert(de.LoadResources(), qt.IsNil)
-
- ns := partials.New(de)
-
- b.ResetTimer()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if err := f(ns); err != nil {
- b.Fatalf("error executing template: %s", err)
- }
- }
- })
+ b.AssertFileContent("public/blog/hugo-rocks/index.html", expected...)
}
diff --git a/tpl/tplimpl/template_info_test.go b/tpl/tplimpl/template_info_test.go
deleted file mode 100644
index eaf57166a..000000000
--- a/tpl/tplimpl/template_info_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2019 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 required 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 tplimpl
-
-import (
- "testing"
-
- qt "github.com/frankban/quicktest"
- "github.com/gohugoio/hugo/deps"
- "github.com/gohugoio/hugo/hugofs"
- "github.com/gohugoio/hugo/tpl"
-)
-
-func TestTemplateInfoShortcode(t *testing.T) {
- c := qt.New(t)
- d := newD(c)
- defer d.Close()
- h := d.Tmpl().(*templateExec)
-
- c.Assert(h.AddTemplate("shortcodes/mytemplate.html", `
-{{ .Inner }}
-`), qt.IsNil)
-
- c.Assert(h.postTransform(), qt.IsNil)
-
- tt, found, _ := d.Tmpl().LookupVariant("mytemplate", tpl.TemplateVariants{})
-
- c.Assert(found, qt.Equals, true)
- tti, ok := tt.(tpl.Info)
- c.Assert(ok, qt.Equals, true)
- c.Assert(tti.ParseInfo().IsInner, qt.Equals, true)
-}
-
-// TODO(bep) move and use in other places
-func newD(c *qt.C) *deps.Deps {
- v := newTestConfig()
- fs := hugofs.NewMem(v)
-
- depsCfg := newDepsConfig(v)
- depsCfg.Fs = fs
- d, err := deps.New(depsCfg)
- c.Assert(err, qt.IsNil)
-
- provider := DefaultTemplateProvider
- provider.Update(d)
-
- return d
-}