mirror of
https://github.com/danog/blackfriday.git
synced 2024-11-30 04:29:13 +01:00
Merge pull request #294 from Ambrevar/v2TOC2Renderer
v2: Move TOC generation to the HTML Renderer
This commit is contained in:
commit
c9f76b530b
@ -1530,6 +1530,71 @@ func TestTOC(t *testing.T) {
|
||||
<h2 id="toc_1">Subtitle</h2>
|
||||
|
||||
<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
|
||||
|
59
html.go
59
html.go
@ -745,6 +745,59 @@ func (r *HTMLRenderer) writeDocumentHeader(w *bytes.Buffer, sr *SPRenderer) {
|
||||
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 @@ func (r *HTMLRenderer) Render(ast *Node) []byte {
|
||||
})
|
||||
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)
|
||||
})
|
||||
|
64
markdown.go
64
markdown.go
@ -415,73 +415,9 @@ func Parse(input []byte, opts Options) *Node {
|
||||
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() {
|
||||
if p.flags&Footnotes == 0 || len(p.notes) == 0 {
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user