Compare commits

...

4 Commits

Author SHA1 Message Date
Přemysl Eric Janouch 8f542c7120
hswg: execute a template given on standard input
So that information can be extracted from documents easily.
2020-09-21 19:26:55 +02:00
Přemysl Eric Janouch ea8c59961c
hswg: don't link to drafts 2020-09-21 19:26:54 +02:00
Přemysl Eric Janouch 750f2139f5
hswg: extract attributes from documents 2020-09-21 19:26:54 +02:00
Přemysl Eric Janouch 3d002bc540
Bump libasciidoc dependency 2020-09-21 19:26:54 +02:00
3 changed files with 87 additions and 42 deletions

10
go.mod
View File

@ -5,11 +5,11 @@ go 1.14
require (
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
github.com/alecthomas/chroma v0.8.0 // indirect
github.com/bytesparadise/libasciidoc v0.3.1-0.20200809205252-bb8703c3bc60
github.com/bytesparadise/libasciidoc v0.3.1-0.20200913184529-3480071e77b4
github.com/dlclark/regexp2 v1.2.1 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/onsi/ginkgo v1.14.0 // indirect
github.com/onsi/ginkgo v1.14.1 // indirect
golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed // indirect
golang.org/x/tools v0.0.0-20200815165600-90abf76919f3 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b // indirect
)

21
go.sum
View File

@ -20,8 +20,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bytesparadise/libasciidoc v0.3.1-0.20200809205252-bb8703c3bc60 h1:WXgpYFlpYkEpGfmPb1pMxZpHUa07ObfYP0iUuydi5xk=
github.com/bytesparadise/libasciidoc v0.3.1-0.20200809205252-bb8703c3bc60/go.mod h1:kWkgc6KJVCDUT2QlPbYIlYspvy0+3vuWriL0kV2NDig=
github.com/bytesparadise/libasciidoc v0.3.1-0.20200913184529-3480071e77b4 h1:JaZtHWcjj7GcPmt52XoS/iDoe472tIrZrnZf/sHnUdw=
github.com/bytesparadise/libasciidoc v0.3.1-0.20200913184529-3480071e77b4/go.mod h1:kWkgc6KJVCDUT2QlPbYIlYspvy0+3vuWriL0kV2NDig=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@ -42,6 +42,8 @@ github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.2.1 h1:Ff/S0snjr1oZHUNOkvA/gP6KUaMg5vDDl3Qnhjnwgm8=
github.com/dlclark/regexp2 v1.2.1/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@ -117,8 +119,8 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
@ -171,6 +173,7 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@ -198,6 +201,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -223,8 +228,8 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@ -237,8 +242,8 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190830223141-573d9926052a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200815165600-90abf76919f3 h1:0aScV/0rLmANzEYIhjCOi2pTvDyhZNduBUMD2q3iqs4=
golang.org/x/tools v0.0.0-20200815165600-90abf76919f3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b h1:3/5GThpuWHBq2GFcurHBWuWlzdbln+Er+cyzGqQAPOs=
golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

View File

