111 lines
1.8 KiB
Go
111 lines
1.8 KiB
Go
|
package server
|
||
|
|
||
|
import (
|
||
|
"github.com/gomarkdown/markdown/ast"
|
||
|
"strings"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
type Heading struct {
|
||
|
Id string
|
||
|
Level int
|
||
|
Text string
|
||
|
}
|
||
|
|
||
|
func getDocumentHeadings(doc ast.Node) []Heading {
|
||
|
ret := []Heading{}
|
||
|
ast.WalkFunc(doc, func(node ast.Node, entering bool) ast.WalkStatus {
|
||
|
heading, ok := node.(*ast.Heading)
|
||
|
if !ok {
|
||
|
return ast.GoToNext
|
||
|
}
|
||
|
children := node.GetChildren()
|
||
|
if len(children) > 0 && !entering {
|
||
|
leaf := children[0].AsLeaf()
|
||
|
if leaf == nil {
|
||
|
return ast.GoToNext
|
||
|
}
|
||
|
ret = append(ret, Heading{
|
||
|
Id: heading.HeadingID,
|
||
|
Level: heading.Level,
|
||
|
Text: string(leaf.Literal),
|
||
|
})
|
||
|
return ast.SkipChildren
|
||
|
}
|
||
|
|
||
|
return ast.GoToNext
|
||
|
})
|
||
|
return ret
|
||
|
}
|
||
|
|
||
|
type HeadingTree struct {
|
||
|
Heading Heading
|
||
|
Children HeadingTrees
|
||
|
}
|
||
|
|
||
|
type HeadingTrees []*HeadingTree
|
||
|
|
||
|
func makeHeadingTrees(hs []Heading) HeadingTrees {
|
||
|
if len(hs) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if len(hs) == 1 {
|
||
|
return HeadingTrees{
|
||
|
&HeadingTree{
|
||
|
Heading: hs[0],
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
lasti int
|
||
|
found bool
|
||
|
)
|
||
|
|
||
|
first := hs[0]
|
||
|
rest := hs[1:]
|
||
|
for i, h := range rest {
|
||
|
if first.Level >= h.Level {
|
||
|
lasti = i+1
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !found {
|
||
|
return HeadingTrees{
|
||
|
&HeadingTree{
|
||
|
Heading: first,
|
||
|
Children: makeHeadingTrees(rest),
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return append(
|
||
|
makeHeadingTrees(hs[:lasti]),
|
||
|
makeHeadingTrees(hs[lasti:])... ,
|
||
|
)
|
||
|
}
|
||
|
|
||
|
func RenderHeadingTrees(trees HeadingTrees, first bool) string {
|
||
|
var b strings.Builder
|
||
|
fmt.Fprint(&b, "<nav")
|
||
|
if first {
|
||
|
fmt.Fprint(&b, " class=\"document\"")
|
||
|
}
|
||
|
fmt.Fprint(&b, ">")
|
||
|
|
||
|
for _, tree := range trees {
|
||
|
fmt.Fprintf(
|
||
|
&b, "<a href=\"#%s\">%s</a>",
|
||
|
tree.Heading.Id, tree.Heading.Text,
|
||
|
)
|
||
|
if len(tree.Children) > 0 {
|
||
|
fmt.Fprint(&b, RenderHeadingTrees(tree.Children, false))
|
||
|
}
|
||
|
}
|
||
|
fmt.Fprint(&b, "</nav>")
|
||
|
return b.String()
|
||
|
}
|