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\nHeader 1
\n\nGoodbye
\n",
+
+ "* List\n# Header {#someid}\n* List\n",
+ "\nList
\n\nHeader
\n\nList
\n
\n",
+
+ "* List\n#Header {#someid}\n* List\n",
+ "\nList
\n\nHeader
\n\nList
\n
\n",
+
+ "* List\n * Nested list\n # Nested header {#someid}\n",
+ "\nList
\n\n\nNested list
\n\n" +
+ "Nested header
\n
\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)
}