diff --git a/commands/genchromastyles.go b/commands/genchromastyles.go index a2231e56e..1f82d764e 100644 --- a/commands/genchromastyles.go +++ b/commands/genchromastyles.go @@ -68,7 +68,7 @@ func (g *genChromaStyles) generate() error { if err != nil { return err } - formatter := html.New(html.WithClasses()) + formatter := html.New(html.WithClasses(true)) formatter.WriteCSS(os.Stdout, style) return nil } diff --git a/docs/config.toml b/docs/config.toml index 6f66955ba..2170b20c4 100644 --- a/docs/config.toml +++ b/docs/config.toml @@ -15,18 +15,6 @@ pluralizeListTitles = false # We do redirects via Netlify's _redirects file, generated by Hugo (see "outputs" below). disableAliases = true -# Highlighting config (Pygments) -# It is (currently) not in use, but you can do ```go in a content file if you want to. -pygmentsCodeFences = true - -pygmentsOptions = "" -# Use the Chroma stylesheet -pygmentsUseClasses = true -pygmentsUseClassic = false - -# See https://help.farbox.com/pygments.html -pygmentsStyle = "trac" - [module] [module.hugoVersion] min = "0.56.0" diff --git a/docs/config/_default/config.toml b/docs/config/_default/config.toml index 9f908c9fd..ec0a08f56 100644 --- a/docs/config/_default/config.toml +++ b/docs/config/_default/config.toml @@ -14,17 +14,11 @@ pluralizeListTitles = false # We do redirects via Netlify's _redirects file, generated by Hugo (see "outputs" below). disableAliases = true -# Highlighting config (Pygments) -# It is (currently) not in use, but you can do ```go in a content file if you want to. -pygmentsCodeFences = true - -pygmentsOptions = "" -# Use the Chroma stylesheet -pygmentsUseClasses = true -pygmentsUseClassic = false - -# See https://help.farbox.com/pygments.html -pygmentsStyle = "trac" +[markup] +[markup.highlight] +style = "trac" +lineNumbersInTable = true +noClasses = false [outputs] home = [ "HTML", "RSS", "REDIR", "HEADERS" ] diff --git a/docs/content/en/about/features.md b/docs/content/en/about/features.md index cadcc6b0f..a1e741ac0 100644 --- a/docs/content/en/about/features.md +++ b/docs/content/en/about/features.md @@ -52,7 +52,7 @@ toc: true * Integrated [Google Analytics][] support * Automatic [RSS][] creation * Support for [Go][], [Amber], and [Ace][] HTML templates -* [Syntax highlighting][] powered by [Chroma][] (partly compatible with Pygments) +* [Syntax highlighting][] powered by [Chroma][] [Ace]: /templates/alternatives/ diff --git a/docs/content/en/content-management/formats.md b/docs/content/en/content-management/formats.md index a0ed992f2..ea0568616 100644 --- a/docs/content/en/content-management/formats.md +++ b/docs/content/en/content-management/formats.md @@ -1,6 +1,6 @@ --- -title: Supported Content Formats -linktitle: Supported Content Formats +title: Content Formats +linktitle: Content Formats description: Both HTML and Markdown are supported content formats. date: 2017-01-10 publishdate: 2017-01-10 @@ -13,191 +13,37 @@ menu: weight: 20 weight: 20 #rem draft: false -aliases: [/content/markdown-extras/,/content/supported-formats/,/doc/supported-formats/,/tutorials/mathjax/] +aliases: [/content/markdown-extras/,/content/supported-formats/,/doc/supported-formats/] toc: true --- -**Markdown is the main content format** and comes in two flavours: The excellent [Blackfriday project][blackfriday] (name your files `*.md` or set `markup = "markdown"` in front matter) or its fork [Mmark][mmark] (name your files `*.mmark` or set `markup = "mmark"` in front matter), both very fast markdown engines written in Go. +You can put any file type into your `/content` directories, but Hugo uses the `markup` front matter value if set or the file extension (see `Markup identifiers` in the table below) to determine if the markup needs to be processed, e.g.: -For Emacs users, [go-org](https://github.com/niklasfasching/go-org) provides built-in native support for Org-mode (name your files `*.org` or set `markup = "org"` in front matter) +* Markdown converted to HTML +* [Shortcodes](/content-management/shortcodes/) processed +* Layout applied -But in many situations, plain HTML is what you want. Just name your files with `.html` or `.htm` extension inside your content folder. Note that if you want your HTML files to have a layout, they need front matter. It can be empty, but it has to be there: +## List of content formats -```html ---- -title: "This is a content file in HTML" ---- +The current list of content formats in Hugo: -
false
disables smart fractions.<sup>5</sup>⁄<sub>12</sub>
).fractions = false
, Blackfriday still converts `1/2`, `1/4`, and `3/4` respectively to ½ (½
), ¼ (¼
) and ¾ (¾
), but only these three.
-
-`smartDashes`
-: default: **`true`** `, lang, lang)
- return preRe.ReplaceAllString(code, fmt.Sprintf("$1%s$2
$3", codeTag))
-}
-
-func chromaHighlight(w io.Writer, source, lexer, style string, f chroma.Formatter) error {
- l := lexers.Get(lexer)
- if l == nil {
- l = lexers.Analyse(source)
- }
- if l == nil {
- l = lexers.Fallback
- }
- l = chroma.Coalesce(l)
-
- if f == nil {
- f = formatters.Fallback
- }
-
- s := styles.Get(style)
- if s == nil {
- s = styles.Fallback
- }
-
- it, err := l.Tokenise(nil, source)
- if err != nil {
- return err
- }
-
- return f.Format(w, s, it)
-}
-
-var pygmentsKeywords = make(map[string]bool)
-
-func init() {
- pygmentsKeywords["encoding"] = true
- pygmentsKeywords["outencoding"] = true
- pygmentsKeywords["nowrap"] = true
- pygmentsKeywords["full"] = true
- pygmentsKeywords["title"] = true
- pygmentsKeywords["style"] = true
- pygmentsKeywords["noclasses"] = true
- pygmentsKeywords["classprefix"] = true
- pygmentsKeywords["cssclass"] = true
- pygmentsKeywords["cssstyles"] = true
- pygmentsKeywords["prestyles"] = true
- pygmentsKeywords["linenos"] = true
- pygmentsKeywords["hl_lines"] = true
- pygmentsKeywords["linenostart"] = true
- pygmentsKeywords["linenostep"] = true
- pygmentsKeywords["linenospecial"] = true
- pygmentsKeywords["nobackground"] = true
- pygmentsKeywords["lineseparator"] = true
- pygmentsKeywords["lineanchors"] = true
- pygmentsKeywords["linespans"] = true
- pygmentsKeywords["anchorlinenos"] = true
- pygmentsKeywords["startinline"] = true
-}
-
-func parseOptions(defaults map[string]string, in string) (map[string]string, error) {
- in = strings.Trim(in, " ")
- opts := make(map[string]string)
-
- for k, v := range defaults {
- opts[k] = v
- }
-
- if in == "" {
- return opts, nil
- }
-
- for _, v := range strings.Split(in, ",") {
- keyVal := strings.Split(v, "=")
- key := strings.ToLower(strings.Trim(keyVal[0], " "))
- if len(keyVal) != 2 || !pygmentsKeywords[key] {
- return opts, fmt.Errorf("invalid Pygments option: %s", key)
- }
- opts[key] = keyVal[1]
- }
-
- return opts, nil
-}
-
-func createOptionsString(options map[string]string) string {
- var keys []string
- for k := range options {
- keys = append(keys, k)
- }
- sort.Strings(keys)
-
- var optionsStr string
- for i, k := range keys {
- optionsStr += fmt.Sprintf("%s=%s", k, options[k])
- if i < len(options)-1 {
- optionsStr += ","
- }
- }
-
- return optionsStr
-}
-
-func parseDefaultPygmentsOpts(cfg config.Provider) (map[string]string, error) {
- options, err := parseOptions(nil, cfg.GetString("pygmentsOptions"))
- if err != nil {
- return nil, err
- }
-
- if cfg.IsSet("pygmentsStyle") {
- options["style"] = cfg.GetString("pygmentsStyle")
- }
-
- if cfg.IsSet("pygmentsUseClasses") {
- if cfg.GetBool("pygmentsUseClasses") {
- options["noclasses"] = "false"
- } else {
- options["noclasses"] = "true"
- }
-
- }
-
- if _, ok := options["encoding"]; !ok {
- options["encoding"] = "utf8"
- }
-
- return options, nil
-}
-
-func (cs *ContentSpec) chromaFormatterFromOptions(pygmentsOpts map[string]string) (chroma.Formatter, error) {
- var options = []html.Option{html.TabWidth(4)}
-
- if pygmentsOpts["noclasses"] == "false" {
- options = append(options, html.WithClasses())
- }
-
- lineNumbers := pygmentsOpts["linenos"]
- if lineNumbers != "" {
- options = append(options, html.WithLineNumbers())
- if lineNumbers != "inline" {
- options = append(options, html.LineNumbersInTable())
- }
- }
-
- startLineStr := pygmentsOpts["linenostart"]
- var startLine = 1
- if startLineStr != "" {
-
- line, err := strconv.Atoi(strings.TrimSpace(startLineStr))
- if err == nil {
- startLine = line
- options = append(options, html.BaseLineNumber(startLine))
- }
- }
-
- hlLines := pygmentsOpts["hl_lines"]
-
- if hlLines != "" {
- ranges, err := hlLinesToRanges(startLine, hlLines)
-
- if err == nil {
- options = append(options, html.HighlightLines(ranges))
- }
- }
-
- return html.New(options...), nil
-}
-
-func (cs *ContentSpec) parsePygmentsOpts(in string) (map[string]string, error) {
- opts, err := parseOptions(cs.defatultPygmentsOpts, in)
- if err != nil {
- return nil, err
- }
- return opts, nil
-
-}
-
-func (cs *ContentSpec) createPygmentsOptionsString(in string) (string, error) {
- opts, err := cs.parsePygmentsOpts(in)
- if err != nil {
- return "", err
- }
- return createOptionsString(opts), nil
-}
-
-// startLine compansates for https://github.com/alecthomas/chroma/issues/30
-func hlLinesToRanges(startLine int, s string) ([][2]int, error) {
- var ranges [][2]int
- s = strings.TrimSpace(s)
-
- if s == "" {
- return ranges, nil
- }
-
- // Variants:
- // 1 2 3 4
- // 1-2 3-4
- // 1-2 3
- // 1 3-4
- // 1 3-4
- fields := strings.Split(s, " ")
- for _, field := range fields {
- field = strings.TrimSpace(field)
- if field == "" {
- continue
- }
- numbers := strings.Split(field, "-")
- var r [2]int
- first, err := strconv.Atoi(numbers[0])
- if err != nil {
- return ranges, err
- }
- first = first + startLine - 1
- r[0] = first
- if len(numbers) > 1 {
- second, err := strconv.Atoi(numbers[1])
- if err != nil {
- return ranges, err
- }
- second = second + startLine - 1
- r[1] = second
- } else {
- r[1] = first
- }
-
- ranges = append(ranges, r)
- }
- return ranges, nil
-
-}
diff --git a/helpers/pygments_test.go b/helpers/pygments_test.go
deleted file mode 100644
index 05d86e104..000000000
--- a/helpers/pygments_test.go
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2015 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 helpers
-
-import (
- "fmt"
- "reflect"
- "testing"
-
- "github.com/alecthomas/chroma/formatters/html"
-
- qt "github.com/frankban/quicktest"
- "github.com/spf13/viper"
-)
-
-func TestParsePygmentsArgs(t *testing.T) {
- c := qt.New(t)
-
- for i, this := range []struct {
- in string
- pygmentsStyle string
- pygmentsUseClasses bool
- expect1 interface{}
- }{
- {"", "foo", true, "encoding=utf8,noclasses=false,style=foo"},
- {"style=boo,noclasses=true", "foo", true, "encoding=utf8,noclasses=true,style=boo"},
- {"Style=boo, noClasses=true", "foo", true, "encoding=utf8,noclasses=true,style=boo"},
- {"noclasses=true", "foo", true, "encoding=utf8,noclasses=true,style=foo"},
- {"style=boo", "foo", true, "encoding=utf8,noclasses=false,style=boo"},
- {"boo=invalid", "foo", false, false},
- {"style", "foo", false, false},
- } {
-
- v := viper.New()
- v.Set("pygmentsStyle", this.pygmentsStyle)
- v.Set("pygmentsUseClasses", this.pygmentsUseClasses)
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- result1, err := spec.createPygmentsOptionsString(this.in)
- if b, ok := this.expect1.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] parsePygmentArgs didn't return an expected error", i)
- }
- } else {
- if err != nil {
- t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
- continue
- }
- if result1 != this.expect1 {
- t.Errorf("[%d] parsePygmentArgs got %v but expected %v", i, result1, this.expect1)
- }
-
- }
- }
-}
-
-func TestParseDefaultPygmentsArgs(t *testing.T) {
- c := qt.New(t)
-
- expect := "encoding=utf8,noclasses=false,style=foo"
-
- for i, this := range []struct {
- in string
- pygmentsStyle interface{}
- pygmentsUseClasses interface{}
- pygmentsOptions string
- }{
- {"", "foo", true, "style=override,noclasses=override"},
- {"", nil, nil, "style=foo,noclasses=false"},
- {"style=foo,noclasses=false", nil, nil, "style=override,noclasses=override"},
- {"style=foo,noclasses=false", "override", false, "style=override,noclasses=override"},
- } {
- v := viper.New()
-
- v.Set("pygmentsOptions", this.pygmentsOptions)
-
- if s, ok := this.pygmentsStyle.(string); ok {
- v.Set("pygmentsStyle", s)
- }
-
- if b, ok := this.pygmentsUseClasses.(bool); ok {
- v.Set("pygmentsUseClasses", b)
- }
-
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- result, err := spec.createPygmentsOptionsString(this.in)
- if err != nil {
- t.Errorf("[%d] parsePygmentArgs failed: %s", i, err)
- continue
- }
- if result != expect {
- t.Errorf("[%d] parsePygmentArgs got %v but expected %v", i, result, expect)
- }
- }
-}
-
-type chromaInfo struct {
- classes bool
- lineNumbers bool
- lineNumbersInTable bool
- highlightRangesLen int
- highlightRangesStr string
- baseLineNumber int
-}
-
-func formatterChromaInfo(f *html.Formatter) chromaInfo {
- v := reflect.ValueOf(f).Elem()
- c := chromaInfo{}
- // Hack:
-
- c.classes = f.Classes
- c.lineNumbers = v.FieldByName("lineNumbers").Bool()
- c.lineNumbersInTable = v.FieldByName("lineNumbersInTable").Bool()
- c.baseLineNumber = int(v.FieldByName("baseLineNumber").Int())
- vv := v.FieldByName("highlightRanges")
- c.highlightRangesLen = vv.Len()
- c.highlightRangesStr = fmt.Sprint(vv)
-
- return c
-}
-
-func TestChromaHTMLHighlight(t *testing.T) {
- c := qt.New(t)
-
- v := viper.New()
- v.Set("pygmentsUseClasses", true)
- spec, err := NewContentSpec(v, nil, nil)
- c.Assert(err, qt.IsNil)
-
- result, err := spec.Highlight(`echo "Hello"`, "bash", "")
- c.Assert(err, qt.IsNil)
-
- c.Assert(result, qt.Contains, `echo "Hello"
The End.
"} + contentMatchers := []string{"The End.
"} for i := 1; i <= numPages; i++ { if i%3 != 0 { @@ -691,13 +691,13 @@ END checkContent(b, fmt.Sprintf("public/%s/page%d/index.json", section, i), contentMatchers...) } - checkContent(b, "public/s1/index.html", "P: s1/_index.md\nList: 10|List Content: 8335\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8335\n\nRender 1: View: 8335\n\nRender 2: View: 8335\n\nRender 3: View: 8335\n\nRender 4: View: 8335\n\nEND\n") - checkContent(b, "public/s2/index.html", "P: s2/_index.md\nList: 10|List Content: 8335", "Render 4: View: 8335\n\nEND") - checkContent(b, "public/index.html", "P: _index.md\nList: 10|List Content: 8335", "4: View: 8335\n\nEND") + checkContent(b, "public/s1/index.html", "P: s1/_index.md\nList: 10|List Content: 8033\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8033\n\nRender 1: View: 8033\n\nRender 2: View: 8033\n\nRender 3: View: 8033\n\nRender 4: View: 8033\n\nEND\n") + checkContent(b, "public/s2/index.html", "P: s2/_index.md\nList: 10|List Content: 8033", "Render 4: View: 8033\n\nEND") + checkContent(b, "public/index.html", "P: _index.md\nList: 10|List Content: 8033", "4: View: 8033\n\nEND") // Check paginated pages for i := 2; i <= 9; i++ { - checkContent(b, fmt.Sprintf("public/page/%d/index.html", i), fmt.Sprintf("Page: %d", i), "Content: 8335\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8335", "Render 4: View: 8335\n\nEND") + checkContent(b, fmt.Sprintf("public/page/%d/index.html", i), fmt.Sprintf("Page: %d", i), "Content: 8033\n\n\nL1: 500 L2: 5\n\nRender 0: View: 8033", "Render 4: View: 8033\n\nEND") } } @@ -977,7 +977,10 @@ enableRobotsTXT = true [permalinks] other = "/somewhere/else/:filename" -[blackfriday] +# TODO(bep) +[markup] + defaultMarkdownHandler = "blackfriday" +[markup.blackfriday] angledQuotes = true [Taxonomies] @@ -1035,7 +1038,10 @@ enableRobotsTXT: true permalinks: other: "/somewhere/else/:filename" -blackfriday: +# TODO(bep) +markup: + defaultMarkdownHandler: blackfriday + blackFriday: angledQuotes: true Taxonomies: @@ -1093,9 +1099,12 @@ var multiSiteJSONConfigTemplate = ` "permalinks": { "other": "/somewhere/else/:filename" }, - "blackfriday": { - "angledQuotes": true - }, + "markup": { + "defaultMarkdownHandler": "blackfriday", + "blackfriday": { + "angledQuotes": true + } + }, "Taxonomies": { "tag": "tags" }, diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index ca5c7007e..1fc69c218 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -565,7 +565,7 @@ func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatte pm.sitemap = p.s.siteCfg.sitemap } - pm.markup = helpers.GuessType(pm.markup) + pm.markup = p.s.ContentSpec.ResolveMarkup(pm.markup) if draft != nil && published != nil { pm.draft = *draft @@ -596,7 +596,7 @@ func (p *pageMeta) applyDefaultValues() error { if p.markup == "" { if !p.File().IsZero() { // Fall back to file extension - p.markup = helpers.GuessType(p.File().Ext()) + p.markup = p.s.ContentSpec.ResolveMarkup(p.File().Ext()) } if p.markup == "" { p.markup = "markdown" @@ -638,14 +638,20 @@ func (p *pageMeta) applyDefaultValues() error { } } - if !p.f.IsZero() && p.markup != "html" { + if !p.f.IsZero() { var renderingConfigOverrides map[string]interface{} bfParam := getParamToLower(p, "blackfriday") if bfParam != nil { renderingConfigOverrides = maps.ToStringMap(bfParam) } - cp := p.s.ContentSpec.Converters.Get(p.markup) + markup := p.markup + if markup == "html" { + // Only used for shortcode inner content. + markup = "markdown" + } + + cp := p.s.ContentSpec.Converters.Get(markup) if cp == nil { return errors.Errorf("no content renderer found for markup %q", p.markup) } diff --git a/hugolib/page__per_output.go b/hugolib/page__per_output.go index ef2419eca..59de10be3 100644 --- a/hugolib/page__per_output.go +++ b/hugolib/page__per_output.go @@ -77,7 +77,7 @@ func newPageContentOutput(p *pageState) func(f output.Format) (*pageContentOutpu // See https://github.com/gohugoio/hugo/issues/6210 if r := recover(); r != nil { err = fmt.Errorf("%s", r) - p.s.Log.ERROR.Println("[BUG] Got panic:\n", string(debug.Stack())) + p.s.Log.ERROR.Printf("[BUG] Got panic:\n%s\n%s", r, string(debug.Stack())) } }() @@ -103,11 +103,14 @@ func newPageContentOutput(p *pageState) func(f output.Format) (*pageContentOutpu if err != nil { return err } + cp.convertedResult = r cp.workContent = r.Bytes() - tmpContent, tmpTableOfContents := helpers.ExtractTOC(cp.workContent) - cp.tableOfContents = helpers.BytesToHTML(tmpTableOfContents) - cp.workContent = tmpContent + if _, ok := r.(converter.TableOfContentsProvider); !ok { + tmpContent, tmpTableOfContents := helpers.ExtractTOC(cp.workContent) + cp.tableOfContents = helpers.BytesToHTML(tmpTableOfContents) + cp.workContent = tmpContent + } } if cp.placeholdersEnabled { @@ -223,7 +226,8 @@ type pageContentOutput struct { // Content state - workContent []byte + workContent []byte + convertedResult converter.Result // Temporary storage of placeholders mapped to their content. // These are shortcodes etc. Some of these will need to be replaced @@ -284,6 +288,10 @@ func (p *pageContentOutput) Summary() template.HTML { func (p *pageContentOutput) TableOfContents() template.HTML { p.p.s.initInit(p.initMain, p.p) + if tocProvider, ok := p.convertedResult.(converter.TableOfContentsProvider); ok { + cfg := p.p.s.ContentSpec.Converters.GetMarkupConfig() + return template.HTML(tocProvider.TableOfContents().ToHTML(cfg.TableOfContents.StartLevel, cfg.TableOfContents.EndLevel)) + } return p.tableOfContents } diff --git a/hugolib/page_test.go b/hugolib/page_test.go index abceec9c6..665827502 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -326,7 +326,7 @@ func normalizeContent(c string) string { func checkPageTOC(t *testing.T, page page.Page, toc string) { if page.TableOfContents() != template.HTML(toc) { - t.Fatalf("Page TableOfContents is: %q.\nExpected %q", page.TableOfContents(), toc) + t.Fatalf("Page TableOfContents is:\n%q.\nExpected %q", page.TableOfContents(), toc) } } @@ -442,6 +442,7 @@ func testAllMarkdownEnginesForPages(t *testing.T, func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) { t.Parallel() cfg, fs := newTestCfg() + c := qt.New(t) writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder) @@ -453,12 +454,12 @@ func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) { p := s.RegularPages()[0] if p.Summary() != template.HTML( - "The best static site generator.1
") { + "The best static site generator.1
") { t.Fatalf("Got summary:\n%q", p.Summary()) } cnt := content(p) - if cnt != "The best static site generator.1
\n\nThe best static site generator.1
\nMany people say so.
\nFor some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.
\n\nI have no idea, of course, how long it took me to reach the limit of the plain,\nbut at last I entered the foothills, following a pretty little canyon upward\ntoward the mountains. Beside me frolicked a laughing brooklet, hurrying upon\nits noisy way down to the silent sea. In its quieter pools I discovered many\nsmall fish, of four-or five-pound weight I should imagine. In appearance,\nexcept as to size and color, they were not unlike the whale of our own seas. As\nI watched them playing about I discovered, not only that they suckled their\nyoung, but that at intervals they rose to the surface to breathe as well as to\nfeed upon certain grasses and a strange, scarlet lichen which grew upon the\nrocks just above the water line.
\n\nI remember I felt an extraordinary persuasion that I was being played with,\nthat presently, when I was upon the very verge of safety, this mysterious\ndeath–as swift as the passage of light–would leap after me from the pit about\nthe cylinder and strike me down. ## BB
\n\n“You’re a great Granser,” he cried delightedly, “always making believe them little marks mean something.”
\n") - checkPageTOC(t, p, "") + checkPageContent(t, p, "For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.
I have no idea, of course, how long it took me to reach the limit of the plain, but at last I entered the foothills, following a pretty little canyon upward toward the mountains. Beside me frolicked a laughing brooklet, hurrying upon its noisy way down to the silent sea. In its quieter pools I discovered many small fish, of four-or five-pound weight I should imagine. In appearance, except as to size and color, they were not unlike the whale of our own seas. As I watched them playing about I discovered, not only that they suckled their young, but that at intervals they rose to the surface to breathe as well as to feed upon certain grasses and a strange, scarlet lichen which grew upon the rocks just above the water line.
I remember I felt an extraordinary persuasion that I was being played with, that presently, when I was upon the very verge of safety, this mysterious death–as swift as the passage of light–would leap after me from the pit about the cylinder and strike me down. ## BB
“You're a great Granser,” he cried delightedly, “always making believe them little marks mean something.”
") + checkPageTOC(t, p, "") } func TestPageWithMoreTag(t *testing.T) { @@ -1518,12 +1509,12 @@ Summary: In Chinese, 好 means good. b.AssertFileContent("public/p1/index.html", "WordCount: 510\nFuzzyWordCount: 600\nReadingTime: 3\nLen Plain: 2550\nLen PlainWords: 510\nTruncated: false\nLen Summary: 2549\nLen Content: 2557") - b.AssertFileContent("public/p2/index.html", "WordCount: 314\nFuzzyWordCount: 400\nReadingTime: 2\nLen Plain: 1569\nLen PlainWords: 314\nTruncated: true\nLen Summary: 25\nLen Content: 1583") + b.AssertFileContent("public/p2/index.html", "WordCount: 314\nFuzzyWordCount: 400\nReadingTime: 2\nLen Plain: 1569\nLen PlainWords: 314\nTruncated: true\nLen Summary: 25\nLen Content: 1582") - b.AssertFileContent("public/p3/index.html", "WordCount: 206\nFuzzyWordCount: 300\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 43\nLen Content: 652") - b.AssertFileContent("public/p4/index.html", "WordCount: 7\nFuzzyWordCount: 100\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 43\nLen Content: 652") - b.AssertFileContent("public/p5/index.html", "WordCount: 206\nFuzzyWordCount: 300\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 229\nLen Content: 653") - b.AssertFileContent("public/p6/index.html", "WordCount: 7\nFuzzyWordCount: 100\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: false\nLen Summary: 637\nLen Content: 653") + b.AssertFileContent("public/p3/index.html", "WordCount: 206\nFuzzyWordCount: 300\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 43\nLen Content: 651") + b.AssertFileContent("public/p4/index.html", "WordCount: 7\nFuzzyWordCount: 100\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 43\nLen Content: 651") + b.AssertFileContent("public/p5/index.html", "WordCount: 206\nFuzzyWordCount: 300\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: true\nLen Summary: 229\nLen Content: 652") + b.AssertFileContent("public/p6/index.html", "WordCount: 7\nFuzzyWordCount: 100\nReadingTime: 1\nLen Plain: 638\nLen PlainWords: 7\nTruncated: false\nLen Summary: 637\nLen Content: 652") } @@ -1611,3 +1602,132 @@ author = "Jo Nesbø" "Author site config: Kurt Vonnegut") } + +func TestGoldmark(t *testing.T) { + t.Parallel() + + b := newTestSitesBuilder(t).WithConfigFile("toml", ` +baseURL = "https://example.org" + +[markup] +defaultMarkdownHandler="goldmark" +[markup.goldmark] +[markup.goldmark.renderer] +unsafe = false +[markup.highlight] +noClasses=false + + +`) + b.WithTemplatesAdded("_default/single.html", ` +Title: {{ .Title }} +ToC: {{ .TableOfContents }} +Content: {{ .Content }} + +`, "shortcodes/t.html", `T-SHORT`, "shortcodes/s.html", `## Code +{{ .Inner }} +`) + + content := ` ++++ +title = "A Page!" ++++ + +## Shortcode {{% t %}} in header + +## Code Fense in Shortcode + +{{% s %}} +$$$bash {hl_lines=[1]} +SHORT +$$$ +{{% /s %}} + +## Code Fence + +$$$bash {hl_lines=[1]} +MARKDOWN +$$$ + +` + content = strings.ReplaceAll(content, "$$$", "```") + + b.WithContent("page.md", content) + + b.Build(BuildCfg{}) + + b.AssertFileContent("public/page/index.html", + `