Skip to content

Commit

Permalink
docs: switch markdown renderer [wip]
Browse files Browse the repository at this point in the history
TODO: link targets need to be rewritten ("./foo.md" works
on GitHub, but we'll need "/docs/foo"). Also, the path
prefix should be injected into Handler(), which means
deferring the markdown rendering to the start of the server.
  • Loading branch information
dmke committed Jul 15, 2022
1 parent 726bab5 commit c7d779c
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 83 deletions.
File renamed without changes.
170 changes: 103 additions & 67 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,126 +3,162 @@ package docs
import (
"bytes"
"embed"
"fmt"
"html/template"
"io"
"log"
"net/http"
"strings"

"github.com/Depado/bfchroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
bf "github.com/russross/blackfriday/v2"
toc "github.com/abhinav/goldmark-toc"
chromahtml "github.com/alecthomas/chroma/formatters/html"
"github.com/digineo/texd"
"github.com/microcosm-cc/bluemonday"
"github.com/yuin/goldmark"
highlighting "github.com/yuin/goldmark-highlighting"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"gopkg.in/yaml.v3"
)

//go:embed docs.yml *.md **/*.md
//go:embed *.md **/*.md
var sources embed.FS

//go:embed docs.yml
var config []byte

//go:embed docs.html
var rawLayout string
var tplLayout = template.Must(template.New("layout").Parse(rawLayout))

type page struct {
Title string
Breadcrumbs []string
Body string
TOC *toc.TOC
CSS []byte
Body []byte
File string
Route string
Children []*page
}
type pageRoutes map[string]*page

var root = func() page {
structure, err := sources.Open("docs.yml")
if err != nil {
panic(err)
}
defer structure.Close()

var root, routes = func() (page, pageRoutes) {
var menu page
dec := yaml.NewDecoder(structure)
dec := yaml.NewDecoder(bytes.NewReader(config))
dec.KnownFields(true)
if err := dec.Decode(&menu); err != nil {
panic(err)
}

menu.init()
return menu
r := make(pageRoutes)
menu.init(r)
return menu, r
}()

func (pg *page) init(crumbs ...string) {
func (pg *page) init(r pageRoutes, crumbs ...string) {
if pg.File != "" {
if r := strings.TrimSuffix(pg.File, ".md"); r == "index" {
if r := strings.TrimSuffix(pg.File, ".md"); r == "README" {
pg.Route = ""
} else {
pg.Route = "/" + r
}

r[pg.Route] = pg
pg.parseFile()
}
if pg.Title != "" {
pg.Breadcrumbs = append(pg.Breadcrumbs, pg.Title)
pg.Breadcrumbs = append([]string{pg.Title}, crumbs...)
}
for _, child := range pg.Children {
child.init(pg.Breadcrumbs...)
child.init(r, pg.Breadcrumbs...)
}
}

var sanitize = func() func(io.Reader) *bytes.Buffer {
p := bluemonday.UGCPolicy()
p.AllowAttrs("class").Globally()
return p.SanitizeReader
}()

func (pg *page) parseFile() {
body, err := sources.ReadFile(pg.File)
raw, err := sources.ReadFile(pg.File)
if err != nil {
panic(err)
}

r := bfchroma.NewRenderer(
bfchroma.WithoutAutodetect(),
bfchroma.ChromaOptions(
html.WithLineNumbers(true),
var css, body bytes.Buffer
md := goldmark.New(
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
html.WithUnsafe(),
),
goldmark.WithExtensions(
extension.GFM,
highlighting.NewHighlighting(
highlighting.WithCSSWriter(&css),
highlighting.WithStyle("github"),
highlighting.WithFormatOptions(
chromahtml.WithLineNumbers(true),
chromahtml.WithClasses(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"))
}
doc := md.Parser().Parse(text.NewReader(raw))
tree, err := toc.Inspect(doc, raw)
if err != nil {
panic(err)
}
if pg.Title == "" {
if len(tree.Items) > 0 {
pg.Title = string(tree.Items[0].Title)
}
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)
}
if err := md.Renderer().Render(&body, raw, doc); err != nil {
panic(err)
}
pg.TOC = tree
pg.CSS = css.Bytes()
pg.Body = sanitize(&body).Bytes()
}

func Handler() http.Handler {
type pageVars struct {
Version string
Title string
CSS template.CSS
Content template.HTML
}

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)
pg := routes[r.URL.Path]
if pg == nil {
http.NotFound(w, r)
return
}

fmt.Fprintf(w, "%#v\n\n", r.URL)
var buf bytes.Buffer
err := tplLayout.Execute(&buf, &pageVars{
Version: texd.Version(),
Title: strings.Join(pg.Breadcrumbs, " · "),
CSS: template.CSS(pg.CSS),
Content: template.HTML(pg.Body),
})

if err != nil {
log.Println(err)
code := http.StatusInternalServerError
http.Error(w, http.StatusText(code), code)
return
}

root.Dump(w)
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(http.StatusOK)
_, _ = buf.WriteTo(w)
})
}
39 changes: 39 additions & 0 deletions docs/docs.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .Title }}</title>

<link rel="stylesheet" href="/assets/bootstrap-5.1.3.min.css">
<style>{{ .CSS }}</style>
</head>

<body>
<div id="app" class="pb-5">
<nav class="navbar navbar-light navbar-expand-sm bg-light">
<div class="container-fluid">
<a href="https://github.com/digineo/texd" class="navbar-brand">texd</a>

<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="/">Play</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/docs">Documentation</a>
</li>
</ul>

<span class="navbar-text">
{{ .Version }}
</span>
</div>
</nav>

<div class="container">
{{ .Content }}
</div>
</div>
</body>
</html>
2 changes: 1 addition & 1 deletion docs/docs.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
file: index.md
file: README.md
children:
- file: operation-modes.md
- file: cli-options.md
Expand Down
10 changes: 7 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@ 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/abhinav/goldmark-toc v0.2.1
github.com/alecthomas/chroma v0.10.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
github.com/docker/go-units v0.4.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/microcosm-cc/bluemonday v1.0.19
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
github.com/yuin/goldmark v1.4.13
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
go.uber.org/zap v1.21.0
gopkg.in/yaml.v3 v3.0.1
)

require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
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
Expand All @@ -36,6 +39,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/morikuni/aec v1.0.0 // indirect
Expand Down
22 changes: 16 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,21 @@ 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/abhinav/goldmark-toc v0.2.1 h1:QJsKKGbdVeCWYMB11hSkNuZLuIzls7Y4KBZfwTkBB90=
github.com/abhinav/goldmark-toc v0.2.1/go.mod h1:aq1IZ9qN85uFYpowec98iJrFkEHYT4oeFD1SC0qd8d0=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
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=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
Expand Down Expand Up @@ -195,6 +196,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
Expand Down Expand Up @@ -239,6 +242,8 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=
Expand Down Expand Up @@ -310,7 +315,6 @@ 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=
Expand Down Expand Up @@ -352,7 +356,13 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
Expand Down
2 changes: 1 addition & 1 deletion service/assets/texd.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const app = Vue.createApp({
},

beforeMount() {
setInterval(this.fetchStatus, 1000)
setInterval(this.fetchStatus, 5000)
this.fetchStatus()
},

Expand Down
Loading

0 comments on commit c7d779c

Please sign in to comment.