diff --git a/main.go b/main.go index d6b3c05..fadd146 100644 --- a/main.go +++ b/main.go @@ -34,9 +34,10 @@ import ( func Markdown(text []byte) []byte { const htmlFlags = 0 renderer := &renderer{Html: blackfriday.HtmlRenderer(htmlFlags, "", "").(*blackfriday.Html)} + renderer.headerIDs = make(map[string]int) + unsanitized := blackfriday.Markdown(text, renderer, extensions) - sanitized := policy.SanitizeBytes(unsanitized) - return sanitized + return policy.SanitizeBytes(unsanitized) } // Heading returns a heading HTML node with title text. @@ -91,10 +92,11 @@ var policy = func() *bluemonday.Policy { type renderer struct { *blackfriday.Html + headerIDs map[string]int } // GitHub Flavored Markdown heading with clickable and hidden anchor. -func (*renderer) Header(out *bytes.Buffer, text func() bool, level int, _ string) { +func (r *renderer) Header(out *bytes.Buffer, text func() bool, level int, _ string) { marker := out.Len() doubleSpace(out) @@ -114,9 +116,11 @@ func (*renderer) Header(out *bytes.Buffer, text func() bool, level int, _ string // Failed to parse HTML (probably can never happen), so just use the whole thing. textContent = html.UnescapeString(textHTML) } - anchorName := sanitized_anchor_name.Create(textContent) - out.WriteString(fmt.Sprintf(``, level, anchorName, anchorName)) + id := sanitized_anchor_name.Create(textContent) + id = r.ensureUniqueHeaderID(id) + + out.WriteString(fmt.Sprintf(``, level, id, id)) out.WriteString(textHTML) out.WriteString(fmt.Sprintf("\n", level)) } @@ -330,3 +334,22 @@ func attrEscape(out *bytes.Buffer, src []byte) { out.Write(src[org:]) } } + +func (r *renderer) ensureUniqueHeaderID(id string) string { + for idx, found := r.headerIDs[id]; found; idx, found = r.headerIDs[id] { + tmp := fmt.Sprintf("%s-%d", id, idx+1) + + if _, tmpFound := r.headerIDs[tmp]; !tmpFound { + r.headerIDs[id] = idx + 1 + id = tmp + } else { + id = id + "-1" + } + } + + if _, found := r.headerIDs[id]; !found { + r.headerIDs[id] = 0 + } + + return id +} diff --git a/main_test.go b/main_test.go index a19f2fc..de0c757 100644 --- a/main_test.go +++ b/main_test.go @@ -1,4 +1,4 @@ -package github_flavored_markdown_test +package github_flavored_markdown import ( "io" @@ -6,7 +6,6 @@ import ( "os" "testing" - "github.com/shurcooL/github_flavored_markdown" "github.com/shurcooL/github_flavored_markdown/gfmstyle" "golang.org/x/net/html" "golang.org/x/net/html/atom" @@ -15,7 +14,7 @@ import ( func ExampleMarkdown() { text := []byte("Hello world github/linguist#1 **cool**, and #1!") - os.Stdout.Write(github_flavored_markdown.Markdown(text)) + os.Stdout.Write(Markdown(text)) // Output: //

Hello world github/linguist#1 cool, and #1!

@@ -30,11 +29,11 @@ func ExampleMarkdown_completeHTMLPage() { markdown := []byte("# GitHub Flavored Markdown\n\nHello.") io.WriteString(w, `
`) - w.Write(github_flavored_markdown.Markdown(markdown)) + w.Write(Markdown(markdown)) io.WriteString(w, `
`) // Output: - //

GitHub Flavored Markdown

+ //

GitHub Flavored Markdown

// //

Hello.

//
@@ -48,12 +47,12 @@ func TestComponents(t *testing.T) { { // Heading. text: "## git diff", - want: `

git diff

` + "\n", + want: `

git diff

` + "\n", }, { // Heading Link. text: "### [Some **bold** _italic_ link](http://www.example.com)", - want: `

Some bold italic link

` + "\n", + want: `

Some bold italic link

` + "\n", }, { // Task List. @@ -85,14 +84,14 @@ func TestComponents(t *testing.T) { } for _, test := range tests { - if got := string(github_flavored_markdown.Markdown([]byte(test.text))); got != test.want { + if got := string(Markdown([]byte(test.text))); got != test.want { t.Errorf("\ngot %q\nwant %q", got, test.want) } } } func ExampleHeading() { - heading := github_flavored_markdown.Heading(atom.H2, "Hello > Goodbye") + heading := Heading(atom.H2, "Hello > Goodbye") html.Render(os.Stdout, heading) // Output: diff --git a/sanitize_test.go b/sanitize_test.go index a9a3609..6a6c4c2 100644 --- a/sanitize_test.go +++ b/sanitize_test.go @@ -48,6 +48,7 @@ index dc83bf7..5260a7d 100644 htmlFlags := 0 renderer := &renderer{Html: blackfriday.HtmlRenderer(htmlFlags, "", "").(*blackfriday.Html)} + renderer.headerIDs = make(map[string]int) unsanitized := blackfriday.Markdown(text, renderer, extensions) @@ -113,11 +114,11 @@ func TestSanitizeAnchorName(t *testing.T) { }{ { text: "## Did you just steal this template from Tom's TOML?", - want: `

Did you just steal this template from Tom's TOML?

` + "\n", + want: `

Did you just steal this template from Tom's TOML?

` + "\n", }, { text: `## What about "quotes" & things?`, - want: `

What about "quotes" & things?

` + "\n", + want: `

What about "quotes" & things?

` + "\n", }, }