@ -6,6 +6,7 @@ import (
"bytes"
"encoding/xml"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
@ -18,9 +19,12 @@ import (
"unicode"
"unicode/utf8"
"github.com/bytesparadise/libasciidoc"
"github.com/bytesparadise/libasciidoc/pkg/configuration"
"github.com/bytesparadise/libasciidoc/pkg/parser"
"github.com/bytesparadise/libasciidoc/pkg/renderer"
"github.com/bytesparadise/libasciidoc/pkg/renderer/sgml/html5"
"github.com/bytesparadise/libasciidoc/pkg/types"
"github.com/bytesparadise/libasciidoc/pkg/validator"
)
// isTitle returns the title level if the lines seem to form a title,
@ -68,14 +72,27 @@ func ConvertTitles(w *io.PipeWriter, input []byte) {
writeLine(w, last, nil)
}
// Metadata contains select metadata about a rendered document.
type Metadata struct {
types.Metadata
// Note that this includes entries from the front-matter
// (see parser.ApplySubstitutions <- parser.ParseDocument).
Attributes types.Attributes
}
// IsDraft returns whether the document is marked as a draft, and should not
// be linked anywhere else.
func (m *Metadata) IsDraft() bool { return m.Attributes.Has("draft") }
// Render converts an io.Reader with an AsciiDoc document to HTML. So long as
// the file could be read at all, it will always return a non-empty document.
func Render(doc io.Reader, config configuration.Configuration) (
html *bytes.Buffer, meta types.Metadata, err error) {
func Render(r io.Reader, config configuration.Configuration) (
html *bytes.Buffer, meta Metadata, err error) {
html = bytes.NewBuffer(nil)
var input []byte
if input, err = ioutil.ReadAll(doc); err != nil {
if input, err = ioutil.ReadAll(r); err != nil {
return
}
@ -88,7 +105,14 @@ func Render(doc io.Reader, config configuration.Configuration) (
// io.Copy(os.Stdout, pr)
// return
meta, err = libasciidoc.Convert(pr, html, config)
var doc types.Document
if doc, err = parser.ParseDocument(pr, config); err == nil {
for _, problem := range validator.Validate(&doc) {
fmt.Fprintln(os.Stderr, problem.Message)
}
ctx := renderer.NewContext(doc, config)
meta.Metadata, err = html5.Render(ctx, doc, html)
}
if err != nil {
// Fallback: output all the text sanitized for direct inclusion.
html.Reset()
@ -100,16 +124,18 @@ func Render(doc io.Reader, config configuration.Configuration) (
}
_, _ = html.WriteString("</pre>")
}
meta.Attributes = doc.Attributes
return
}
// entry contains all context information about a single page.
type entry struct {
path string // path
mtime time.Time // modification time
metadata types.Metadata // metadata
document []byte // inner document with expanded LinkWords
backlinks []string // what documents link back here
// Entry contains all context information about a single page.
type Entry struct {
PathSource string // path to source AsciiDoc
PathDestination string // path to destination HTML
mtime time.Time // modification time
Metadata Metadata // metadata
document []byte // inner document with expanded LinkWords
backlinks []string // what documents link back here
}
var extRE = regexp.MustCompile(`\.[^/.]*$`)
@ -125,16 +151,17 @@ func resultPath(path string) string {
return path + ".html"
}
func makeLink(m *map[string]*entry, name string) string {
func makeLink(m *map[string]*Entry, name string) string {
e := (*m)[name]
return fmt.Sprintf("<a href='%s'>%s</a>", resultPath(e.path), name)
return fmt.Sprintf("<a href='%s'>%s</a>", e.PathDestination, name)
}
var linkWordRE = regexp.MustCompile(`\b\p{Lu}\p{L}*\b`)
func expand(m *map[string]*entry, name string, chunk []byte) []byte {
func expand(m *map[string]*Entry, name string, chunk []byte) []byte {
return linkWordRE.ReplaceAllFunc(chunk, func(match []byte) []byte {
if link, ok := (*m)[string(match)]; ok && string(match) != name {
if link, ok := (*m)[string(match)]; ok && string(match) != name &&
!link.Metadata.IsDraft() {
link.backlinks = append(link.backlinks, name)
return []byte(makeLink(m, string(match)))
}
@ -143,9 +170,7 @@ func expand(m *map[string]*entry, name string, chunk []byte) []byte {
}
func singleFile() {
html, meta, err := Render(os.Stdin, configuration.NewConfiguration(
configuration.WithBackEnd("xhtml5"),
))
html, meta, err := Render(os.Stdin, configuration.NewConfiguration())
if err != nil {
log.Println(err)
} else if meta.Title != "" {
@ -172,7 +197,7 @@ func main() {
}
// Create a map from document names to their page entries.
entries := map[string]*entry{}
entries := map[string]*Entry{}
for _, glob := range os.Args[2:] {
matches, err := filepath.Glob(glob)
if err != nil {
@ -181,15 +206,18 @@ func main() {
for _, path := range matches {
name := stripExtension(filepath.Base(path))
if conflict, ok := entries[name]; ok {
log.Fatalf("%s: conflicts with %s\n", name, conflict.path)
log.Fatalf("%s: conflicts with %s\n", name, conflict.PathSource)
}
entries[name] = &Entry{
PathSource: path,
PathDestination: resultPath(path),
}
entries[name] = &entry{path: path}
}
}
tagRE := regexp.MustCompile(`<[^<>]+>`)
for name, e := range entries {
f, err := os.Open(e.path)
f, err := os.Open(e.PathSource)
if err != nil {
log.Fatalln(err)
}
@ -201,9 +229,8 @@ func main() {
}
var html *bytes.Buffer
if html, e.metadata, err = Render(f, configuration.NewConfiguration(
configuration.WithBackEnd("xhtml5"),
configuration.WithFilename(e.path),
if html, e.Metadata, err = Render(f, configuration.NewConfiguration(
configuration.WithFilename(e.PathSource),
configuration.WithLastUpdated(e.mtime),
)); err != nil {
log.Fatalln(err)
@ -221,15 +248,16 @@ func main() {
e.document = expanded.Bytes()
}
// TODO(p): These should be run through html/template.
for name, e := range entries {
f, err := os.Create(resultPath(e.path))
f, err := os.Create(e.PathDestination)
if err != nil {
log.Fatalln(err)
}
_, _ = f.Write(header)
title := e.metadata.Title
title := e.Metadata.Title
if title == "" {
title = name
}
@ -255,6 +283,18 @@ func main() {
_, _ = f.Write(e.document)
_, _ = f.WriteString(fmt.Sprintf("<p id=footer>Last updated: %s"+
" &mdash; <a href='%s'>Source</p>\n",
e.metadata.LastUpdated, e.path))
e.Metadata.LastUpdated, e.PathSource))
}
// Execute a template from the standard input.
var input []byte
if input, err = ioutil.ReadAll(os.Stdin); err != nil {
log.Fatalln(err)
}
// TODO(p): Splitting content to categories would be nice.
t, err := template.New("-").Parse(string(input))
if err = t.Execute(os.Stdout, entries); err != nil {
log.Fatalln(err)
}
}