Add Goldmark as the new default markdown handler

This commit adds the fast and CommonMark compliant Goldmark as the new default markdown handler in Hugo.

If you want to continue using BlackFriday as the default for md/markdown extensions, you can use this configuration:

```toml
[markup]
defaultMarkdownHandler="blackfriday"
```

Fixes #5963
Fixes #1778
Fixes #6355
This commit is contained in:
Bjørn Erik Pedersen
2019-11-06 20:10:47 +01:00
parent a3fe5e5e35
commit bfb9613a14
69 changed files with 3424 additions and 1668 deletions

View File

@@ -0,0 +1,70 @@
// 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 helpers implements general utility functions that work with
// and on content. The helper functions defined here lay down the
// foundation of how Hugo works with files and filepaths, and perform
// string operations on content.
package blackfriday_config
import (
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)
// Default holds the default BlackFriday config.
// Do not change!
var Default = Config{
Smartypants: true,
AngledQuotes: false,
SmartypantsQuotesNBSP: false,
Fractions: true,
HrefTargetBlank: false,
NofollowLinks: false,
NoreferrerLinks: false,
SmartDashes: true,
LatexDashes: true,
PlainIDAnchors: true,
TaskLists: true,
SkipHTML: false,
}
// Config holds configuration values for BlackFriday rendering.
// It is kept here because it's used in several packages.
type Config struct {
Smartypants bool
SmartypantsQuotesNBSP bool
AngledQuotes bool
Fractions bool
HrefTargetBlank bool
NofollowLinks bool
NoreferrerLinks bool
SmartDashes bool
LatexDashes bool
TaskLists bool
PlainIDAnchors bool
Extensions []string
ExtensionsMask []string
SkipHTML bool
FootnoteAnchorPrefix string
FootnoteReturnLinkContents string
}
func UpdateConfig(b Config, m map[string]interface{}) (Config, error) {
if err := mapstructure.Decode(m, &b); err != nil {
return b, errors.WithMessage(err, "failed to decode rendering config")
}
return b, nil
}

View File

