diff --git a/block.go b/block.go index d21a0e6..b7d870c 100644 --- a/block.go +++ b/block.go @@ -13,7 +13,10 @@ package blackfriday -import "bytes" +import ( + "bytes" + "unicode" +) // Parse block-level data. // Note: this function and many that it calls assume that @@ -223,6 +226,9 @@ func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int { end-- } if end > i { + if id == "" && p.flags&EXTENSION_AUTO_HEADER_IDS != 0 { + id = createSanitizedAnchorName(string(data[i:end])) + } work := func() bool { p.inline(out, data[i:end]) return true @@ -1267,7 +1273,13 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { return true } }(out, p, data[prev:eol]) - p.r.Header(out, work, level, "") + + id := "" + if p.flags&EXTENSION_AUTO_HEADER_IDS != 0 { + id = createSanitizedAnchorName(string(data[prev:eol])) + } + + p.r.Header(out, work, level, id) // find the end of the underline for data[i] != '\n' { @@ -1313,3 +1325,16 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { p.renderParagraph(out, data[:i]) return i } + +func createSanitizedAnchorName(text string) string { + var anchorName []rune + for _, r := range []rune(text) { + switch { + case r == ' ': + anchorName = append(anchorName, '-') + case unicode.IsLetter(r) || unicode.IsNumber(r): + anchorName = append(anchorName, unicode.ToLower(r)) + } + } + return string(anchorName) +} diff --git a/block_test.go b/block_test.go index 7f8e18f..de77aa9 100644 --- a/block_test.go +++ b/block_test.go @@ -237,6 +237,48 @@ func TestPrefixHeaderIdExtension(t *testing.T) { doTestsBlock(t, tests, EXTENSION_HEADER_IDS) } +func TestPrefixAutoHeaderIdExtension(t *testing.T) { + var tests = []string{ + "# Header 1\n", + "

Header 1

\n", + + "# Header 1 \n", + "

Header 1

\n", + + "## Header 2\n", + "

Header 2

\n", + + "### Header 3\n", + "

Header 3

\n", + + "#### Header 4\n", + "

Header 4

\n", + + "##### Header 5\n", + "
Header 5
\n", + + "###### Header 6\n", + "
Header 6
\n", + + "####### Header 7\n", + "
# Header 7
\n", + + "Hello\n# Header 1\nGoodbye\n", + "

Hello

\n\n

Header 1

\n\n

Goodbye

\n", + + "* List\n# Header\n* List\n", + "\n", + + "* List\n#Header\n* List\n", + "\n", + + "* List\n * Nested list\n # Nested header\n", + "\n", + } + doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS) +} + func TestUnderlineHeaders(t *testing.T) { var tests = []string{ "Header 1\n========\n", @@ -287,6 +329,50 @@ func TestUnderlineHeaders(t *testing.T) { doTestsBlock(t, tests, 0) } +func TestUnderlineHeadersAutoIDs(t *testing.T) { + var tests = []string{ + "Header 1\n========\n", + "

Header 1

\n", + + "Header 2\n--------\n", + "

Header 2

\n", + + "A\n=\n", + "

A

\n", + + "B\n-\n", + "

B

\n", + + "Paragraph\nHeader\n=\n", + "

Paragraph

\n\n

Header

\n", + + "Header\n===\nParagraph\n", + "

Header

\n\n

Paragraph

\n", + + "Header\n===\nAnother header\n---\n", + "

Header

\n\n

Another header

\n", + + " Header\n======\n", + "

Header

\n", + + "Header with *inline*\n=====\n", + "

Header with inline

\n", + + "Paragraph\n\n\n\n\nHeader\n===\n", + "

Paragraph

\n\n

Header

\n", + + "Trailing space \n==== \n\n", + "

Trailing space

\n", + + "Trailing spaces\n==== \n\n", + "

Trailing spaces

\n", + + "Double underline\n=====\n=====\n", + "

Double underline

\n\n

=====

\n", + } + doTestsBlock(t, tests, EXTENSION_AUTO_HEADER_IDS) +} + func TestHorizontalRule(t *testing.T) { var tests = []string{ "-\n", diff --git a/markdown.go b/markdown.go index 0d13459..d07e37c 100644 --- a/markdown.go +++ b/markdown.go @@ -41,6 +41,7 @@ const ( EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK // No need to insert an empty line to start a (code, quote, order list, unorder list)block EXTENSION_HEADER_IDS // specify header IDs with {#id} EXTENSION_TITLEBLOCK // Titleblock ala pandoc + EXTENSION_AUTO_HEADER_IDS // Create the header ID from the text commonHtmlFlags = 0 | HTML_USE_XHTML |