all repos — grayfriday @ c9f76b530b0b9f0c27cba7a8e67897b798d47b14

blackfriday fork with a few changes

Merge pull request #294 from Ambrevar/v2TOC2Renderer

v2: Move TOC generation to the HTML Renderer
Vytautas Ĺ altenis vytas@rtfb.lt
Tue, 09 Aug 2016 22:06:04 +0300
commit

c9f76b530b0b9f0c27cba7a8e67897b798d47b14

parent

0f3eafddfa07d57c2d0fd289a9d6520a0f766220

3 files changed, 124 insertions(+), 64 deletions(-)

jump to
M block_test.goblock_test.go

@@ -1532,6 +1532,71 @@

<h1 id="toc_2">Title2</h1> `, + "## Subtitle\n\n# Title", + `<nav> + +<ul> +<li> +<ul> +<li><a href="#toc_0">Subtitle</a></li> +</ul></li> + +<li><a href="#toc_1">Title</a></li> +</ul> + +</nav> + +<h2 id="toc_0">Subtitle</h2> + +<h1 id="toc_1">Title</h1> +`, + + "# Title 1\n\n## Subtitle 1\n\n### Subsubtitle 1\n\n# Title 2\n\n### Subsubtitle 2", + `<nav> + +<ul> +<li><a href="#toc_0">Title 1</a> +<ul> +<li><a href="#toc_1">Subtitle 1</a> +<ul> +<li><a href="#toc_2">Subsubtitle 1</a></li> +</ul></li> +</ul></li> + +<li><a href="#toc_3">Title 2</a> +<ul> +<li> +<ul> +<li><a href="#toc_4">Subsubtitle 2</a></li> +</ul></li> +</ul></li> +</ul> + +</nav> + +<h1 id="toc_0">Title 1</h1> + +<h2 id="toc_1">Subtitle 1</h2> + +<h3 id="toc_2">Subsubtitle 1</h3> + +<h1 id="toc_3">Title 2</h1> + +<h3 id="toc_4">Subsubtitle 2</h3> +`, + + "# Title with `code`", + `<nav> + +<ul> +<li><a href="#toc_0">Title with <code>code</code></a></li> +</ul> + +</nav> + +<h1 id="toc_0">Title with <code>code</code></h1> +`, + // Trigger empty TOC "#", "",
M html.gohtml.go

@@ -745,6 +745,59 @@ w.WriteString("</head>\n")

w.WriteString("<body>\n\n") } +func (r *HTMLRenderer) writeTOC(w *bytes.Buffer, ast *Node) { + buf := bytes.Buffer{} + + inHeader := false + tocLevel := 0 + headerCount := 0 + + ast.Walk(func(node *Node, entering bool) WalkStatus { + if node.Type == Header && !node.HeaderData.IsTitleblock { + inHeader = entering + if entering { + node.HeaderID = fmt.Sprintf("toc_%d", headerCount) + if node.Level == tocLevel { + buf.WriteString("</li>\n\n<li>") + } else if node.Level < tocLevel { + for node.Level < tocLevel { + tocLevel-- + buf.WriteString("</li>\n</ul>") + } + buf.WriteString("</li>\n\n<li>") + } else { + for node.Level > tocLevel { + tocLevel++ + buf.WriteString("\n<ul>\n<li>") + } + } + + fmt.Fprintf(&buf, `<a href="#toc_%d">`, headerCount) + headerCount++ + } else { + buf.WriteString("</a>") + } + return GoToNext + } + + if inHeader { + return r.RenderNode(&buf, node, entering) + } + + return GoToNext + }) + + for ; tocLevel > 0; tocLevel-- { + buf.WriteString("</li>\n</ul>") + } + + if buf.Len() > 0 { + w.WriteString("<nav>\n") + w.Write(buf.Bytes()) + w.WriteString("\n\n</nav>\n") + } +} + func (r *HTMLRenderer) writeDocumentFooter(w *bytes.Buffer) { if r.Flags&CompletePage == 0 { return

@@ -770,6 +823,12 @@ return GoToNext

}) var buff bytes.Buffer r.writeDocumentHeader(&buff, sr) + if r.Extensions&TOC != 0 || r.Extensions&OmitContents != 0 { + r.writeTOC(&buff, ast) + if r.Extensions&OmitContents != 0 { + return buff.Bytes() + } + } ast.Walk(func(node *Node, entering bool) WalkStatus { return r.RenderNode(&buff, node, entering) })
M markdown.gomarkdown.go

@@ -415,71 +415,7 @@ }

return GoToNext }) p.parseRefsToAST() - p.generateTOC() return p.doc -} - -func (p *parser) generateTOC() { - if p.flags&TOC == 0 && p.flags&OmitContents == 0 { - return - } - navNode := NewNode(HTMLBlock) - navNode.Literal = []byte("<nav>") - navNode.open = false - - var topList *Node - var listNode *Node - var lastItem *Node - headerCount := 0 - currentLevel := 0 - p.doc.Walk(func(node *Node, entering bool) WalkStatus { - if entering && node.Type == Header { - if node.Level > currentLevel { - currentLevel++ - newList := NewNode(List) - if lastItem != nil { - lastItem.appendChild(newList) - listNode = newList - } else { - listNode = newList - topList = listNode - } - } - if node.Level < currentLevel { - finalizeList(listNode) - lastItem = listNode.Parent - listNode = lastItem.Parent - } - node.HeaderID = fmt.Sprintf("toc_%d", headerCount) - headerCount++ - lastItem = NewNode(Item) - listNode.appendChild(lastItem) - anchorNode := NewNode(Link) - anchorNode.Destination = []byte("#" + node.HeaderID) - lastItem.appendChild(anchorNode) - anchorNode.appendChild(text(node.FirstChild.Literal)) - } - return GoToNext - }) - firstChild := p.doc.FirstChild - // Insert TOC only if there is anything to insert - if topList != nil { - finalizeList(topList) - firstChild.insertBefore(navNode) - firstChild.insertBefore(topList) - navCloseNode := NewNode(HTMLBlock) - navCloseNode.Literal = []byte("</nav>") - navCloseNode.open = false - firstChild.insertBefore(navCloseNode) - } - // Drop everything after the TOC if OmitContents was requested - if p.flags&OmitContents != 0 { - for firstChild != nil { - next := firstChild.Next - firstChild.unlink() - firstChild = next - } - } } func (p *parser) parseRefsToAST() {