@@ -15,36 +15,27 @@
package blackfriday
import (
"github.com/gohugoio/hugo/markup/blackfriday/blackfriday_config"
"github.com/gohugoio/hugo/markup/converter"
"github.com/gohugoio/hugo/markup/internal"
"github.com/russross/blackfriday"
)
// Provider is the package entry point.
var Provider converter.NewProvider = provider{}
var Provider converter.ProviderProvider = provider{}
type provider struct {
}
func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error) {
defaultBlackFriday, err := internal.NewBlackfriday(cfg)
if err != nil {
return nil, err
}
defaultExtensions := getMarkdownExtensions(cfg.MarkupConfig.BlackFriday)
defaultExtensions := getMarkdownExtensions(defaultBlackFriday)
pygmentsCodeFences := cfg.Cfg.GetBool("pygmentsCodeFences")
pygmentsCodeFencesGuessSyntax := cfg.Cfg.GetBool("pygmentsCodeFencesGuessSyntax")
pygmentsOptions := cfg.Cfg.GetString("pygmentsOptions")
var n converter.NewConverter = func(ctx converter.DocumentContext) (converter.Converter, error) {
b := defaultBlackFriday
return converter.NewProvider("blackfriday", func(ctx converter.DocumentContext) (converter.Converter, error) {
b := cfg.MarkupConfig.BlackFriday
extensions := defaultExtensions
if ctx.ConfigOverrides != nil {
var err error
b, err = internal.UpdateBlackFriday(b, ctx.ConfigOverrides)
b, err = blackfriday_config.UpdateConfig(b, ctx.ConfigOverrides)
if err != nil {
return nil, err
}
@@ -56,27 +47,16 @@ func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error)
bf: b,
extensions: extensions,
cfg: cfg,
pygmentsCodeFences: pygmentsCodeFences,
pygmentsCodeFencesGuessSyntax: pygmentsCodeFencesGuessSyntax,
pygmentsOptions: pygmentsOptions,
}, nil
}
return n, nil
}), nil
}
type blackfridayConverter struct {
ctx converter.DocumentContext
bf *internal.BlackFriday
bf blackfriday_config.Config
extensions int
pygmentsCodeFences bool
pygmentsCodeFencesGuessSyntax bool
pygmentsOptions string
cfg converter.ProviderConfig
cfg converter.ProviderConfig
}
func (c *blackfridayConverter) AnchorSuffix() string {
@@ -90,7 +70,6 @@ func (c *blackfridayConverter) Convert(ctx converter.RenderContext) (converter.R
r := c.getHTMLRenderer(ctx.RenderTOC)
return converter.Bytes(blackfriday.Markdown(ctx.Src, r, c.extensions)), nil
}
func (c *blackfridayConverter) getHTMLRenderer(renderTOC bool) blackfriday.Renderer {
@@ -114,7 +93,7 @@ func (c *blackfridayConverter) getHTMLRenderer(renderTOC bool) blackfriday.Rende
}
}
func getFlags(renderTOC bool, cfg *internal.BlackFriday) int {
func getFlags(renderTOC bool, cfg blackfriday_config.Config) int {
var flags int
@@ -168,7 +147,7 @@ func getFlags(renderTOC bool, cfg *internal.BlackFriday) int {
return flags
}
func getMarkdownExtensions(cfg *internal.BlackFriday) int {
func getMarkdownExtensions(cfg blackfriday_config.Config) int {
// Default Blackfriday common extensions
commonExtensions := 0 |
blackfriday.EXTENSION_NO_INTRA_EMPHASIS |

View File

@@ -18,19 +18,15 @@ import (
"github.com/spf13/viper"
"github.com/gohugoio/hugo/markup/internal"
"github.com/gohugoio/hugo/markup/converter"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/markup/blackfriday/blackfriday_config"
"github.com/russross/blackfriday"
)
func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) {
c := qt.New(t)
b, err := internal.NewBlackfriday(converter.ProviderConfig{Cfg: viper.New()})
c.Assert(err, qt.IsNil)
b := blackfriday_config.Default
b.Extensions = []string{"headerId"}
b.ExtensionsMask = []string{"noIntraEmphasis"}
@@ -45,9 +41,7 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
testFlag int
}
c := qt.New(t)
b, err := internal.NewBlackfriday(converter.ProviderConfig{Cfg: viper.New()})
c.Assert(err, qt.IsNil)
b := blackfriday_config.Default
b.Extensions = []string{""}
b.ExtensionsMask = []string{""}
@@ -79,9 +73,7 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
}
func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
c := qt.New(t)
b, err := internal.NewBlackfriday(converter.ProviderConfig{Cfg: viper.New()})
c.Assert(err, qt.IsNil)
b := blackfriday_config.Default
b.Extensions = []string{"definitionLists"}
b.ExtensionsMask = []string{""}
@@ -93,10 +85,7 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
}
func TestGetFlags(t *testing.T) {
c := qt.New(t)
cfg := converter.ProviderConfig{Cfg: viper.New()}
b, err := internal.NewBlackfriday(cfg)
c.Assert(err, qt.IsNil)
b := blackfriday_config.Default
flags := getFlags(false, b)
if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML {
t.Errorf("Test flag: %d was not found amongs set flags:%d; Result: %d", blackfriday.HTML_USE_XHTML, flags, flags&blackfriday.HTML_USE_XHTML)
@@ -105,9 +94,8 @@ func TestGetFlags(t *testing.T) {
func TestGetAllFlags(t *testing.T) {
c := qt.New(t)
cfg := converter.ProviderConfig{Cfg: viper.New()}
b, err := internal.NewBlackfriday(cfg)
c.Assert(err, qt.IsNil)
b := blackfriday_config.Default
type data struct {
testFlag int
@@ -145,9 +133,8 @@ func TestGetAllFlags(t *testing.T) {
for _, d := range allFlags {
expectedFlags |= d.testFlag
}
if expectedFlags != actualFlags {
t.Errorf("Expected flags (%d) did not equal actual (%d) flags.", expectedFlags, actualFlags)
}
c.Assert(actualFlags, qt.Equals, expectedFlags)
}
func TestConvert(t *testing.T) {

View File

@@ -30,10 +30,9 @@ type hugoHTMLRenderer struct {
// BlockCode renders a given text as a block of code.
// Pygments is used if it is setup to handle code fences.
func (r *hugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
if r.c.pygmentsCodeFences && (lang != "" || r.c.pygmentsCodeFencesGuessSyntax) {
opts := r.c.pygmentsOptions
if r.c.cfg.MarkupConfig.Highlight.CodeFences {
str := strings.Trim(string(text), "\n\r")
highlighted, _ := r.c.cfg.Highlight(str, lang, opts)
highlighted, _ := r.c.cfg.Highlight(str, lang, "")
out.WriteString(highlighted)
} else {
r.Renderer.BlockCode(out, text, lang)