Reuse the BlackFriday instance when possible
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 16 Dec 2017 17:56:58 +0000 (18:56 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 16 Dec 2017 18:44:33 +0000 (19:44 +0100)
This is in heavy use in rendering, so this makes a difference:

```bash
benchmark                                                                                    old ns/op     new ns/op     delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4     124551144     107743429     -13.49%

benchmark                                                                                    old allocs     new allocs     delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4     528684         435118         -17.70%

benchmark                                                                                    old bytes     new bytes     delta
BenchmarkSiteBuilding/TOML,num_langs=1,num_pages=500,tags_per_page=5,shortcodes,render-4     53306848      45147832      -15.31%
```

helpers/content.go
helpers/content_renderer_test.go
helpers/content_test.go
hugolib/config.go
hugolib/page.go
tpl/transform/transform.go

index b776d08137a81f6d0ead5dd148b3484ca6d3a9b9..ca93d7d9e4c2069adcaea28c7638f70ef022c77d 100644 (file)
@@ -41,7 +41,7 @@ var SummaryDivider = []byte("<!--more-->")
 
 // ContentSpec provides functionality to render markdown content.
 type ContentSpec struct {
-       blackfriday                map[string]interface{}
+       BlackFriday                *BlackFriday
        footnoteAnchorPrefix       string
        footnoteReturnLinkContents string
        // SummaryLength is the length of the summary that Hugo extracts from a content.
@@ -56,8 +56,9 @@ type ContentSpec struct {
 // NewContentSpec returns a ContentSpec initialized
 // with the appropriate fields from the given config.Provider.
 func NewContentSpec(cfg config.Provider) (*ContentSpec, error) {
+       bf := newBlackfriday(cfg.GetStringMap("blackfriday"))
        spec := &ContentSpec{
-               blackfriday:                cfg.GetStringMap("blackfriday"),
+               BlackFriday:                bf,
                footnoteAnchorPrefix:       cfg.GetString("footnoteAnchorPrefix"),
                footnoteReturnLinkContents: cfg.GetString("footnoteReturnLinkContents"),
                summaryLength:              cfg.GetInt("summaryLength"),
@@ -93,8 +94,8 @@ func NewContentSpec(cfg config.Provider) (*ContentSpec, error) {
        return spec, nil
 }
 
-// Blackfriday holds configuration values for Blackfriday rendering.
-type Blackfriday struct {
+// BlackFriday holds configuration values for BlackFriday rendering.
+type BlackFriday struct {
        Smartypants           bool
        SmartypantsQuotesNBSP bool
        AngledQuotes          bool
@@ -109,7 +110,7 @@ type Blackfriday struct {
 }
 
 // NewBlackfriday creates a new Blackfriday filled with site config or some sane defaults.
-func (c ContentSpec) NewBlackfriday() *Blackfriday {
+func newBlackfriday(config map[string]interface{}) *BlackFriday {
        defaultParam := map[string]interface{}{
                "smartypants":           true,
                "angledQuotes":          false,
@@ -130,13 +131,13 @@ func (c ContentSpec) NewBlackfriday() *Blackfriday {
                siteConfig[k] = v
        }
 
-       if c.blackfriday != nil {
-               for k, v := range c.blackfriday {
+       if config != nil {
+               for k, v := range config {
                        siteConfig[k] = v
                }
        }
 
-       combinedConfig := &Blackfriday{}
+       combinedConfig := &BlackFriday{}
        if err := mapstructure.Decode(siteConfig, combinedConfig); err != nil {
                jww.FATAL.Printf("Failed to get site rendering config\n%s", err.Error())
        }
@@ -434,7 +435,7 @@ type RenderingContext struct {
        PageFmt      string
        DocumentID   string
        DocumentName string
-       Config       *Blackfriday
+       Config       *BlackFriday
        RenderTOC    bool
        Cfg          config.Provider
 }
index 698e3a151fe7b2a461cab1c61a5ad3cb05a5ef8d..a01014b4eb3c7129279242dc67752022806a8763 100644 (file)
@@ -24,7 +24,7 @@ import (
 
 // Renders a codeblock using Blackfriday
 func (c ContentSpec) render(input string) string {
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        render := c.getHTMLRenderer(0, ctx)
 
        buf := &bytes.Buffer{}
@@ -34,7 +34,7 @@ func (c ContentSpec) render(input string) string {
 
 // Renders a codeblock using Mmark
 func (c ContentSpec) renderWithMmark(input string) string {
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        render := c.getMmarkHTMLRenderer(0, ctx)
 
        buf := &bytes.Buffer{}
@@ -128,7 +128,7 @@ END`, true, `<ul class="task-list">
 <p>END</p>
 `},
        } {
-               blackFridayConfig := c.NewBlackfriday()
+               blackFridayConfig := c.BlackFriday
                blackFridayConfig.TaskLists = this.taskListEnabled
                ctx := &RenderingContext{Content: []byte(this.markdown), PageFmt: "markdown", Config: blackFridayConfig}
 
index 8f2d44cd9cfd92692e3e91015167fa831a5e551a..c10ad881ba7a2803f61794ed51d5d771f73bc05c 100644 (file)
@@ -159,7 +159,7 @@ func TestTruncateWordsByRune(t *testing.T) {
 
 func TestGetHTMLRendererFlags(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        renderer := c.getHTMLRenderer(blackfriday.HTML_USE_XHTML, ctx)
        flags := renderer.GetFlags()
        if flags&blackfriday.HTML_USE_XHTML != blackfriday.HTML_USE_XHTML {
@@ -186,7 +186,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
                {blackfriday.HTML_SMARTYPANTS_LATEX_DASHES},
        }
        defaultFlags := blackfriday.HTML_USE_XHTML
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Config.AngledQuotes = true
        ctx.Config.Fractions = true
        ctx.Config.HrefTargetBlank = true
@@ -209,7 +209,7 @@ func TestGetHTMLRendererAllFlags(t *testing.T) {
 
 func TestGetHTMLRendererAnchors(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.DocumentID = "testid"
        ctx.Config.PlainIDAnchors = false
 
@@ -233,7 +233,7 @@ func TestGetHTMLRendererAnchors(t *testing.T) {
 
 func TestGetMmarkHTMLRenderer(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.DocumentID = "testid"
        ctx.Config.PlainIDAnchors = false
        actualRenderer := c.getMmarkHTMLRenderer(0, ctx)
@@ -257,7 +257,7 @@ func TestGetMmarkHTMLRenderer(t *testing.T) {
 
 func TestGetMarkdownExtensionsMasksAreRemovedFromExtensions(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Config.Extensions = []string{"headerId"}
        ctx.Config.ExtensionsMask = []string{"noIntraEmphasis"}
 
@@ -272,7 +272,7 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
                testFlag int
        }
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Config.Extensions = []string{""}
        ctx.Config.ExtensionsMask = []string{""}
        allExtensions := []data{
@@ -304,7 +304,7 @@ func TestGetMarkdownExtensionsByDefaultAllExtensionsAreEnabled(t *testing.T) {
 
 func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Config.Extensions = []string{"definitionLists"}
        ctx.Config.ExtensionsMask = []string{""}
 
@@ -316,7 +316,7 @@ func TestGetMarkdownExtensionsAddingFlagsThroughRenderingContext(t *testing.T) {
 
 func TestGetMarkdownRenderer(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Content = []byte("testContent")
        actualRenderedMarkdown := c.markdownRender(ctx)
        expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
@@ -327,7 +327,7 @@ func TestGetMarkdownRenderer(t *testing.T) {
 
 func TestGetMarkdownRendererWithTOC(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{RenderTOC: true, Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{RenderTOC: true, Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Content = []byte("testContent")
        actualRenderedMarkdown := c.markdownRender(ctx)
        expectedRenderedMarkdown := []byte("<nav>\n</nav>\n\n<p>testContent</p>\n")
@@ -342,7 +342,7 @@ func TestGetMmarkExtensions(t *testing.T) {
                testFlag int
        }
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Config.Extensions = []string{"tables"}
        ctx.Config.ExtensionsMask = []string{""}
        allExtensions := []data{
@@ -371,7 +371,7 @@ func TestGetMmarkExtensions(t *testing.T) {
 
 func TestMmarkRender(t *testing.T) {
        c := newTestContentSpec()
-       ctx := &RenderingContext{Cfg: c.cfg, Config: c.NewBlackfriday()}
+       ctx := &RenderingContext{Cfg: c.cfg, Config: c.BlackFriday}
        ctx.Content = []byte("testContent")
        actualRenderedMarkdown := c.mmarkRender(ctx)
        expectedRenderedMarkdown := []byte("<p>testContent</p>\n")
index da84ab8b25c9fedc036ff904fc5028cd7bdbdad9..62098aeea99c88017bd342e870c1ca947b1ba93a 100644 (file)
@@ -210,7 +210,7 @@ func loadDefaultSettingsFor(v *viper.Viper) error {
        v.SetDefault("paginate", 10)
        v.SetDefault("paginatePath", "page")
        v.SetDefault("summaryLength", 70)
-       v.SetDefault("blackfriday", c.NewBlackfriday())
+       v.SetDefault("blackfriday", c.BlackFriday)
        v.SetDefault("rSSUri", "index.xml")
        v.SetDefault("rssLimit", -1)
        v.SetDefault("sectionPagesMenu", "")
index 2ab4dbc348b037a77a5d8d6bb600a5dbe184f355..dba1ee227afc6e880af7aa0f865166f0b4e78d1d 100644 (file)
@@ -168,7 +168,7 @@ type Page struct {
        plainWords []string
 
        // rendering configuration
-       renderingConfig *helpers.Blackfriday
+       renderingConfig *helpers.BlackFriday
 
        // menus
        pageMenus PageMenus
@@ -700,18 +700,20 @@ func (p *Page) renderContent(content []byte) []byte {
                Config: p.getRenderingConfig()})
 }
 
-func (p *Page) getRenderingConfig() *helpers.Blackfriday {
+func (p *Page) getRenderingConfig() *helpers.BlackFriday {
        p.renderingConfigInit.Do(func() {
-               p.renderingConfig = p.s.ContentSpec.NewBlackfriday()
-
-               if p.Language() == nil {
-                       panic(fmt.Sprintf("nil language for %s with source lang %s", p.BaseFileName(), p.lang))
-               }
-
                bfParam := p.GetParam("blackfriday")
                if bfParam == nil {
+                       p.renderingConfig = p.s.ContentSpec.BlackFriday
                        return
                }
+               // Create a copy so we can modify it.
+               bf := *p.s.ContentSpec.BlackFriday
+               p.renderingConfig = &bf
+
+               if p.Language() == nil {
+                       panic(fmt.Sprintf("nil language for %s with source lang %s", p.BaseFileName(), p.lang))
+               }
 
                pageParam := cast.ToStringMap(bfParam)
                if err := mapstructure.Decode(pageParam, &p.renderingConfig); err != nil {
index f1ffa77ae279c7aa26b121ad1d8ff979ba3d39b8..a94cc46e2bc7ab65e6f04d58ae62bd914ab75e12 100644 (file)
@@ -98,7 +98,7 @@ func (ns *Namespace) Markdownify(s interface{}) (template.HTML, error) {
                        Cfg:     ns.deps.Cfg,
                        Content: []byte(ss),
                        PageFmt: "markdown",
-                       Config:  ns.deps.ContentSpec.NewBlackfriday(),
+                       Config:  ns.deps.ContentSpec.BlackFriday,
                },
        )