first commit
This commit is contained in:
commit
cfcd2ed6eb
|
@ -0,0 +1,3 @@
|
|||
.idea
|
||||
bin
|
||||
*~
|
|
@ -0,0 +1,2 @@
|
|||
build:
|
||||
CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -o bin/ni main.go
|
|
@ -0,0 +1,114 @@
|
|||
package gmi2html
|
||||
|
||||
import (
|
||||
"html"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var heading1Regexp = regexp.MustCompile("^# (.*)$")
|
||||
var heading2Regexp = regexp.MustCompile("^## (.*)$")
|
||||
var heading3Regexp = regexp.MustCompile("^### (.*)$")
|
||||
var linkRegexp = regexp.MustCompile("^=> ([^\\s]+) ?(.+)?$")
|
||||
var blockquoteRegexp = regexp.MustCompile("^> (.*)$")
|
||||
var preRegexp = regexp.MustCompile("^```.*$")
|
||||
var bulletRegexp = regexp.MustCompile(`^\* ?(.*)$`)
|
||||
|
||||
func clearLinkMode(linkMode *bool, rv *[]string) {
|
||||
if *linkMode {
|
||||
*rv = append(*rv, "</p>")
|
||||
*linkMode = false
|
||||
}
|
||||
}
|
||||
|
||||
func clearUlMode(ulMode *bool, rv *[]string) {
|
||||
if *ulMode {
|
||||
*rv = append(*rv, "</ul>")
|
||||
*ulMode = false
|
||||
}
|
||||
}
|
||||
|
||||
func sanitize(input string) string {
|
||||
return html.EscapeString(input)
|
||||
}
|
||||
|
||||
func Convert(gmi string) string {
|
||||
var rv []string
|
||||
preMode := false
|
||||
ulMode := false
|
||||
linkMode := false
|
||||
for _, l := range strings.Split(gmi, "\n") {
|
||||
l = strings.TrimRight(l, "\r")
|
||||
if preMode {
|
||||
switch {
|
||||
case preRegexp.MatchString(l):
|
||||
rv = append(rv, "</pre>")
|
||||
preMode = false
|
||||
default:
|
||||
rv = append(rv, sanitize(l))
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case heading1Regexp.MatchString(l):
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
matches := heading1Regexp.FindStringSubmatch(l)
|
||||
rv = append(rv, "<h1>"+sanitize(matches[1])+"</h1>")
|
||||
case heading2Regexp.MatchString(l):
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
matches := heading2Regexp.FindStringSubmatch(l)
|
||||
rv = append(rv, "<h2>"+sanitize(matches[1])+"</h2>")
|
||||
case heading3Regexp.MatchString(l):
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
matches := heading3Regexp.FindStringSubmatch(l)
|
||||
rv = append(rv, "<h3>"+sanitize(matches[1])+"</h3>")
|
||||
case blockquoteRegexp.MatchString(l):
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
matches := blockquoteRegexp.FindStringSubmatch(l)
|
||||
rv = append(rv, "<blockquote>"+sanitize(matches[1])+"</blockquote>")
|
||||
case linkRegexp.MatchString(l):
|
||||
clearUlMode(&ulMode, &rv)
|
||||
matches := linkRegexp.FindStringSubmatch(l)
|
||||
if len(matches[2]) == 0 {
|
||||
matches[2] = matches[1]
|
||||
}
|
||||
if strings.HasSuffix(matches[1], ".png") || strings.HasSuffix(matches[1], ".PNG") || strings.HasSuffix(matches[1], ".jpg") || strings.HasSuffix(matches[1], ".JPG") || strings.HasSuffix(matches[1], ".jpeg") || strings.HasSuffix(matches[1], ".gif") || strings.HasSuffix(matches[1], ".GIF") {
|
||||
rv = append(rv, "<img src=\""+sanitize(matches[1])+"\"/>")
|
||||
continue
|
||||
}
|
||||
if linkMode {
|
||||
rv = append(rv, "<a href=\""+sanitize(matches[1])+"\">"+sanitize(matches[2])+"</a><br/>")
|
||||
continue
|
||||
}
|
||||
rv = append(rv, "<p><a href=\""+sanitize(matches[1])+"\">"+sanitize(matches[2])+"</a><br/>")
|
||||
linkMode = true
|
||||
case preRegexp.MatchString(l):
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
rv = append(rv, "<pre>")
|
||||
preMode = true
|
||||
case bulletRegexp.MatchString(l):
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
matches := bulletRegexp.FindStringSubmatch(l)
|
||||
if ulMode {
|
||||
rv = append(rv, "<li>"+sanitize(matches[1])+"</li>")
|
||||
continue
|
||||
}
|
||||
rv = append(rv, "<ul>\n<li>"+sanitize(matches[1])+"</li>")
|
||||
ulMode = true
|
||||
default:
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
if len(l) != 0 {
|
||||
rv = append(rv, "<p>"+sanitize(l)+"</p>")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clearUlMode(&ulMode, &rv)
|
||||
clearLinkMode(&linkMode, &rv)
|
||||
return strings.Join(rv, "\n")
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"ni/gmi2html"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var tplPage = `
|
||||
{{ define "content" }}
|
||||
{{ .Content }}
|
||||
{{ if .Backlinks }}
|
||||
<h2>Backlinks</h2>
|
||||
<ul>
|
||||
{{ range .Backlinks }}
|
||||
<li><a href="/{{ . }}">{{ . }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
var tplChangelog = `
|
||||
{{ define "content" }}
|
||||
<ul>
|
||||
{{ range . }}
|
||||
<li><a href="/{{ .Name }}">{{ .Name }}</a> {{ .TimeFormatted }}</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
`
|
||||
|
||||
var filename = regexp.MustCompile(`^[a-z0-9\-]+\.gmi$`)
|
||||
var re = regexp.MustCompile(`\[\[([a-z0-9\-]+)\]\]`)
|
||||
var PageContent *template.Template
|
||||
var PageChangelog *template.Template
|
||||
|
||||
func backlinks(in, name string) []string {
|
||||
var rv []string
|
||||
bl, err := exec.Command("/bin/sh", "-c", "grep -l '\\[\\["+name[:len(name)-4]+"\\]\\]' "+in+"/*.gmi").Output()
|
||||
if err != nil {
|
||||
return rv
|
||||
}
|
||||
for _, link := range strings.Fields(string(bl)) {
|
||||
oname := outputName(link[len(in+"/"):])
|
||||
if oname != outputName(name) {
|
||||
rv = append(rv, oname)
|
||||
}
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
func process(in, name string, d []byte) []byte {
|
||||
html := bytes.NewBufferString("")
|
||||
|
||||
err := PageContent.Execute(html, map[string]interface{}{
|
||||
"Title": name,
|
||||
"Content": template.HTML(re.ReplaceAllString(gmi2html.Convert(string(d)), `<a href="/$1.html">$1</a>`)),
|
||||
"Backlinks": backlinks(in, name),
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return html.Bytes()
|
||||
}
|
||||
|
||||
func buildChangelog(f []File) []byte {
|
||||
html := bytes.NewBufferString("")
|
||||
err := PageChangelog.Execute(html, f)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return html.Bytes()
|
||||
}
|
||||
|
||||
type File struct {
|
||||
Name string
|
||||
UpdatedAt time.Time
|
||||
TimeFormatted string
|
||||
}
|
||||
|
||||
func outputName(inputName string) string {
|
||||
return inputName[:len(inputName)-3] + "html"
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 4 {
|
||||
log.Fatal("Usage: ni input output template.html")
|
||||
}
|
||||
|
||||
in := os.Args[1]
|
||||
out := os.Args[2]
|
||||
tpl := os.Args[3]
|
||||
|
||||
b, err := os.ReadFile(tpl)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
PageContent = template.Must(template.New("").Parse(string(b) + tplPage))
|
||||
PageChangelog = template.Must(template.New("").Parse(string(b) + tplChangelog))
|
||||
|
||||
files, err := ioutil.ReadDir(in)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var changelog []File
|
||||
|
||||
for _, file := range files {
|
||||
if !filename.MatchString(file.Name()) {
|
||||
continue
|
||||
}
|
||||
file, err := os.Stat(filepath.Join(in, file.Name()))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
updatedAt := file.ModTime()
|
||||
changelog = append(changelog, File{
|
||||
UpdatedAt: updatedAt,
|
||||
TimeFormatted: updatedAt.Format("2006-01-02"),
|
||||
Name: outputName(file.Name()),
|
||||
})
|
||||
data, err := os.ReadFile(filepath.Join(in, file.Name()))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
output := process(in, file.Name(), data)
|
||||
err = os.WriteFile(filepath.Join(out, outputName(file.Name())), output, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Print(".")
|
||||
}
|
||||
sort.Slice(changelog, func(i, j int) bool {
|
||||
return changelog[i].UpdatedAt.After(changelog[j].UpdatedAt)
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(out, "changelog.html"), buildChangelog(changelog), 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Done")
|
||||
}
|
Loading…
Reference in New Issue