notes-server/notes/file.go

108 lines
3.0 KiB
Go
Executable File

package notes
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"io/ioutil"
"local/notes-server/filetree"
"path"
"regexp"
"strings"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/ast"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
)
func (n *Notes) File(urlPath string) (string, error) {
p := filetree.NewPathFromURL(urlPath)
if p.IsDir() {
return "", errors.New("path is dir")
}
b, _ := ioutil.ReadFile(p.Local)
renderer := html.NewRenderer(html.RendererOptions{
Flags: html.CommonFlags | html.TOC,
RenderNodeHook: n.commentFormer(urlPath, b),
})
parser := parser.NewWithExtensions(parser.CommonExtensions | parser.HeadingIDs | parser.AutoHeadingIDs | parser.Titleblock)
content := markdown.ToHTML(b, parser, renderer)
return string(content) + "\n", nil
}
func (n *Notes) commentFormer(urlPath string, md []byte) html.RenderNodeFunc {
urlPath = strings.TrimPrefix(urlPath, "/")
urlPath = strings.TrimPrefix(urlPath, strings.Split(urlPath, "/")[0])
lines := bytes.Split(md, []byte("\n"))
cur := -1
nextHeader := func() {
cur++
for cur < len(lines) {
for _, opener_closer := range [][]string{{"```", "```"}, {`<summary>`, `</summary>`}} {
if bytes.Contains(lines[cur], []byte(opener_closer[0])) {
cur++
for cur < len(lines) && !bytes.Contains(lines[cur], []byte(opener_closer[1])) {
cur++
}
cur++
}
}
if cur >= len(lines) {
break
}
line := lines[cur]
ok, err := regexp.Match(`^\s*#+\s*.+$`, line)
if err != nil {
panic(err)
} else if ok {
return
}
cur++
}
}
return func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
if heading, ok := node.(*ast.Heading); ok && !entering {
nextHeader()
fmt.Fprintf(w, `
<form method="POST" action=%q class="comment">
<input name="lineno" type="number" style="display:none" value="%d"/>
<input autocomplete="off" name="content" type="text"/>
<input type="submit"/>
</form>
`, path.Join("/comment", urlPath)+"#"+heading.HeadingID, cur)
}
return ast.GoToNext, false
}
}
func (n *Notes) commentFormerOld() html.RenderNodeFunc {
return func(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
if heading, ok := node.(*ast.Heading); ok {
if !entering {
literal := ""
ast.WalkFunc(heading, func(n ast.Node, e bool) ast.WalkStatus {
if leaf := n.AsLeaf(); e && leaf != nil {
if literal != "" {
literal += ".*"
}
literal += string(leaf.Literal)
}
return ast.GoToNext
})
level := heading.Level
id := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(`^[ \t]*%s\s*%s(].*)?\s*$`, strings.Repeat("#", level), literal)))
fmt.Fprintf(w, `
<form method="POST" target="/comment" name="%s" class="comment">
<input autocomplete="off" name="content" type="text"/>
<input type="submit"/>
</form>
`, id)
}
}
return ast.GoToNext, false
}
}