commit 8cf8567e14434f466421ff604798206d4b1e705e
parent a0b45c199ec0409f00ee8bdd327a888bcb12809f
Author: gearsix <gearsix@tuta.io>
Date: Wed, 9 Jun 2021 13:37:41 +0100
finished LoadContentsDir, Page.NewBodyFromFile()
Diffstat:
M | content.go | | | 149 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
1 file changed, 141 insertions(+), 8 deletions(-)
diff --git a/content.go b/content.go
@@ -1,27 +1,43 @@
package main
import (
+ "bytes"
+ "bufio"
"path/filepath"
+ "io"
"io/fs"
"strings"
+ "os"
"notabug.org/gearsix/suti"
+ "github.com/yuin/goldmark"
+ goldmarkext "github.com/yuin/goldmark/extension"
+ goldmarkparse "github.com/yuin/goldmark/parser"
+ goldmarkhtml "github.com/yuin/goldmark/renderer/html"
)
+var SupportedContentFiletypes = []string{
+ ".txt", // plain-text
+ ".html", // HTML
+ ".md", // commonmark with non-intrusive extensions: linkify, auto heading id, unsafe HTML
+ ".gfm", // github-flavoured markdown
+ ".cm", // commonmark
+}
+
type Content []Page
-func LoadContentDir(dirpath string) (c Content, e error) {
+func LoadContentDir(path string) (c Content, e error) {
pages := make(map[string]Page)
defaults := make(map[string]Meta)
- e = filepath.Walk(dirpath, func(fpath string, info fs.FileInfo, err error) error {
+ e = filepath.Walk(path, func(fpath string, info fs.FileInfo, err error) error {
if err != nil {
return nil
}
if info.IsDir() {
- p := NewPage(strings.TrimPrefix(fpath, dirpath))
+ p := NewPage(strings.TrimPrefix(fpath, path))
for _, dir := range strings.Split(fpath, "/") {
if _, ok := defaults[dir]; ok {
- p.Meta.MergeMeta(defaults[dir])
+ p.Meta.MergeMeta(defaults[dir], true)
}
}
return nil
@@ -35,14 +51,14 @@ func LoadContentDir(dirpath string) (c Content, e error) {
return err
}
if strings.Contains(fpath, ".page") {
- page.Meta.MergeMeta(m)
+ page.Meta.MergeMeta(m, true)
} else if strings.Contains(fpath, ".defaults") {
defaults[pdir] = m
}
} else if ext := filepath.Ext(fpath); ext == ".txt" || ext == ".md" || ext == ".html" {
page.NewBodyFromFile(fpath)
} else {
- page.Assets = append(page.Assets, strings.TrimPrefix(fpath, dirpath))
+ page.Assets = append(page.Assets, strings.TrimPrefix(fpath, path))
}
pages[pdir] = page
@@ -58,7 +74,7 @@ func LoadContentDir(dirpath string) (c Content, e error) {
type Meta map[string]interface{}
-func (m *Meta) MergeMeta(meta Meta, overwrite bool) {
+func (m Meta) MergeMeta(meta Meta, overwrite bool) {
for k, v := range meta {
if _, ok := m[k]; ok && overwrite {
m[k] = v
@@ -84,6 +100,123 @@ func NewPage(path string) Page {
}
}
-func (p *Page) NewBodyFromFile(fpath string) {
+func (p *Page) NewBodyFromFile(fpath string) (err error) {
+ var buf []byte
+ if f, err := os.Open(fpath); err == nil {
+ buf, err = io.ReadAll(f)
+ f.Close()
+ }
+ if err != nil {
+ return
+ }
+
+ var body string
+ for _, lang := range SupportedContentFiletypes {
+ if filepath.Ext(fpath) == lang {
+ switch (lang) {
+ case ".txt":
+ body = txt2html(bytes.NewReader(buf))
+ case ".md":
+ case ".gmd":
+ case ".cm":
+ markdown := getMarkdown(lang)
+ var out bytes.Buffer
+ if err = markdown.Convert(buf, &out); err == nil {
+ body = out.String()
+ }
+ case ".html":
+ body = string(buf)
+ default:
+ continue
+ }
+ }
+ }
+
+ if len(body) == 0 {
+ panic("passed invalid filetype to NewBodyFromFile")
+ }
+ p.Body = append(p.Body, body)
+
+ return err
+}
+
+// txt2html parses textual data from `in` and line-by-line converts
+// it to HTML. Conversion rules are as follows:
+// - Blank lines (with escape characters trimmed) will close any opon tags
+// - If a text line is prefixed with a tab and no tag is open, it will open a <pre> tag
+// - Otherwise any line of text will open a <p> tag
+func txt2html(in io.Reader) (html string) {
+ var block int
+ const p = 1
+ const pre = 2
+
+ fscan := bufio.NewScanner(in)
+ for fscan.Scan() {
+ line := fscan.Text()
+ if len(strings.TrimSpace(line)) == 0 {
+ switch block {
+ case p:
+ html += "</p>\n"
+ case pre:
+ html += "</pre>\n"
+ }
+ block = 0
+ } else if block == 0 && line[0] == '\t' {
+ block = pre
+ html += "<pre>" + line + "\n"
+ } else if block == 0 {
+ block = p
+ html += "<p>" + line
+ } else if block == p {
+ html += " " + line
+ } else if block == pre {
+ html += line + "\n"
+ }
+ }
+ if block == p {
+ html += "</p>"
+ } else if block == pre {
+ html += "</pre>"
+ }
+
+ return html
+}
+
+func getMarkdown(lang string) (markdown goldmark.Markdown) {
+ switch lang {
+ case ".gfm":
+ markdown = goldmark.New(
+ goldmark.WithExtensions(
+ goldmarkext.GFM,
+ goldmarkext.Table,
+ goldmarkext.Strikethrough,
+ goldmarkext.Linkify,
+ goldmarkext.TaskList,
+ ),
+ goldmark.WithParserOptions(
+ goldmarkparse.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ goldmarkhtml.WithUnsafe(),
+ goldmarkhtml.WithHardWraps(),
+ ),
+ )
+ case ".cm":
+ markdown = goldmark.New()
+ case ".md":
+ default:
+ markdown = goldmark.New(
+ goldmark.WithExtensions(
+ goldmarkext.Linkify,
+ ),
+ goldmark.WithParserOptions(
+ goldmarkparse.WithAutoHeadingID(),
+ ),
+ goldmark.WithRendererOptions(
+ goldmarkhtml.WithUnsafe(),
+ ),
+ )
+ }
+ return
}