diff --git a/block.go b/block.go index 7ff1142..c992d43 100644 --- a/block.go +++ b/block.go @@ -186,6 +186,24 @@ func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int { for end = i; data[end] != '\n'; end++ { } skip := end + id := "" + if p.flags&EXTENSION_HEADER_IDS != 0 { + j, k := 0, 0 + // find start/end of header id + for j = i; j < end - 1 && (data[j] != '{' || data[j+1] != '#'); j++ { + } + for k = j + 1; k < end && data[k] != '}'; k++ { + } + // extract header id iff found + if j < end && k < end { + id = string(data[j+2:k]) + end = j + skip = k + 1 + for end > 0 && data[end-1] == ' ' { + end-- + } + } + } for end > 0 && data[end-1] == '#' { end-- } @@ -197,7 +215,7 @@ func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int { p.inline(out, data[i:end]) return true } - p.r.Header(out, work, level) + p.r.Header(out, work, level, id) } return skip } @@ -1218,7 +1236,7 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { return true } }(out, p, data[prev:eol]) - p.r.Header(out, work, level) + p.r.Header(out, work, level, "") // find the end of the underline for data[i] != '\n' { diff --git a/block_test.go b/block_test.go index 9321a41..6692ce1 100644 --- a/block_test.go +++ b/block_test.go @@ -177,6 +177,66 @@ func TestPrefixHeaderSpaceExtension(t *testing.T) { doTestsBlock(t, tests, EXTENSION_SPACE_HEADERS) } +func TestPrefixHeaderIdExtension(t *testing.T) { + var tests = []string{ + "# Header 1 {#someid}\n", + "

Header 1

\n", + + "# Header 1 {#someid} \n", + "

Header 1

\n", + + "# Header 1 {#someid}\n", + "

Header 1

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

Header 1 {#someid

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

Header 1 {#someid

\n", + + "# Header 1 {#someid}}\n", + "

Header 1

\n\n

}

\n", + + "## Header 2 {#someid}\n", + "

Header 2

\n", + + "### Header 3 {#someid}\n", + "

Header 3

\n", + + "#### Header 4 {#someid}\n", + "

Header 4

\n", + + "##### Header 5 {#someid}\n", + "
Header 5
\n", + + "###### Header 6 {#someid}\n", + "
Header 6
\n", + + "####### Header 7 {#someid}\n", + "
# Header 7
\n", + + "# Header 1 # {#someid}\n", + "

Header 1

\n", + + "## Header 2 ## {#someid}\n", + "

Header 2

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

Hello

\n\n

Header 1

\n\n

Goodbye

\n", + + "* List\n# Header {#someid}\n* List\n", + "\n", + + "* List\n#Header {#someid}\n* List\n", + "\n", + + "* List\n * Nested list\n # Nested header {#someid}\n", + "\n", + } + doTestsBlock(t, tests, EXTENSION_HEADER_IDS) +} + func TestUnderlineHeaders(t *testing.T) { var tests = []string{ "Header 1\n========\n", diff --git a/html.go b/html.go index 2b2959f..c93a9ec 100644 --- a/html.go +++ b/html.go @@ -181,11 +181,13 @@ func (options *Html) GetFlags() int { return options.flags } -func (options *Html) Header(out *bytes.Buffer, text func() bool, level int) { +func (options *Html) Header(out *bytes.Buffer, text func() bool, level int, id string) { marker := out.Len() doubleSpace(out) - if options.flags&HTML_TOC != 0 { + if id != "" { + out.WriteString(fmt.Sprintf("", level, id)) + } else if options.flags&HTML_TOC != 0 { // headerCount is incremented in htmlTocHeader out.WriteString(fmt.Sprintf("", level, options.headerCount)) } else { diff --git a/latex.go b/latex.go index da17234..8924aca 100644 --- a/latex.go +++ b/latex.go @@ -68,7 +68,7 @@ func (options *Latex) BlockHtml(out *bytes.Buffer, text []byte) { out.WriteString("\n\\end{verbatim}\n") } -func (options *Latex) Header(out *bytes.Buffer, text func() bool, level int) { +func (options *Latex) Header(out *bytes.Buffer, text func() bool, level int, id string) { marker := out.Len() switch level { diff --git a/markdown.go b/markdown.go index 606b53b..3d211e1 100644 --- a/markdown.go +++ b/markdown.go @@ -39,6 +39,7 @@ const ( EXTENSION_TAB_SIZE_EIGHT // expand tabs to eight spaces instead of four EXTENSION_FOOTNOTES // Pandoc-style footnotes 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} ) // These are the possible flag values for the link renderer. @@ -133,7 +134,7 @@ type Renderer interface { BlockCode(out *bytes.Buffer, text []byte, lang string) BlockQuote(out *bytes.Buffer, text []byte) BlockHtml(out *bytes.Buffer, text []byte) - Header(out *bytes.Buffer, text func() bool, level int) + Header(out *bytes.Buffer, text func() bool, level int, id string) HRule(out *bytes.Buffer) List(out *bytes.Buffer, text func() bool, flags int) ListItem(out *bytes.Buffer, text []byte, flags int) @@ -226,6 +227,8 @@ func MarkdownBasic(input []byte) []byte { // * Strikethrough support // // * Strict header parsing +// +// * Custom Header IDs func MarkdownCommon(input []byte) []byte { // set up the HTML renderer htmlFlags := 0 @@ -244,6 +247,7 @@ func MarkdownCommon(input []byte) []byte { extensions |= EXTENSION_AUTOLINK extensions |= EXTENSION_STRIKETHROUGH extensions |= EXTENSION_SPACE_HEADERS + extensions |= EXTENSION_HEADER_IDS return Markdown(input, renderer, extensions) }