diff --git a/commands/hugo.go b/commands/hugo.go
index f57a7c910..94c955925 100644
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -136,7 +136,7 @@ func InitializeConfig() {
viper.SetDefault("FootnoteAnchorPrefix", "")
viper.SetDefault("FootnoteReturnLinkContents", "")
viper.SetDefault("NewContentEditor", "")
- viper.SetDefault("Blackfriday", map[string]bool{"angledQuotes": false, "fractions": true, "plainIdAnchors": false})
+ viper.SetDefault("Blackfriday", new(helpers.Blackfriday))
if hugoCmdV.PersistentFlags().Lookup("buildDrafts").Changed {
viper.Set("BuildDrafts", Draft)
diff --git a/docs/content/overview/configuration.md b/docs/content/overview/configuration.md
index a07ef5672..c492296fc 100644
--- a/docs/content/overview/configuration.md
+++ b/docs/content/overview/configuration.md
@@ -71,7 +71,7 @@ Here is a yaml configuration file which sets a few more options
[Blackfriday](https://github.com/russross/blackfriday) is the [Markdown](http://daringfireball.net/projects/markdown/) rendering engine used in Hugo. The Blackfriday configuration in Hugo is mostly a set of sane defaults that should fit most use cases.
-But Hugo does expose some options---as listed in the table below, matched with the corresponding flag in the [Blackfriday source](https://github.com/russross/blackfriday/blob/master/html.go):
+But Hugo does expose some options---as listed in the table below, matched with the corresponding flag in the Blackfriday source ([html.go](https://github.com/russross/blackfriday/blob/master/html.go) and [markdown.go](https://github.com/russross/blackfriday/blob/master/markdown.go)):
@@ -115,6 +115,16 @@ but only these three.
Purpose: |
If true , then header and footnote IDs are generated without the document ID (e.g. #my-header instead of #my-header:bec3ed8ba720b9073ab75abcf3ba5d97 ) |
+
+
+extensions |
+[] |
+EXTENSION_* |
+
+
+Purpose: |
+Use non-default additional extensions (e.g. Add "hardLineBreak" to use EXTENSION_HARD_LINE_BREAK ) |
+
@@ -130,11 +140,14 @@ but only these three.
angledQuotes = true
fractions = false
plainIdAnchors = true
+ extensions = ["hardLineBreak"]
blackfriday:
angledQuotes: true
fractions: false
plainIdAnchors: true
+ extensions:
+ - hardLineBreak
|
diff --git a/helpers/content.go b/helpers/content.go
index 3f9cc55d5..59207964e 100644
--- a/helpers/content.go
+++ b/helpers/content.go
@@ -28,6 +28,7 @@ import (
jww "github.com/spf13/jwalterweatherman"
"strings"
+ "sync"
)
// Length of the summary that Hugo extracts from a content.
@@ -36,6 +37,30 @@ var SummaryLength = 70
// Custom divider let's user define where summarization ends.
var SummaryDivider = []byte("")
+type Blackfriday struct {
+ AngledQuotes bool
+ Fractions bool
+ PlainIdAnchors bool
+ Extensions []string
+}
+
+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,
+}
+
// StripHTML accepts a string, strips out all HTML tags and returns it.
func StripHTML(s string) string {
output := ""
@@ -87,7 +112,7 @@ func GetHtmlRenderer(defaultFlags int, ctx RenderingContext) blackfriday.Rendere
b := len(ctx.DocumentId) != 0
- if m, ok := ctx.ConfigFlags["plainIdAnchors"]; b && ((ok && !m) || !ok) {
+ if b && !ctx.getConfig().PlainIdAnchors {
renderParameters.FootnoteAnchorPrefix = ctx.DocumentId + ":" + renderParameters.FootnoteAnchorPrefix
renderParameters.HeaderIDSuffix = ":" + ctx.DocumentId
}
@@ -99,40 +124,40 @@ func GetHtmlRenderer(defaultFlags int, ctx RenderingContext) blackfriday.Rendere
htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
htmlFlags |= blackfriday.HTML_FOOTNOTE_RETURN_LINKS
- var angledQuotes bool
-
- if m, ok := ctx.ConfigFlags["angledQuotes"]; ok {
- angledQuotes = m
- }
-
- if angledQuotes {
+ if ctx.getConfig().AngledQuotes {
htmlFlags |= blackfriday.HTML_SMARTYPANTS_ANGLED_QUOTES
}
- if m, ok := ctx.ConfigFlags["fractions"]; ok && !m {
+ if !ctx.getConfig().Fractions {
htmlFlags &^= blackfriday.HTML_SMARTYPANTS_FRACTIONS
}
return blackfriday.HtmlRendererWithParameters(htmlFlags, "", "", renderParameters)
}
-func GetMarkdownExtensions() int {
- return 0 | blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
+func GetMarkdownExtensions(ctx RenderingContext) int {
+ flags := 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_FOOTNOTES |
blackfriday.EXTENSION_HEADER_IDS | blackfriday.EXTENSION_AUTO_HEADER_IDS
+ for _, extension := range ctx.getConfig().Extensions {
+ if flag, ok := blackfridayExtensionMap[extension]; ok {
+ flags |= flag
+ }
+ }
+ return flags
}
func MarkdownRender(ctx RenderingContext) []byte {
return blackfriday.Markdown(ctx.Content, GetHtmlRenderer(0, ctx),
- GetMarkdownExtensions())
+ GetMarkdownExtensions(ctx))
}
func MarkdownRenderWithTOC(ctx RenderingContext) []byte {
return blackfriday.Markdown(ctx.Content,
GetHtmlRenderer(blackfriday.HTML_TOC, ctx),
- GetMarkdownExtensions())
+ GetMarkdownExtensions(ctx))
}
// ExtractTOC extracts Table of Contents from content.
@@ -172,10 +197,20 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) {
}
type RenderingContext struct {
- Content []byte
- PageFmt string
- DocumentId string
- ConfigFlags map[string]bool
+ Content []byte
+ PageFmt string
+ DocumentId string
+ Config *Blackfriday
+ configInit sync.Once
+}
+
+func (c *RenderingContext) getConfig() *Blackfriday {
+ c.configInit.Do(func() {
+ if c.Config == nil {
+ c.Config = new(Blackfriday)
+ }
+ })
+ return c.Config
}
func RenderBytesWithTOC(ctx RenderingContext) []byte {
@@ -261,8 +296,8 @@ func GetRstContent(content []byte) string {
path, err = exec.LookPath("rst2html.py")
if err != nil {
jww.ERROR.Println("rst2html / rst2html.py not found in $PATH: Please install.\n",
- " Leaving reStructuredText content unrendered.")
- return(string(content))
+ " Leaving reStructuredText content unrendered.")
+ return (string(content))
}
}
diff --git a/hugolib/page.go b/hugolib/page.go
index eadf6eb3d..ffbe7772b 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -17,16 +17,12 @@ import (
"bytes"
"errors"
"fmt"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/parser"
"reflect"
- "github.com/spf13/cast"
- "github.com/spf13/hugo/hugofs"
- "github.com/spf13/hugo/source"
- "github.com/spf13/hugo/tpl"
- jww "github.com/spf13/jwalterweatherman"
- "github.com/spf13/viper"
+ "github.com/mitchellh/mapstructure"
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/parser"
+
"html/template"
"io"
"net/url"
@@ -35,6 +31,13 @@ import (
"strings"
"sync"
"time"
+
+ "github.com/spf13/cast"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/tpl"
+ jww "github.com/spf13/jwalterweatherman"
+ "github.com/spf13/viper"
)
type Page struct {
@@ -52,17 +55,17 @@ type Page struct {
Tmpl tpl.Template
Markup string
- extension string
- contentType string
- renderable bool
- layout string
- linkTitle string
- frontmatter []byte
- rawContent []byte
- contentShortCodes map[string]string
- plain string // TODO should be []byte
- renderingConfigFlags map[string]bool
- renderingConfigFlagsInit sync.Once
+ extension string
+ contentType string
+ renderable bool
+ layout string
+ linkTitle string
+ frontmatter []byte
+ rawContent []byte
+ contentShortCodes map[string]string
+ plain string // TODO should be []byte
+ renderingConfig *helpers.Blackfriday
+ renderingConfigInit sync.Once
PageMeta
Source
Position
@@ -182,37 +185,33 @@ func (p *Page) setSummary() {
func (p *Page) renderBytes(content []byte) []byte {
return helpers.RenderBytes(
helpers.RenderingContext{Content: content, PageFmt: p.guessMarkupType(),
- DocumentId: p.UniqueId(), ConfigFlags: p.getRenderingConfigFlags()})
+ DocumentId: p.UniqueId(), Config: p.getRenderingConfig()})
}
func (p *Page) renderContent(content []byte) []byte {
return helpers.RenderBytesWithTOC(helpers.RenderingContext{Content: content, PageFmt: p.guessMarkupType(),
- DocumentId: p.UniqueId(), ConfigFlags: p.getRenderingConfigFlags()})
+ DocumentId: p.UniqueId(), Config: p.getRenderingConfig()})
}
-func (p *Page) getRenderingConfigFlags() map[string]bool {
-
- p.renderingConfigFlagsInit.Do(func() {
- p.renderingConfigFlags = make(map[string]bool)
+func (p *Page) getRenderingConfig() *helpers.Blackfriday {
+ p.renderingConfigInit.Do(func() {
pageParam := p.GetParam("blackfriday")
siteParam := viper.GetStringMap("blackfriday")
- p.renderingConfigFlags = cast.ToStringMapBool(siteParam)
-
if pageParam != nil {
- pageFlags := cast.ToStringMapBool(pageParam)
- for key, value := range pageFlags {
- p.renderingConfigFlags[key] = value
+ pageConfig := cast.ToStringMap(pageParam)
+ for key, value := range pageConfig {
+ siteParam[key] = value
}
}
+ p.renderingConfig = new(helpers.Blackfriday)
+ if err := mapstructure.Decode(siteParam, p.renderingConfig); err != nil {
+ jww.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
+ }
})
- return p.renderingConfigFlags
-}
-
-func (p *Page) isRenderingFlagEnabled(flag string) bool {
- return p.getRenderingConfigFlags()[flag]
+ return p.renderingConfig
}
func newPage(filename string) *Page {
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index d4ce7a0a0..dc8ebf64b 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -212,6 +212,16 @@ the cylinder and strike me down. ## BB
### BBB
"You're a great Granser," he cried delightedly, "always making believe them little marks mean something."
+`
+
+ SIMPLE_PAGE_WITH_ADDITIONAL_EXTENSION = `+++
+[blackfriday]
+ extensions = ["hardLineBreak"]
++++
+first line.
+second line.
+
+fourth line.
`
)
@@ -366,6 +376,16 @@ func TestPageWithEmbeddedScriptTag(t *testing.T) {
checkPageContent(t, p, "\n")
}
+func TestPageWithAdditionalExtension(t *testing.T) {
+ p, _ := NewPage("simple.md")
+ err := p.ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_ADDITIONAL_EXTENSION))
+ p.Convert()
+ if err != nil {
+ t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
+ }
+ checkPageContent(t, p, "first line.
\nsecond line.
\n\nfourth line.
\n")
+}
+
func TestTableOfContents(t *testing.T) {
p, _ := NewPage("tocpage.md")
err := p.ReadFrom(strings.NewReader(PAGE_WITH_TOC))
diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
index ff3eaeb89..03cd7d4a7 100644
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -205,7 +205,7 @@ func renderShortcode(sc shortcode, p *Page, t tpl.Template) string {
if sc.doMarkup {
newInner := helpers.RenderBytes(helpers.RenderingContext{
Content: []byte(inner), PageFmt: p.guessMarkupType(),
- DocumentId: p.UniqueId(), ConfigFlags: p.getRenderingConfigFlags()})
+ DocumentId: p.UniqueId(), Config: p.getRenderingConfig()})
// If the type is “unknown” or “markdown”, we assume the markdown
// generation has been performed. Given the input: `a line`, markdown
diff --git a/hugolib/site.go b/hugolib/site.go
index 39e900cfd..a6a18a2fe 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -187,9 +187,9 @@ func (s *SiteInfo) refLink(ref string, page *Page, relative bool) (string, error
if refUrl.Fragment != "" {
link = link + "#" + refUrl.Fragment
- if refUrl.Path != "" && target != nil && !target.isRenderingFlagEnabled("plainIdAnchors") {
+ if refUrl.Path != "" && target != nil && !target.getRenderingConfig().PlainIdAnchors {
link = link + ":" + target.UniqueId()
- } else if page != nil && !page.isRenderingFlagEnabled("plainIdAnchors") {
+ } else if page != nil && !page.getRenderingConfig().PlainIdAnchors {
link = link + ":" + page.UniqueId()
}
}