From 726bab59b97d00b39e03f3b5b051b71b86177364 Mon Sep 17 00:00:00 2001 From: Dominik Menke Date: Wed, 13 Jul 2022 22:11:51 +0200 Subject: [PATCH] docs: render markdown documentation [wip] --- docs/docs.go | 128 +++++++++++++++++++++++++++++++++++++++++++++ docs/docs.yml | 14 +++++ go.mod | 6 ++- go.sum | 9 +++- service/service.go | 2 + 5 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 docs/docs.go create mode 100644 docs/docs.yml diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..b83996b --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,128 @@ +package docs + +import ( + "bytes" + "embed" + "fmt" + "io" + "net/http" + "strings" + + "github.com/Depado/bfchroma/v2" + "github.com/alecthomas/chroma/v2/formatters/html" + bf "github.com/russross/blackfriday/v2" + "gopkg.in/yaml.v3" +) + +//go:embed docs.yml *.md **/*.md +var sources embed.FS + +type page struct { + Title string + Breadcrumbs []string + Body string + File string + Route string + Children []*page +} + +var root = func() page { + structure, err := sources.Open("docs.yml") + if err != nil { + panic(err) + } + defer structure.Close() + + var menu page + dec := yaml.NewDecoder(structure) + dec.KnownFields(true) + if err := dec.Decode(&menu); err != nil { + panic(err) + } + + menu.init() + return menu +}() + +func (pg *page) init(crumbs ...string) { + if pg.File != "" { + if r := strings.TrimSuffix(pg.File, ".md"); r == "index" { + pg.Route = "" + } else { + pg.Route = "/" + r + } + + pg.parseFile() + } + if pg.Title != "" { + pg.Breadcrumbs = append(pg.Breadcrumbs, pg.Title) + } + for _, child := range pg.Children { + child.init(pg.Breadcrumbs...) + } +} + +func (pg *page) parseFile() { + body, err := sources.ReadFile(pg.File) + if err != nil { + panic(err) + } + + r := bfchroma.NewRenderer( + bfchroma.WithoutAutodetect(), + bfchroma.ChromaOptions( + html.WithLineNumbers(true), + ), + bfchroma.Extend(bf.NewHTMLRenderer(bf.HTMLRendererParameters{ + Flags: bf.CommonHTMLFlags & ^bf.UseXHTML & ^bf.CompletePage, + })), + ) + parser := bf.New( + bf.WithExtensions(bf.CommonExtensions), + bf.WithRenderer(r), + ) + + ast := parser.Parse(body) + var buf bytes.Buffer + var inH1 bool + + ast.Walk(func(node *bf.Node, entering bool) bf.WalkStatus { + switch node.Type { + case bf.Heading: + inH1 = entering && node.HeadingData.Level == 1 && pg.Title == "" + case bf.Text: + if inH1 { + pg.Title = string(node.Literal) + } + case bf.Link: + if entering && bytes.HasPrefix(node.LinkData.Destination, []byte("./")) { + node.LinkData.Destination = bytes.TrimSuffix(node.LinkData.Destination, []byte(".md")) + } + } + return r.RenderNode(&buf, node, entering) + }) + + pg.Body = buf.String() +} + +func (pg *page) Dump(w io.Writer) { + fmt.Fprintf(w, "- %s (%s)\n", pg.Title, pg.Route) + fmt.Fprintln(w, pg.Body) + fmt.Fprintln(w) + + for _, c := range pg.Children { + c.Dump(w) + } +} + +func Handler() http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + w.Header().Set("X-Content-Type-Options", "nosniff") + w.WriteHeader(http.StatusOK) + + fmt.Fprintf(w, "%#v\n\n", r.URL) + + root.Dump(w) + }) +} diff --git a/docs/docs.yml b/docs/docs.yml new file mode 100644 index 0000000..34ab7c8 --- /dev/null +++ b/docs/docs.yml @@ -0,0 +1,14 @@ +--- +file: index.md +children: + - file: operation-modes.md + - file: cli-options.md + - title: HTTP API + children: + - file: http-api/render.md + - file: http-api/status.md + - file: http-api/metrics.md + - file: http-api/web-ui.md + - file: reference-store.md + - file: history.md + - file: future.md diff --git a/go.mod b/go.mod index a52cd21..f52c3a2 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module github.com/digineo/texd go 1.18 require ( + github.com/Depado/bfchroma/v2 v2.0.0 + github.com/alecthomas/chroma/v2 v2.2.0 github.com/bahlo/generic-list-go v0.2.0 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d github.com/docker/docker v20.10.17+incompatible @@ -12,11 +14,13 @@ require ( github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/opencontainers/image-spec v1.0.2 github.com/prometheus/client_golang v1.12.2 + github.com/russross/blackfriday/v2 v2.1.0 github.com/spf13/afero v1.8.2 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.0 github.com/thediveo/enumflag v0.10.1 go.uber.org/zap v1.21.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -25,6 +29,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect @@ -54,7 +59,6 @@ require ( golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.1.0 // indirect ) diff --git a/go.sum b/go.sum index fad6146..40dcd76 100644 --- a/go.sum +++ b/go.sum @@ -40,9 +40,14 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOEl github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Depado/bfchroma/v2 v2.0.0 h1:IRpN9BPkNwEpR6w1ectIcNWOuhDSLx+8f1pn83fzxx8= +github.com/Depado/bfchroma/v2 v2.0.0/go.mod h1:wFwW/Pw8Tnd0irzgO9Zxtxgzp3aPS8qBWlyadxujxmw= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY= +github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs= +github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -60,7 +65,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= @@ -89,6 +93,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/digineo/afero v1.8.3-0.20220715171204-b7c01b3e0267 h1:yjyMHADZ7cyDMxzJdF09tVdowOTwfb+cys5WdtU/5k4= github.com/digineo/afero v1.8.3-0.20220715171204-b7c01b3e0267/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= @@ -304,6 +310,7 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= diff --git a/service/service.go b/service/service.go index 98f4869..d21cd3a 100644 --- a/service/service.go +++ b/service/service.go @@ -9,6 +9,7 @@ import ( "net/http" "time" + "github.com/digineo/texd/docs" "github.com/digineo/texd/exec" "github.com/digineo/texd/metrics" "github.com/digineo/texd/refstore" @@ -87,6 +88,7 @@ func (svc *service) routes() http.Handler { r := mux.NewRouter() r.HandleFunc("/", HandleUI).Methods(http.MethodGet) r.PathPrefix("/assets/").Handler(HandleAssets()).Methods(http.MethodGet) + r.PathPrefix("/docs").Handler(http.StripPrefix("/docs", docs.Handler())).Methods(http.MethodGet) render := http.Handler(http.HandlerFunc(svc.HandleRender)) if max := svc.maxJobSize; max > 0 {