shithub: hugo

Download patch

ref: cd558958a0c0ecd06f7560a38e27334fe983e0de
parent: 5ef52294f90c51697bd3f918b3c3ed83baff657a
author: Bjørn Erik Pedersen <[email protected]>
date: Mon Apr 11 09:17:25 EDT 2016

Use Node.ID for anchor ID

Fixes #2057

--- a/helpers/content.go
+++ b/helpers/content.go
@@ -23,6 +23,10 @@
 	"os/exec"
 	"unicode/utf8"
 
+	"fmt"
+	"strings"
+	"sync"
+
 	"github.com/miekg/mmark"
 	"github.com/mitchellh/mapstructure"
 	"github.com/russross/blackfriday"
@@ -30,9 +34,6 @@
 	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 @@
 		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 @@
 		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 @@
 type RenderingContext struct {
 	Content      []byte
 	PageFmt      string
-	DocumentID   string
+	DocumentID   int
 	Config       *Blackfriday
 	FileResolver FileResolverFunc
 	LinkResolver LinkResolverFunc
--- a/helpers/content_test.go
+++ b/helpers/content_test.go
@@ -172,7 +172,7 @@
 
 func TestGetHTMLRendererAnchors(t *testing.T) {
 	ctx := &RenderingContext{}
-	ctx.DocumentID = "testid"
+	ctx.DocumentID = 123
 	ctx.Config = ctx.getConfig()
 	ctx.Config.PlainIDAnchors = false
 
@@ -179,8 +179,8 @@
 	actualRenderer := getHTMLRenderer(0, ctx)
 	headerBuffer := &bytes.Buffer{}
 	footnoteBuffer := &bytes.Buffer{}
-	expectedFootnoteHref := []byte("href=\"#fn:testid:href\"")
-	expectedHeaderID := []byte("<h1 id=\"id:testid\"></h1>\n")
+	expectedFootnoteHref := []byte("href=\"#fn:123:href\"")
+	expectedHeaderID := []byte("<h1 id=\"id:123\"></h1>\n")
 
 	actualRenderer.Header(headerBuffer, func() bool { return true }, 1, "id")
 	actualRenderer.FootnoteRef(footnoteBuffer, []byte("href"), 1)
@@ -196,7 +196,7 @@
 
 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)
@@ -203,7 +203,7 @@
 
 	headerBuffer := &bytes.Buffer{}
 	footnoteBuffer := &bytes.Buffer{}
-	expectedFootnoteHref := []byte("href=\"#fn:testid:href\"")
+	expectedFootnoteHref := []byte("href=\"#fn:321:href\"")
 	expectedHeaderID := []byte("<h1 id=\"id\"></h1>")
 
 	actualRenderer.FootnoteRef(footnoteBuffer, []byte("href"), 1)
--- a/hugolib/handler_test.go
+++ b/hugolib/handler_test.go
@@ -25,8 +25,8 @@
 )
 
 func TestDefaultHandler(t *testing.T) {
-	viper.Reset()
-	defer viper.Reset()
+	setUp()
+	defer tearDown()
 
 	hugofs.InitMemFs()
 	sources := []source.ByteSource{
@@ -63,14 +63,14 @@
 		doc      string
 		expected string
 	}{
-		{filepath.FromSlash("sect/doc1.html"), "\n\n<h1 id=\"title:5d74edbb89ef198cd37882b687940cda\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+		{filepath.FromSlash("sect/doc1.html"), "\n\n<h1 id=\"title:42\">title</h1>\n\n<p>some <em>content</em></p>\n"},
 		{filepath.FromSlash("sect/doc2.html"), "<!doctype html><html><body>more content</body></html>"},
-		{filepath.FromSlash("sect/doc3.html"), "\n\n<h1 id=\"doc3:28c75a9e2162b8eccda73a1ab9ce80b4\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
+		{filepath.FromSlash("sect/doc3.html"), "\n\n<h1 id=\"doc3:42\">doc3</h1>\n\n<p><em>some</em> content</p>\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"), "<html><body>doc7 content</body></html>"},
-		{filepath.FromSlash("sect/doc8.html"), "\n\n<h1 id=\"title:0ae308ad73e2f37bd09874105281b5d8\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+		{filepath.FromSlash("sect/doc8.html"), "\n\n<h1 id=\"title:42\">title</h1>\n\n<p>some <em>content</em></p>\n"},
 	}
 
 	for _, test := range tests {
--- /dev/null
+++ b/hugolib/hugolib_test.go
@@ -1,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
+}
--- a/hugolib/node.go
+++ b/hugolib/node.go
@@ -47,6 +47,15 @@
 // 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 @@
 // 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 {
--- a/hugolib/node_test.go
+++ b/hugolib/node_test.go
@@ -43,15 +43,7 @@
 }
 
 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
 
 	for i := 1; i <= 10; i++ {
@@ -58,8 +50,13 @@
 		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)
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -265,7 +265,7 @@
 	}
 	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 @@
 		}
 	}
 	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 {
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -605,6 +605,9 @@
 }
 
 func TestTableOfContents(t *testing.T) {
+	setUp()
+	defer tearDown()
+
 	p, _ := NewPage("tocpage.md")
 	_, err := p.ReadFrom(strings.NewReader(pageWithToC))
 	p.Convert()
@@ -611,8 +614,8 @@
 	if err != nil {
 		t.Fatalf("Unable to create a page with frontmatter and body content: %s", err)
 	}
-	checkPageContent(t, p, "\n\n<p>For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.</p>\n\n<h2 id=\"aa:90b9174a5bdb091a9625b04adac96ca6\">AA</h2>\n\n<p>I 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.</p>\n\n<h3 id=\"aaa:90b9174a5bdb091a9625b04adac96ca6\">AAA</h3>\n\n<p>I 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&ndash;as swift as the passage of light&ndash;would leap after me from the pit about\nthe cylinder and strike me down. ## BB</p>\n\n<h3 id=\"bbb:90b9174a5bdb091a9625b04adac96ca6\">BBB</h3>\n\n<p>&ldquo;You&rsquo;re a great Granser,&rdquo; he cried delightedly, &ldquo;always making believe them little marks mean something.&rdquo;</p>\n")
-	checkPageTOC(t, p, "<nav id=\"TableOfContents\">\n<ul>\n<li>\n<ul>\n<li><a href=\"#aa:90b9174a5bdb091a9625b04adac96ca6\">AA</a>\n<ul>\n<li><a href=\"#aaa:90b9174a5bdb091a9625b04adac96ca6\">AAA</a></li>\n<li><a href=\"#bbb:90b9174a5bdb091a9625b04adac96ca6\">BBB</a></li>\n</ul></li>\n</ul></li>\n</ul>\n</nav>")
+	checkPageContent(t, p, "\n\n<p>For some moments the old man did not reply. He stood with bowed head, buried in deep thought. But at last he spoke.</p>\n\n<h2 id=\"aa:42\">AA</h2>\n\n<p>I 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.</p>\n\n<h3 id=\"aaa:42\">AAA</h3>\n\n<p>I 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&ndash;as swift as the passage of light&ndash;would leap after me from the pit about\nthe cylinder and strike me down. ## BB</p>\n\n<h3 id=\"bbb:42\">BBB</h3>\n\n<p>&ldquo;You&rsquo;re a great Granser,&rdquo; he cried delightedly, &ldquo;always making believe them little marks mean something.&rdquo;</p>\n")
+	checkPageTOC(t, p, "<nav id=\"TableOfContents\">\n<ul>\n<li>\n<ul>\n<li><a href=\"#aa:42\">AA</a>\n<ul>\n<li><a href=\"#aaa:42\">AAA</a></li>\n<li><a href=\"#bbb:42\">BBB</a></li>\n</ul></li>\n</ul></li>\n</ul>\n</nav>")
 }
 
 func TestPageWithMoreTag(t *testing.T) {
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -235,7 +235,7 @@
 		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
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -168,6 +168,9 @@
 }
 
 func TestInnerSCWithMarkdown(t *testing.T) {
+	setUp()
+	defer tearDown()
+
 	tem := tpl.New()
 	tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
 
@@ -176,10 +179,12 @@
 
 [link](http://spf13.com) and text
 
-{{% /inside %}}`, "<div><h1 id=\"more-here:bec3ed8ba720b9073ab75abcf3ba5d97\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>", tem)
+{{% /inside %}}`, "<div><h1 id=\"more-here:42\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>", tem)
 }
 
 func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {
+	setUp()
+	defer tearDown()
 	tem := tpl.New()
 	tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
 
@@ -198,7 +203,7 @@
 This is **plain** text.
 
 {{< /inside >}}
-`, "<div><h1 id=\"more-here:bec3ed8ba720b9073ab75abcf3ba5d97\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>\n\nAnd then:\n\n<div>\n# More Here\n\nThis is **plain** text.\n\n</div>\n", tem)
+`, "<div><h1 id=\"more-here:42\">More Here</h1>\n\n<p><a href=\"http://spf13.com\">link</a> and text</p>\n</div>\n\nAnd then:\n\n<div>\n# More Here\n\nThis is **plain** text.\n\n</div>\n", tem)
 }
 
 func TestEmbeddedSC(t *testing.T) {
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -142,6 +142,9 @@
 }
 
 func TestRenderThing(t *testing.T) {
+	setUp()
+	defer tearDown()
+
 	tests := []struct {
 		content  string
 		template string
@@ -149,7 +152,7 @@
 	}{
 		{pageSimpleTitle, templateTitle, "simple template"},
 		{pageSimpleTitle, templateFunc, "simple-template"},
-		{pageWithMd, templateContent, "\n\n<h1 id=\"heading-1:91b5c4a22fc6103c73bb91e4a40568f8\">heading 1</h1>\n\n<p>text</p>\n\n<h2 id=\"heading-2:91b5c4a22fc6103c73bb91e4a40568f8\">heading 2</h2>\n\n<p>more text</p>\n"},
+		{pageWithMd, templateContent, "\n\n<h1 id=\"heading-1:42\">heading 1</h1>\n\n<p>text</p>\n\n<h2 id=\"heading-2:42\">heading 2</h2>\n\n<p>more text</p>\n"},
 		{simplePageRFC3339Date, templateDate, "2013-05-17 16:59:30 &#43;0000 UTC"},
 	}
 
@@ -556,8 +559,8 @@
 
 }
 func TestSkipRender(t *testing.T) {
-	viper.Reset()
-	defer viper.Reset()
+	setUp()
+	defer tearDown()
 
 	hugofs.InitMemFs()
 	sources := []source.ByteSource{
@@ -593,14 +596,14 @@
 		doc      string
 		expected string
 	}{
-		{filepath.FromSlash("sect/doc1.html"), "\n\n<h1 id=\"title:5d74edbb89ef198cd37882b687940cda\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+		{filepath.FromSlash("sect/doc1.html"), "\n\n<h1 id=\"title:42\">title</h1>\n\n<p>some <em>content</em></p>\n"},
 		{filepath.FromSlash("sect/doc2.html"), "<!doctype html><html><body>more content</body></html>"},
-		{filepath.FromSlash("sect/doc3.html"), "\n\n<h1 id=\"doc3:28c75a9e2162b8eccda73a1ab9ce80b4\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
-		{filepath.FromSlash("sect/doc4.html"), "\n\n<h1 id=\"doc4:f8e6806123f341b8975509637645a4d3\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
+		{filepath.FromSlash("sect/doc3.html"), "\n\n<h1 id=\"doc3:42\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
+		{filepath.FromSlash("sect/doc4.html"), "\n\n<h1 id=\"doc4:42\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
 		{filepath.FromSlash("sect/doc5.html"), "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
 		{filepath.FromSlash("sect/doc6.html"), "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
 		{filepath.FromSlash("doc7.html"), "<html><body>doc7 content</body></html>"},
-		{filepath.FromSlash("sect/doc8.html"), "\n\n<h1 id=\"title:0ae308ad73e2f37bd09874105281b5d8\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+		{filepath.FromSlash("sect/doc8.html"), "\n\n<h1 id=\"title:42\">title</h1>\n\n<p>some <em>content</em></p>\n"},
 	}
 
 	for _, test := range tests {