feat: added automatic document navigation generation.

This commit is contained in:
Andrey Parhomenko 2024-03-03 04:25:25 +05:00
parent 3776c1db93
commit 243bd2b9e9
3 changed files with 132 additions and 6 deletions

View file

@ -3,6 +3,7 @@ package server
import ( import (
"github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/ast"
"github.com/gomarkdown/markdown/parser" "github.com/gomarkdown/markdown/parser"
"vultras.su/core/bond" "vultras.su/core/bond"
"vultras.su/core/bond/contents" "vultras.su/core/bond/contents"
@ -11,7 +12,7 @@ import (
"path" "path"
"os" "os"
"fmt" "fmt"
//"strings" "strings"
"bytes" "bytes"
) )
@ -32,6 +33,7 @@ type ServerOptions struct {
WikiExt string WikiExt string
WebPath string WebPath string
AddFileNavigation bool AddFileNavigation bool
AddDocNavigation bool
} }
type Server struct { type Server struct {
@ -88,6 +90,113 @@ func (srv *Server) pageFooter() string {
return htmlFooter return htmlFooter
} }
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 {
fmt.Println("fooooooooooooooouuuuuuuuund", len(hs), hs)
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 {
fmt.Println("thefound: ", first, h)
lasti = i+1
found = true
break
}
}
if !found {
fmt.Println("in not found")
return HeadingTrees{
&HeadingTree{
Heading: first,
Children: makeHeadingTrees(rest),
},
}
}
fmt.Println("through", lasti)
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()
}
func (srv *Server) ProcessToHtml(urlPath, filePath string, bts []byte) ([]byte, error) { func (srv *Server) ProcessToHtml(urlPath, filePath string, bts []byte) ([]byte, error) {
var b bytes.Buffer var b bytes.Buffer
doc := srv.makeMdParser().Parse(bts) doc := srv.makeMdParser().Parse(bts)
@ -133,14 +242,21 @@ func (srv *Server) ProcessToHtml(urlPath, filePath string, bts []byte) ([]byte,
fmt.Fprint(&b, "</nav></header>") fmt.Fprint(&b, "</nav></header>")
} }
fmt.Fprint(&b, "<main>", string(main_section), "</main>") fmt.Fprint(&b, "<div class=\"content\">")
if srv.options.AddDocNavigation {
headings := getDocumentHeadings(doc)
trees := makeHeadingTrees(headings)
docNav := RenderHeadingTrees(trees, true)
fmt.Fprint(&b, docNav)
}
fmt.Fprint(&b, "<main>")
fmt.Fprintf(&b, string(main_section))
fmt.Fprintf(&b, "</main>")
fmt.Fprintf(&b, "</div>")
fmt.Fprint(&b, srv.pageFooter()) fmt.Fprint(&b, srv.pageFooter())
return b.Bytes(), nil return b.Bytes(), nil
} }
var makeRootHandler = func(opts ServerOptions) bond.Handler { var makeRootHandler = func(opts ServerOptions) bond.Handler {
return bond.Root(bond.Path(). return bond.Root(bond.Path().
Case( Case(

View file

@ -16,7 +16,8 @@ var Tool = mtool.T("rwiki").Func(func(flags *mtool.Flags){
flags.StringVar(&opts.WikiPath, "wiki", "wiki", "path to wiki files") flags.StringVar(&opts.WikiPath, "wiki", "wiki", "path to wiki files")
flags.StringVar(&opts.WebPath, "web", "web", "path to static web files") flags.StringVar(&opts.WebPath, "web", "web", "path to static web files")
flags.StringVar(&opts.WikiExt, "ext", ".pmd", "wiki file exitension") flags.StringVar(&opts.WikiExt, "ext", ".pmd", "wiki file exitension")
flags.BoolVar(&opts.AddFileNavigation, "nav", true, "generate navigation") flags.BoolVar(&opts.AddFileNavigation, "filenav", true, "generate file navigation")
flags.BoolVar(&opts.AddDocNavigation, "docnav", true, "generate document navigation")
flags.Parse() flags.Parse()
srv := bond.Server{ srv := bond.Server{

View file

@ -6,7 +6,16 @@ teahuste| ehtua | shit
--------|-------------|-------------- --------|-------------|--------------
cock| dick|die cock| dick|die
* [X] check
```go ```go
func main() { func main() {
} }
``` ```
## Second header shit
## another dick
### third level header