mirror of https://github.com/gohugoio/hugo.git
Browse Source
Remove Blackfriday markdown engine
Remove Blackfriday markdown engine
It has been deprecated for a long time, its v1 version is not maintained anymore, and there are many known issues. Goldmark should be a mature replacement by now. Closes #9934pull/9950/head

22 changed files with 71 additions and 1675 deletions
-
231hugolib/case_insensitive_test.go
-
5hugolib/config.go
-
63hugolib/hugo_sites_build_test.go
-
4hugolib/page.go
-
2hugolib/page__content.go
-
27hugolib/page__meta.go
-
2hugolib/page__per_output.go
-
104hugolib/page_test.go
-
4hugolib/shortcode.go
-
563hugolib/shortcode_test.go
-
7hugolib/site_test.go
-
5hugolib/testhelpers_test.go
-
39markup/blackfriday/anchors.go
-
71markup/blackfriday/blackfriday_config/config.go
-
233markup/blackfriday/convert.go
-
223markup/blackfriday/convert_test.go
-
84markup/blackfriday/renderer.go
-
9markup/converter/converter.go
-
6markup/markup.go
-
29markup/markup_config/config.go
-
34markup/markup_config/config_test.go
-
1markup/markup_test.go
@ -1,231 +0,0 @@ |
|||
// Copyright 2016 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 hugolib |
|||
|
|||
import ( |
|||
"path/filepath" |
|||
"testing" |
|||
|
|||
"github.com/gohugoio/hugo/hugofs" |
|||
|
|||
qt "github.com/frankban/quicktest" |
|||
"github.com/gohugoio/hugo/deps" |
|||
"github.com/spf13/afero" |
|||
) |
|||
|
|||
var ( |
|||
caseMixingSiteConfigTOML = ` |
|||
Title = "In an Insensitive Mood" |
|||
DefaultContentLanguage = "nn" |
|||
defaultContentLanguageInSubdir = true |
|||
|
|||
[Blackfriday] |
|||
AngledQuotes = true |
|||
HrefTargetBlank = true |
|||
|
|||
[Params] |
|||
Search = true |
|||
Color = "green" |
|||
mood = "Happy" |
|||
[Params.Colors] |
|||
Blue = "blue" |
|||
Yellow = "yellow" |
|||
|
|||
[Languages] |
|||
[Languages.nn] |
|||
title = "Nynorsk title" |
|||
languageName = "Nynorsk" |
|||
weight = 1 |
|||
|
|||
[Languages.en] |
|||
TITLE = "English title" |
|||
LanguageName = "English" |
|||
Mood = "Thoughtful" |
|||
Weight = 2 |
|||
COLOR = "Pink" |
|||
[Languages.en.blackfriday] |
|||
angledQuotes = false |
|||
hrefTargetBlank = false |
|||
[Languages.en.Colors] |
|||
BLUE = "blues" |
|||
Yellow = "golden" |
|||
` |
|||
caseMixingPage1En = ` |
|||
--- |
|||
TITLE: Page1 En Translation |
|||
BlackFriday: |
|||
AngledQuotes: false |
|||
Color: "black" |
|||
Search: true |
|||
mooD: "sad and lonely" |
|||
ColorS: |
|||
Blue: "bluesy" |
|||
Yellow: "sunny" |
|||
--- |
|||
# "Hi" |
|||
{{< shortcode >}} |
|||
` |
|||
|
|||
caseMixingPage1 = ` |
|||
--- |
|||
titLe: Side 1 |
|||
blackFriday: |
|||
angledQuotes: true |
|||
color: "red" |
|||
search: false |
|||
MooD: "sad" |
|||
COLORS: |
|||
blue: "heavenly" |
|||
yelloW: "Sunny" |
|||
--- |
|||
# "Hi" |
|||
{{< shortcode >}} |
|||
` |
|||
|
|||
caseMixingPage2 = ` |
|||
--- |
|||
TITLE: Page2 Title |
|||
BlackFriday: |
|||
AngledQuotes: false |
|||
Color: "black" |
|||
search: true |
|||
MooD: "moody" |
|||
ColorS: |
|||
Blue: "sky" |
|||
YELLOW: "flower" |
|||
--- |
|||
# Hi |
|||
{{< shortcode >}} |
|||
` |
|||
) |
|||
|
|||
func caseMixingTestsWriteCommonSources(t *testing.T, fs afero.Fs) { |
|||
writeToFs(t, fs, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1) |
|||
writeToFs(t, fs, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2) |
|||
writeToFs(t, fs, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En) |
|||
|
|||
writeToFs(t, fs, "layouts/shortcodes/shortcode.html", ` |
|||
Shortcode Page: {{ .Page.Params.COLOR }}|{{ .Page.Params.Colors.Blue }} |
|||
Shortcode Site: {{ .Page.Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} |
|||
`) |
|||
|
|||
writeToFs(t, fs, "layouts/partials/partial.html", ` |
|||
Partial Page: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }} |
|||
Partial Site: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }} |
|||
Partial Site Global: {{ site.Params.COLOR }}|{{ site.Params.COLORS.YELLOW }} |
|||
`) |
|||
|
|||
writeToFs(t, fs, "config.toml", caseMixingSiteConfigTOML) |
|||
} |
|||
|
|||
func TestCaseInsensitiveConfigurationVariations(t *testing.T) { |
|||
t.Parallel() |
|||
|
|||
c := qt.New(t) |
|||
|
|||
mm := afero.NewMemMapFs() |
|||
|
|||
caseMixingTestsWriteCommonSources(t, mm) |
|||
|
|||
cfg, _, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Filename: "config.toml"}) |
|||
c.Assert(err, qt.IsNil) |
|||
|
|||
fs := hugofs.NewFrom(mm, cfg) |
|||
|
|||
th := newTestHelper(cfg, fs, t) |
|||
|
|||
writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), ` |
|||
Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }} |
|||
{{ block "main" . }}default{{end}}`) |
|||
|
|||
writeSource(t, fs, filepath.Join("layouts", "sect2", "single.html"), ` |
|||
{{ define "main"}} |
|||
Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }} |
|||
Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }} |
|||
{{ template "index-color" (dict "name" "Page" "params" .Params) }} |
|||
{{ template "index-color" (dict "name" "Site" "params" .Site.Params) }} |
|||
|
|||
{{ .Content }} |
|||
{{ partial "partial.html" . }} |
|||
{{ end }} |
|||
{{ define "index-color" }} |
|||
{{ $yellow := index .params "COLoRS" "yELLOW" }} |
|||
{{ $colors := index .params "COLoRS" }} |
|||
{{ $yellow2 := index $colors "yEllow" }} |
|||
index1|{{ .name }}: {{ $yellow }}| |
|||
index2|{{ .name }}: {{ $yellow2 }}| |
|||
{{ end }} |
|||
`) |
|||
|
|||
writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), ` |
|||
Page Title: {{ .Title }} |
|||
Site Title: {{ .Site.Title }} |
|||
Site Lang Mood: {{ .Site.Language.Params.MOoD }} |
|||
Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}|{{ index .Params "ColOR" }} |
|||
Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}|{{ index .Site.Params "ColOR" }} |
|||
{{ $page2 := .Site.GetPage "/sect2/page2" }} |
|||
{{ if $page2 }} |
|||
Page2: {{ $page2.Params.ColoR }} |
|||
{{ end }} |
|||
{{ .Content }} |
|||
{{ partial "partial.html" . }} |
|||
`) |
|||
|
|||
sites, err := NewHugoSites(deps.DepsCfg{Fs: fs, Cfg: cfg}) |
|||
if err != nil { |
|||
t.Fatalf("Failed to create sites: %s", err) |
|||
} |
|||
|
|||
err = sites.Build(BuildCfg{}) |
|||
|
|||
if err != nil { |
|||
t.Fatalf("Failed to build sites: %s", err) |
|||
} |
|||
|
|||
th.assertFileContent(filepath.Join("public", "nn", "sect1", "page1", "index.html"), |
|||
"Page Colors: red|heavenly|red", |
|||
"Site Colors: green|yellow|green", |
|||
"Site Lang Mood: Happy", |
|||
"Shortcode Page: red|heavenly", |
|||
"Shortcode Site: green|yellow", |
|||
"Partial Page: red|heavenly", |
|||
"Partial Site: green|yellow", |
|||
"Partial Site Global: green|yellow", |
|||
"Page Title: Side 1", |
|||
"Site Title: Nynorsk title", |
|||
"Page2: black ", |
|||
) |
|||
|
|||
th.assertFileContent(filepath.Join("public", "en", "sect1", "page1", "index.html"), |
|||
"Site Colors: Pink|golden", |
|||
"Page Colors: black|bluesy", |
|||
"Site Lang Mood: Thoughtful", |
|||
"Page Title: Page1 En Translation", |
|||
"Site Title: English title", |
|||
"“Hi”", |
|||
) |
|||
|
|||
th.assertFileContent(filepath.Join("public", "nn", "sect2", "page2", "index.html"), |
|||
"Page Colors: black|sky", |
|||
"Site Colors: green|yellow", |
|||
"Shortcode Page: black|sky", |
|||
"Block Page Colors: black|sky", |
|||
"Partial Page: black|sky", |
|||
"Partial Site: green|yellow", |
|||
"index1|Page: flower|", |
|||
"index1|Site: yellow|", |
|||
"index2|Page: flower|", |
|||
"index2|Site: yellow|", |
|||
) |
|||
} |
@ -0,0 +1,39 @@ |
|||
// 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 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 blackfriday holds some copmpability functions for the old Blackfriday v1 Markdown engine.
|
|||
package blackfriday |
|||
|
|||
import "unicode" |
|||
|
|||
// SanitizedAnchorName is how Blackfriday sanitizes anchor names.
|
|||
// Implementation borrowed from https://github.com/russross/blackfriday/blob/a477dd1646916742841ed20379f941cfa6c5bb6f/block.go#L1464
|
|||
// Note that Hugo removed its Blackfriday support in v0.100.0, but you can still use this strategy for
|
|||
// auto ID generation.
|
|||
func SanitizedAnchorName(text string) string { |
|||
var anchorName []rune |
|||
futureDash := false |
|||
for _, r := range text { |
|||
switch { |
|||
case unicode.IsLetter(r) || unicode.IsNumber(r): |
|||
if futureDash && len(anchorName) > 0 { |
|||
anchorName = append(anchorName, '-') |
|||
} |
|||
futureDash = false |
|||
anchorName = append(anchorName, unicode.ToLower(r)) |
|||
default: |
|||
futureDash = true |
|||
} |
|||
} |
|||
return string(anchorName) |
|||
} |
@ -1,71 +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 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 ( |
|||
"fmt" |
|||
|
|||
"github.com/mitchellh/mapstructure" |
|||
) |
|||
|
|||
// 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]any) (Config, error) { |
|||
if err := mapstructure.Decode(m, &b); err != nil { |
|||
return b, fmt.Errorf("failed to decode rendering config: %w", err) |
|||
} |
|||
return b, nil |
|||
} |
@ -1,233 +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 blackfriday converts Markdown to HTML using Blackfriday v1.
|
|||
package blackfriday |
|||
|
|||
import ( |
|||
"unicode" |
|||
|
|||
"github.com/gohugoio/hugo/identity" |
|||
"github.com/gohugoio/hugo/markup/blackfriday/blackfriday_config" |
|||
"github.com/gohugoio/hugo/markup/converter" |
|||
"github.com/russross/blackfriday" |
|||
) |
|||
|
|||
// Provider is the package entry point.
|
|||
var Provider converter.ProviderProvider = provider{} |
|||
|
|||
type provider struct { |
|||
} |
|||
|
|||
func (p provider) New(cfg converter.ProviderConfig) (converter.Provider, error) { |
|||
defaultExtensions := getMarkdownExtensions(cfg.MarkupConfig.BlackFriday) |
|||
|
|||
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 = blackfriday_config.UpdateConfig(b, ctx.ConfigOverrides) |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
extensions = getMarkdownExtensions(b) |
|||
} |
|||
|
|||
return &blackfridayConverter{ |
|||
ctx: ctx, |
|||
bf: b, |
|||
extensions: extensions, |
|||
cfg: cfg, |
|||
}, nil |
|||
}), nil |
|||
} |
|||
|
|||
type blackfridayConverter struct { |
|||
ctx converter.DocumentContext |
|||
bf blackfriday_config.Config |
|||
extensions int |
|||
cfg converter.ProviderConfig |
|||
} |
|||
|
|||
func (c *blackfridayConverter) SanitizeAnchorName(s string) string { |
|||
return SanitizedAnchorName(s) |
|||
} |
|||
|
|||
// SanitizedAnchorName is how Blackfriday sanitizes anchor names.
|
|||
// Implementation borrowed from https://github.com/russross/blackfriday/blob/a477dd1646916742841ed20379f941cfa6c5bb6f/block.go#L1464
|
|||
func SanitizedAnchorName(text string) string { |
|||
var anchorName []rune |
|||
futureDash := false |
|||
for _, r := range text { |
|||
switch { |
|||
case unicode.IsLetter(r) || unicode.IsNumber(r): |
|||
if futureDash && len(anchorName) > 0 { |
|||
anchorName = append(anchorName, '-') |
|||
} |
|||
futureDash = false |
|||
anchorName = append(anchorName, unicode.ToLower(r)) |
|||
default: |
|||
futureDash = true |
|||
} |
|||
} |
|||
return string(anchorName) |
|||
} |
|||
|
|||
func (c *blackfridayConverter) AnchorSuffix() string { |
|||
if c.bf.PlainIDAnchors { |
|||
return "" |
|||
} |
|||
return ":" + c.ctx.DocumentID |
|||
} |
|||
|
|||
func (c *blackfridayConverter) Convert(ctx converter.RenderContext) (converter.Result, error) { |
|||
r := c.getHTMLRenderer(ctx.RenderTOC) |
|||
|
|||
return converter.Bytes(blackfriday.Markdown(ctx.Src, r, c.extensions)), nil |
|||
} |
|||
|
|||
func (c *blackfridayConverter) Supports(feature identity.Identity) bool { |
|||
return false |
|||
} |
|||
|
|||
func (c *blackfridayConverter) getHTMLRenderer(renderTOC bool) blackfriday.Renderer { |
|||
flags := getFlags(renderTOC, c.bf) |
|||
|
|||
documentID := c.ctx.DocumentID |
|||
|
|||
renderParameters := blackfriday.HtmlRendererParameters{ |
|||
FootnoteAnchorPrefix: c.bf.FootnoteAnchorPrefix, |
|||
FootnoteReturnLinkContents: c.bf.FootnoteReturnLinkContents, |
|||
} |
|||
|
|||
if documentID != "" && !c.bf.PlainIDAnchors { |
|||
renderParameters.FootnoteAnchorPrefix = documentID + ":" + renderParameters.FootnoteAnchorPrefix |
|||
renderParameters.HeaderIDSuffix = ":" + documentID |
|||
} |
|||
|
|||
return &hugoHTMLRenderer{ |
|||
c: c, |
|||
Renderer: blackfriday.HtmlRendererWithParameters(flags, "", "", renderParameters), |
|||
} |
|||
} |
|||
|
|||
func getFlags(renderTOC bool, cfg blackfriday_config.Config) int { |
|||
var flags int |
|||
|
|||
if renderTOC { |
|||
flags = blackfriday.HTML_TOC |
|||
} |
|||
|
|||
flags |= blackfriday.HTML_USE_XHTML |
|||
flags |= blackfriday.HTML_FOOTNOTE_RETURN_LINKS |
|||
|
|||
if cfg.Smartypants { |
|||
flags |= blackfriday.HTML_USE_SMARTYPANTS |
|||
} |
|||
|
|||
if cfg.SmartypantsQuotesNBSP { |
|||
flags |= blackfriday.HTML_SMARTYPANTS_QUOTES_NBSP |
|||
} |
|||
|
|||
if cfg.AngledQuotes { |
|||
flags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES |
|||
} |
|||
|
|||
if cfg.Fractions { |
|||
flags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS |
|||
} |
|||
|
|||
if cfg.HrefTargetBlank { |
|||
flags |= blackfriday.HTML_HREF_TARGET_BLANK |
|||
} |
|||
|
|||
if cfg.NofollowLinks { |
|||
flags |= blackfriday.HTML_NOFOLLOW_LINKS |
|||
} |
|||
|
|||
if cfg.NoreferrerLinks { |
|||
flags |= blackfriday.HTML_NOREFERRER_LINKS |
|||
} |
|||
|
|||
if cfg.SmartDashes { |
|||
flags |= blackfriday.HTML_SMARTYPANTS_DASHES |
|||
} |
|||
|
|||
if cfg.LatexDashes { |
|||
flags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES |
|||
} |
|||
|
|||
if cfg.SkipHTML { |
|||
flags |= blackfriday.HTML_SKIP_HTML |
|||
} |
|||
|
|||
return flags |
|||
} |
|||
|
|||
func getMarkdownExtensions(cfg blackfriday_config.Config) int { |
|||
// Default Blackfriday common extensions
|
|||
commonExtensions := 0 | |
|||
blackfriday.EXTENSION_NO_INTRA_EMPHASIS | |
|||
blackfriday.EXTENSION_TABLES | |
|||
blackfriday.EXTENSION_FENCED_CODE | |
|||
blackfriday.EXTENSION_AUTOLINK | |
|||
blackfriday.EXTENSION_STRIKETHROUGH | |
|||
blackfriday.EXTENSION_SPACE_HEADERS | |
|||
blackfriday.EXTENSION_HEADER_IDS | |
|||
blackfriday.EXTENSION_BACKSLASH_LINE_BREAK | |
|||
blackfriday.EXTENSION_DEFINITION_LISTS |
|||
|
|||
// Extra Blackfriday extensions that Hugo enables by default
|
|||
flags := commonExtensions | |
|||
blackfriday.EXTENSION_AUTO_HEADER_IDS | |
|||
blackfriday.EXTENSION_FOOTNOTES |
|||
|
|||
for _, extension := range cfg.Extensions { |
|||
if flag, ok := blackfridayExtensionMap[extension]; ok { |
|||
flags |= flag |
|||
} |
|||
} |
|||
for _, extension := range cfg.ExtensionsMask { |
|||
if flag, ok := blackfridayExtensionMap[extension]; ok { |
|||
flags &= ^flag |
|||
} |
|||
} |
|||
return flags |
|||
} |
|||
|
|||
var blackfridayExtensionMap = map[string]int{ |
|||
"noIntraEmphasis": blackfriday.EXTENSION_NO_INTRA_EMPHASIS, |
|||
"tables": blackfriday.EXTENSION_TABLES, |
|||
"fencedCode": blackfriday.EXTENSION_FENCED_CODE, |
|||
"autolink": blackfriday.EXTENSION_AUTOLINK, |
|||
"strikethrough": blackfriday.EXTENSION_STRIKETHROUGH, |
|||
"laxHtmlBlocks": blackfriday.EXTENSION_LAX_HTML_BLOCKS, |
|||
"spaceHeaders": blackfriday.EXTENSION_SPACE_HEADERS, |
|||
"hardLineBreak": blackfriday.EXTENSION_HARD_LINE_BREAK, |
|||
"tabSizeEight": blackfriday.EXTENSION_TAB_SIZE_EIGHT, |
|||
"footnotes": blackfriday.EXTENSION_FOOTNOTES, |
|||
"noEmptyLineBeforeBlock": blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK, |
|||
"headerIds": blackfriday.EXTENSION_HEADER_IDS, |
|||
"titleblock": blackfriday.EXTENSION_TITLEBLOCK, |
|||
"autoHeaderIds": blackfriday.EXTENSION_AUTO_HEADER_IDS, |
|||
"backslashLineBreak": blackfriday.EXTENSION_BACKSLASH_LINE_BREAK, |
|||
"definitionLists": blackfriday.EXTENSION_DEFINITION_LISTS, |
|||
"joinLines": blackfriday.EXTENSION_JOIN_LINES, |
|||
} |
|||
|
|||
var ( |
|||
_ converter.DocumentInfo = (*blackfridayConverter)(nil) |
|||
_ converter.AnchorNameSanitizer = (*blackfridayConverter)(nil) |
|||
) |
@ -1,223 +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 blackfriday |
|||
|
|||
import ( |
|||
"testing" |
|||
|
|||
"github.com/gohugoio/hugo/config" |
|||
|
|||
"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) { |
|||
b := blackfriday_config.Default |
|||
b.Extensions = []string{"headerId"} |
|||
b.ExtensionsMask = []string{"noIntraEmphasis"} |
|||
|
|||
actualFlags := getMarkdownExtensions(b) |
|||
if actualFlags&blackfriday.EXTENSION_NO_INTRA_EMPHASIS == blackfriday.EXTENSION_NO_INTRA_EMPHASIS { |
|||
t.Errorf("Masked out flag {%v} found amongst returned extensions.", blackfriday.EXTENSION_NO_INTRA_EMPHASIS) |
|||
} |
|||
} |
|||
|
|||
func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) { |
|||
type data struct { |
|||
testFlag int |
|||
} |
|||
|
|||
b := blackfriday_config.Default |
|||
|
|||
b.Extensions = []string{""} |
|||
b.ExtensionsMask = []string{""} |
|||
allExtensions := []data{ |
|||
{blackfriday.EXTENSION_NO_INTRA_EMPHASIS}, |
|||
{blackfriday.EXTENSION_TABLES}, |
|||
{blackfriday.EXTENSION_FENCED_CODE}, |
|||
{blackfriday.EXTENSION_AUTOLINK}, |
|||
{blackfriday.EXTENSION_STRIKETHROUGH}, |
|||
// {blackfriday.EXTENSION_LAX_HTML_BLOCKS},
|
|||
{blackfriday.EXTENSION_SPACE_HEADERS}, |
|||
// {blackfriday.EXTENSION_HARD_LINE_BREAK},
|
|||
// {blackfriday.EXTENSION_TAB_SIZE_EIGHT},
|
|||
{blackfriday.EXTENSION_FOOTNOTES}, |
|||
// {blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK},
|
|||
{blackfriday.EXTENSION_HEADER_IDS}, |
|||
// {blackfriday.EXTENSION_TITLEBLOCK},
|
|||
{blackfriday.EXTENSION_AUTO_HEADER_IDS}, |
|||
{blackfriday.EXTENSION_BACKSLASH_LINE_BREAK}, |
|||
{blackfriday.EXTENSION_DEFINITION_LISTS}, |
|||
} |
|||
|
|||
actualFlags := getMarkdownExtensions(b) |
|||
for _, e := range allExtensions { |
|||
if actualFlags&e.testFlag != e.testFlag { |
|||
t.Errorf("Flag %v was not found in the list of extensions.", e) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) { |
|||
b := blackfriday_config.Default |
|||
|
|||
b.Extensions = []string{"definitionLists"} |
|||
b.ExtensionsMask = []string{""} |
|||
|
|||
actualFlags := getMarkdownExtensions(b) |
|||
if actualFlags&blackfriday.EXTENSION_DEFINITION_LISTS != blackfriday.EXTENSION_DEFINITION_LISTS { |
|||
t.Errorf("Masked out flag {%v} found amongst returned extensions.", blackfriday.EXTENSION_DEFINITION_LISTS) |
|||
} |
|||
} |
|||
|
|||
func TestGetFlags(t *testing.T) { |
|||
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) |
|||
} |
|||
} |
|||
|
|||
func TestGetAllFlags(t *testing.T) { |
|||
c := qt.New(t) |
|||
|
|||
b := blackfriday_config.Default |
|||
|
|||
type data struct { |
|||
testFlag int |
|||
} |
|||
|
|||
allFlags := []data{ |
|||
{blackfriday.HTML_USE_XHTML}, |
|||
{blackfriday.HTML_FOOTNOTE_RETURN_LINKS}, |
|||
{blackfriday.HTML_USE_SMARTYPANTS}, |
|||
{blackfriday.HTML_SMARTYPANTS_QUOTES_NBSP}, |
|||
{blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES}, |
|||
{blackfriday.HTML_SMARTYPANTS_FRACTIONS}, |
|||
{blackfriday.HTML_HREF_TARGET_BLANK}, |
|||
{blackfriday.HTML_NOFOLLOW_LINKS}, |
|||
{blackfriday.HTML_NOREFERRER_LINKS}, |
|||
{blackfriday.HTML_SMARTYPANTS_DASHES}, |
|||
{blackfriday.HTML_SMARTYPANTS_LATEX_DASHES}, |
|||
} |
|||
|
|||
b.AngledQuotes = true |
|||
b.Fractions = true |
|||
b.HrefTargetBlank = true |
|||
b.NofollowLinks = true |
|||
b.NoreferrerLinks = true |
|||
b.LatexDashes = true |
|||
b.PlainIDAnchors = true |
|||
b.SmartDashes = true |
|||
b.Smartypants = true |
|||
b.SmartypantsQuotesNBSP = true |
|||
|
|||
actualFlags := getFlags(false, b) |
|||
|
|||
var expectedFlags int |
|||
// OR-ing flags together...
|
|||
for _, d := range allFlags { |
|||
expectedFlags |= d.testFlag |
|||
} |
|||
|
|||
c.Assert(actualFlags, qt.Equals, expectedFlags) |
|||
} |
|||
|
|||
func TestConvert(t *testing.T) { |
|||
c := qt.New(t) |
|||
p, err := Provider.New(converter.ProviderConfig{ |
|||
Cfg: config.New(), |
|||
}) |
|||
c.Assert(err, qt.IsNil) |
|||
conv, err := p.New(converter.DocumentContext{}) |
|||
c.Assert(err, qt.IsNil) |
|||
b, err := conv.Convert(converter.RenderContext{Src: []byte("testContent")}) |
|||
c.Assert(err, qt.IsNil) |
|||
c.Assert(string(b.Bytes()), qt.Equals, "<p>testContent</p>\n") |
|||
} |
|||
|
|||
func TestGetHTMLRendererAnchors(t *testing.T) { |
|||
c := qt.New(t) |
|||
p, err := Provider.New(converter.ProviderConfig{ |
|||
Cfg: config.New(), |
|||
}) |
|||
c.Assert(err, qt.IsNil) |
|||
conv, err := p.New(converter.DocumentContext{ |
|||
DocumentID: "testid", |
|||
ConfigOverrides: map[string]any{ |
|||
"plainIDAnchors": false, |
|||
"footnotes": true, |
|||
}, |
|||
}) |
|||
c.Assert(err, qt.IsNil) |
|||
b, err := conv.Convert(converter.RenderContext{Src: []byte(`# Header |
|||
|
|||
This is a footnote.[^1] And then some. |
|||
|
|||
|
|||
[^1]: Footnote text. |
|||
|
|||
`)}) |
|||
|
|||
c.Assert(err, qt.IsNil) |
|||
s := string(b.Bytes()) |
|||
c.Assert(s, qt.Contains, "<h1 id=\"header:testid\">Header</h1>") |
|||
c.Assert(s, qt.Contains, "This is a footnote.<sup class=\"footnote-ref\" id=\"fnref:testid:1\"><a href=\"#fn:testid:1\">1</a></sup>") |
|||
c.Assert(s, qt.Contains, "<a class=\"footnote-return\" href=\"#fnref:testid:1\"><sup>[return]</sup></a>") |
|||
} |
|||
|
|||
// Tests borrowed from https://github.com/russross/blackfriday/blob/a925a152c144ea7de0f451eaf2f7db9e52fa005a/block_test.go#L1817
|
|||
func TestSanitizedAnchorName(t *testing.T) { |
|||
tests := []struct { |
|||
text string |
|||
want string |
|||
}{ |
|||
{ |
|||
text: "This is a header", |
|||
want: "this-is-a-header", |
|||
}, |
|||
{ |
|||
text: "This is also a header", |
|||
want: "this-is-also-a-header", |
|||
}, |
|||
{ |
|||
text: "main.go", |
|||
want: "main-go", |
|||
}, |
|||
{ |
|||
text: "Article 123", |
|||
want: "article-123", |
|||
}, |
|||
{ |
|||
text: "<- Let's try this, shall we?", |
|||
want: "let-s-try-this-shall-we", |
|||
}, |
|||
{ |
|||
text: " ", |
|||
want: "", |
|||
}, |
|||
{ |
|||
text: "Hello, 世界", |
|||
want: "hello-世界", |
|||
}, |
|||
} |
|||
for _, test := range tests { |
|||
if got := SanitizedAnchorName(test.text); got != test.want { |
|||
t.Errorf("SanitizedAnchorName(%q):\ngot %q\nwant %q", test.text, got, test.want) |
|||
} |
|||
} |
|||
} |
@ -1,84 +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 blackfriday |
|||
|
|||
import ( |
|||
"bytes" |
|||
"strings" |
|||
|
|||
"github.com/russross/blackfriday" |
|||
) |
|||
|
|||
// hugoHTMLRenderer wraps a blackfriday.Renderer, typically a blackfriday.Html
|
|||
// adding some custom behaviour.
|
|||
type hugoHTMLRenderer struct { |
|||
c *blackfridayConverter |
|||
blackfriday.Renderer |
|||
} |
|||
|
|||
// BlockCode renders a given text as a block of code.
|
|||
// Chroma is used if it is setup to handle code fences.
|
|||
func (r *hugoHTMLRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) { |
|||
if r.c.cfg.MarkupConfig.Highlight.CodeFences { |
|||
str := strings.Trim(string(text), "\n\r") |
|||
highlighted, _ := r.c.cfg.Highlight(str, lang, "") |
|||
out.WriteString(highlighted) |
|||
} else { |
|||
r.Renderer.BlockCode(out, text, lang) |
|||
} |
|||
} |
|||
|
|||
// ListItem adds task list support to the Blackfriday renderer.
|
|||
func (r *hugoHTMLRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) { |
|||
if !r.c.bf.TaskLists { |
|||
r.Renderer.ListItem(out, text, flags) |
|||
return |
|||
} |
|||
|
|||
switch { |
|||
case bytes.HasPrefix(text, []byte("[ ] ")): |
|||
text = append([]byte(`<label><input type="checkbox" disabled class="task-list-item">`), text[3:]...) |
|||
text = append(text, []byte(`</label>`)...) |
|||
|
|||
case bytes.HasPrefix(text, []byte("[x] ")) || bytes.HasPrefix(text, []byte("[X] ")): |
|||
text = append([]byte(`<label><input type="checkbox" checked disabled class="task-list-item">`), text[3:]...) |
|||
text = append(text, []byte(`</label>`)...) |
|||
} |
|||
|
|||
r.Renderer.ListItem(out, text, flags) |
|||
} |
|||
|
|||
// List adds task list support to the Blackfriday renderer.
|
|||
func (r *hugoHTMLRenderer) List(out *bytes.Buffer, text func() bool, flags int) { |
|||
if !r.c.bf.TaskLists { |
|||
r.Renderer.List(out, text, flags) |
|||
return |
|||
} |
|||
marker := out.Len() |
|||
r.Renderer.List(out, text, flags) |
|||
if out.Len() > marker { |
|||
list := out.Bytes()[marker:] |
|||
if bytes.Contains(list, []byte("task-list-item")) { |
|||
// Find the index of the first >, it might be 3 or 4 depending on whether
|
|||
// there is a new line at the start, but this is safer than just hardcoding it.
|
|||
closingBracketIndex := bytes.Index(list, []byte(">")) |
|||
// Rewrite the buffer from the marker
|
|||
out.Truncate(marker) |
|||
// Safely assuming closingBracketIndex won't be -1 since there is a list
|
|||
// May be either dl, ul or ol
|
|||
list := append(list[:closingBracketIndex], append([]byte(` class="task-list"`), list[closingBracketIndex:]...)...) |
|||
out.Write(list) |
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue