diff --git a/helpers/content.go b/helpers/content.go index f60349055..3ac91b360 100644 --- a/helpers/content.go +++ b/helpers/content.go @@ -23,6 +23,10 @@ import ( "os/exec" "unicode/utf8" + "fmt" + "strings" + "sync" + "github.com/miekg/mmark" "github.com/mitchellh/mapstructure" "github.com/russross/blackfriday" @@ -30,9 +34,6 @@ import ( bp "github.com/spf13/hugo/bufferpool" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" - - "strings" - "sync" ) // SummaryLength is the length of the summary that Hugo extracts from a content. @@ -167,11 +168,11 @@ func getHTMLRenderer(defaultFlags int, ctx *RenderingContext) blackfriday.Render FootnoteReturnLinkContents: viper.GetString("FootnoteReturnLinkContents"), } - b := len(ctx.DocumentID) != 0 + b := ctx.DocumentID != 0 if b && !ctx.getConfig().PlainIDAnchors { - renderParameters.FootnoteAnchorPrefix = ctx.DocumentID + ":" + renderParameters.FootnoteAnchorPrefix - renderParameters.HeaderIDSuffix = ":" + ctx.DocumentID + renderParameters.FootnoteAnchorPrefix = fmt.Sprintf("%d:%s", ctx.DocumentID, renderParameters.FootnoteAnchorPrefix) + renderParameters.HeaderIDSuffix = fmt.Sprintf(":%d", ctx.DocumentID) } htmlFlags := defaultFlags @@ -258,10 +259,10 @@ func getMmarkHTMLRenderer(defaultFlags int, ctx *RenderingContext) mmark.Rendere FootnoteReturnLinkContents: viper.GetString("FootnoteReturnLinkContents"), } - b := len(ctx.DocumentID) != 0 + b := ctx.DocumentID != 0 if b && !ctx.getConfig().PlainIDAnchors { - renderParameters.FootnoteAnchorPrefix = ctx.DocumentID + ":" + renderParameters.FootnoteAnchorPrefix + renderParameters.FootnoteAnchorPrefix = fmt.Sprintf("%d:%s", ctx.DocumentID, renderParameters.FootnoteAnchorPrefix) // renderParameters.HeaderIDSuffix = ":" + ctx.DocumentId } @@ -343,7 +344,7 @@ func ExtractTOC(content []byte) (newcontent []byte, toc []byte) { type RenderingContext struct { Content []byte PageFmt string - DocumentID string + DocumentID int Config *Blackfriday FileResolver FileResolverFunc LinkResolver LinkResolverFunc diff --git a/helpers/content_test.go b/helpers/content_test.go index a89b4992e..347884f0a 100644 --- a/helpers/content_test.go +++ b/helpers/content_test.go @@ -172,15 +172,15 @@ func TestGetHTMLRendererAllFlags(t *testing.T) { func TestGetHTMLRendererAnchors(t *testing.T) { ctx := &RenderingContext{} - ctx.DocumentID = "testid" + ctx.DocumentID = 123 ctx.Config = ctx.getConfig() ctx.Config.PlainIDAnchors = false actualRenderer := getHTMLRenderer(0, ctx) headerBuffer := &bytes.Buffer{} footnoteBuffer := &bytes.Buffer{} - expectedFootnoteHref := []byte("href=\"#fn:testid:href\"") - expectedHeaderID := []byte("
\n") + expectedFootnoteHref := []byte("href=\"#fn:123:href\"") + expectedHeaderID := []byte("\n") actualRenderer.Header(headerBuffer, func() bool { return true }, 1, "id") actualRenderer.FootnoteRef(footnoteBuffer, []byte("href"), 1) @@ -196,14 +196,14 @@ func TestGetHTMLRendererAnchors(t *testing.T) { func TestGetMmarkHTMLRenderer(t *testing.T) { ctx := &RenderingContext{} - ctx.DocumentID = "testid" + ctx.DocumentID = 321 ctx.Config = ctx.getConfig() ctx.Config.PlainIDAnchors = false actualRenderer := getMmarkHTMLRenderer(0, ctx) headerBuffer := &bytes.Buffer{} footnoteBuffer := &bytes.Buffer{} - expectedFootnoteHref := []byte("href=\"#fn:testid:href\"") + expectedFootnoteHref := []byte("href=\"#fn:321:href\"") expectedHeaderID := []byte("") actualRenderer.FootnoteRef(footnoteBuffer, []byte("href"), 1) diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go index 29b1161e4..e48d26932 100644 --- a/hugolib/handler_test.go +++ b/hugolib/handler_test.go @@ -25,8 +25,8 @@ import ( ) func TestDefaultHandler(t *testing.T) { - viper.Reset() - defer viper.Reset() + setUp() + defer tearDown() hugofs.InitMemFs() sources := []source.ByteSource{ @@ -63,14 +63,14 @@ func TestDefaultHandler(t *testing.T) { doc string expected string }{ - {filepath.FromSlash("sect/doc1.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc1.html"), "\n\nsome content
\n"}, {filepath.FromSlash("sect/doc2.html"), "more content"}, - {filepath.FromSlash("sect/doc3.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc3.html"), "\n\nsome content
\n"}, {filepath.FromSlash("sect/doc3/img1.png"), string([]byte("‰PNG ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚"))}, {filepath.FromSlash("sect/img2.gif"), string([]byte("GIF89a��€��ÿÿÿ���,�������D�;"))}, {filepath.FromSlash("sect/img2.spf"), string([]byte("****FAKE-FILETYPE****"))}, {filepath.FromSlash("doc7.html"), "doc7 content"}, - {filepath.FromSlash("sect/doc8.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc8.html"), "\n\nsome content
\n"}, } for _, test := range tests { diff --git a/hugolib/hugolib_test.go b/hugolib/hugolib_test.go new file mode 100644 index 000000000..8f17553df --- /dev/null +++ b/hugolib/hugolib_test.go @@ -0,0 +1,34 @@ +// 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 ( + "github.com/spf13/viper" +) + +var stableNodeIDProvider nodeIDProviderFunc = func(n *Node) int { + return 42 +} + +// common test setup. +func setUp() { + viper.Reset() + nodeIDProvider = stableNodeIDProvider +} + +// common test cleanup. +func tearDown() { + viper.Reset() + nodeIDProvider = defaultNodeIDProvider +} diff --git a/hugolib/node.go b/hugolib/node.go index 7e9ad7458..dde1bd690 100644 --- a/hugolib/node.go +++ b/hugolib/node.go @@ -47,6 +47,15 @@ type Node struct { // but that would lead to massive changes; do it simple for now. var nodeIDCounter uint64 +type nodeIDProviderFunc func(n *Node) int + +var defaultNodeIDProvider nodeIDProviderFunc = func(n *Node) int { + n.idInit.Do(func() { n.id = nextNodeID() }) + return n.id +} + +var nodeIDProvider nodeIDProviderFunc = defaultNodeIDProvider + func nextNodeID() int { return int(atomic.AddUint64(&nodeIDCounter, 1)) } @@ -55,8 +64,7 @@ func nextNodeID() int { // This is unique for a given Hugo build, but must not be considered stable. // See UniqueID on Page for an identify that is stable for repeated builds. func (n *Node) ID() int { - n.idInit.Do(func() { n.id = nextNodeID() }) - return n.id + return nodeIDProvider(n) } func (n *Node) Now() time.Time { diff --git a/hugolib/node_test.go b/hugolib/node_test.go index 5b83cc0ad..a0d08ed07 100644 --- a/hugolib/node_test.go +++ b/hugolib/node_test.go @@ -43,14 +43,6 @@ func TestNodeSimpleMethods(t *testing.T) { } func TestNodeID(t *testing.T) { - t.Parallel() - - n1 := &Node{} - n2 := &Node{} - - assert.True(t, n1.ID() > 0) - assert.Equal(t, n1.ID(), n1.ID()) - assert.True(t, n2.ID() > n1.ID()) var wg sync.WaitGroup @@ -58,8 +50,13 @@ func TestNodeID(t *testing.T) { wg.Add(1) go func(j int) { for k := 0; k < 10; k++ { - n := &Node{} - assert.True(t, n.ID() > 0) + n1 := &Node{} + n2 := &Node{} + + assert.True(t, n1.ID() > 0) + assert.Equal(t, n1.ID(), n1.ID()) + assert.True(t, n2.ID() > n1.ID()) + } wg.Done() }(i) diff --git a/hugolib/page.go b/hugolib/page.go index cff84737b..9a9deecc1 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -265,7 +265,7 @@ func (p *Page) renderBytes(content []byte) []byte { } return helpers.RenderBytes( &helpers.RenderingContext{Content: content, PageFmt: p.determineMarkupType(), - DocumentID: p.UniqueID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn}) + DocumentID: p.ID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn}) } func (p *Page) renderContent(content []byte) []byte { @@ -280,7 +280,7 @@ func (p *Page) renderContent(content []byte) []byte { } } return helpers.RenderBytesWithTOC(&helpers.RenderingContext{Content: content, PageFmt: p.determineMarkupType(), - DocumentID: p.UniqueID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn}) + DocumentID: p.ID(), Config: p.getRenderingConfig(), LinkResolver: fn, FileResolver: fileFn}) } func (p *Page) getRenderingConfig() *helpers.Blackfriday { diff --git a/hugolib/page_test.go b/hugolib/page_test.go index b492bab2d..6e636261d 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -605,14 +605,17 @@ func TestPageWithAdditionalExtension(t *testing.T) { } func TestTableOfContents(t *testing.T) { + setUp() + defer tearDown() + p, _ := NewPage("tocpage.md") _, err := p.ReadFrom(strings.NewReader(pageWithToC)) p.Convert() if err != nil { t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) } - checkPageContent(t, p, "\n\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, "\n\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, "") } func TestPageWithMoreTag(t *testing.T) { diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index 876e9293f..dfe26d4d7 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -235,7 +235,7 @@ func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page, t tpl.Tem if sc.doMarkup { newInner := helpers.RenderBytes(&helpers.RenderingContext{ Content: []byte(inner), PageFmt: p.determineMarkupType(), - DocumentID: p.UniqueID(), Config: p.getRenderingConfig()}) + DocumentID: p.ID(), 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/shortcode_test.go b/hugolib/shortcode_test.go index ab764b845..68c7f9220 100644 --- a/hugolib/shortcode_test.go +++ b/hugolib/shortcode_test.go @@ -168,6 +168,9 @@ func TestInnerSC(t *testing.T) { } func TestInnerSCWithMarkdown(t *testing.T) { + setUp() + defer tearDown() + tem := tpl.New() tem.AddInternalShortcode("inside.html", `link and text
\nlink and text
\nlink and text
\nlink and text
\ntext
\n\nmore text
\n"}, + {pageWithMd, templateContent, "\n\ntext
\n\nmore text
\n"}, {simplePageRFC3339Date, templateDate, "2013-05-17 16:59:30 +0000 UTC"}, } @@ -556,8 +559,8 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) { } func TestSkipRender(t *testing.T) { - viper.Reset() - defer viper.Reset() + setUp() + defer tearDown() hugofs.InitMemFs() sources := []source.ByteSource{ @@ -593,14 +596,14 @@ func TestSkipRender(t *testing.T) { doc string expected string }{ - {filepath.FromSlash("sect/doc1.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc1.html"), "\n\nsome content
\n"}, {filepath.FromSlash("sect/doc2.html"), "more content"}, - {filepath.FromSlash("sect/doc3.html"), "\n\nsome content
\n"}, - {filepath.FromSlash("sect/doc4.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc3.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc4.html"), "\n\nsome content
\n"}, {filepath.FromSlash("sect/doc5.html"), "body5"}, {filepath.FromSlash("sect/doc6.html"), "body5"}, {filepath.FromSlash("doc7.html"), "doc7 content"}, - {filepath.FromSlash("sect/doc8.html"), "\n\nsome content
\n"}, + {filepath.FromSlash("sect/doc8.html"), "\n\nsome content
\n"}, } for _, test := range tests {