Fix BufferSettings append & add ParseFrontMatter

until now buffersettings were always appended using \n which means the first
value would already be written as "\nVALUE". Not anymore.

Also we finally add an option to parse just the front matter. Still not
efficient as we tokenize the whole org file but i don't think saving a few
milliseconds would be worth making the code uglier.
This commit is contained in:
Niklas Fasching 2018-12-13 17:44:26 +01:00
parent 0255a129e2
commit a0e87057d6
3 changed files with 110 additions and 4 deletions

View file

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"log"
"strings"
)
type Document struct {
@ -51,6 +52,15 @@ var lexFns = []lexFn{
var nilToken = token{"nil", -1, "", nil}
var DefaultFrontMatterHandler = func(k, v string) interface{} {
switch k {
case "TAGS":
return strings.Fields(v)
default:
return v
}
}
func NewDocument() *Document {
return &Document{
Footnotes: &Footnotes{
@ -78,6 +88,29 @@ func (d *Document) Write(w Writer) Writer {
}
func (d *Document) Parse(input io.Reader) *Document {
d.tokenize(input)
_, nodes := d.parseMany(0, func(d *Document, i int) bool { return !(i < len(d.tokens)) })
d.Nodes = nodes
return d
}
func (d *Document) FrontMatter(input io.Reader, f func(string, string) interface{}) map[string]interface{} {
d.tokenize(input)
d.parseMany(0, func(d *Document, i int) bool {
if !(i < len(d.tokens)) {
return true
}
t := d.tokens[i]
return t.kind != "keyword" && !(t.kind == "text" && t.content == "")
})
frontMatter := make(map[string]interface{}, len(d.BufferSettings))
for k, v := range d.BufferSettings {
frontMatter[k] = f(k, v)
}
return frontMatter
}
func (d *Document) tokenize(input io.Reader) {
d.tokens = []token{}
scanner := bufio.NewScanner(input)
for scanner.Scan() {
@ -86,9 +119,6 @@ func (d *Document) Parse(input io.Reader) *Document {
if err := scanner.Err(); err != nil {
panic(err)
}
_, nodes := d.parseMany(0, func(d *Document, i int) bool { return !(i < len(d.tokens)) })
d.Nodes = nodes
return d
}
func (d *Document) Get(key string) string {

72
org/document_test.go Normal file
View file

@ -0,0 +1,72 @@
package org
import (
"reflect"
"strings"
"testing"
)
type frontMatterTest struct {
name string
input string
handler func(string, string) interface{}
expected map[string]interface{}
}
var frontMatterTests = []frontMatterTest{
{`basic`,
`#+TITLE: The Title`,
DefaultFrontMatterHandler,
map[string]interface{}{"TITLE": "The Title"}},
{`empty`,
`* No frontmatter here`,
DefaultFrontMatterHandler,
map[string]interface{}{}},
{`custom handler`,
`
#+TITLE: The Title
#+TAGS: foo bar
`,
func(k, v string) interface{} {
switch k {
case "TITLE":
return "Thanks For All The Fish"
default:
return DefaultFrontMatterHandler(k, v)
}
},
map[string]interface{}{
"TITLE": "Thanks For All The Fish",
"TAGS": []string{"foo", "bar"},
}},
{`multiple + ignored keyword`,
`
#+TITLE: The Title
#+AUTHOR: The Author
#+OTHER: some other keyword
#+TAGS: this will become []string
something that's not a keyword or a text line without content
#+SUBTITLE: The Subtitle`,
DefaultFrontMatterHandler,
map[string]interface{}{
"TITLE": "The Title",
"AUTHOR": "The Author",
"OTHER": "some other keyword",
"TAGS": []string{"this", "will", "become", "[]string"},
},
},
}
func TestParseFrontMatter(t *testing.T) {
for _, test := range frontMatterTests {
actual := NewDocument().FrontMatter(strings.NewReader(test.input), test.handler)
if !reflect.DeepEqual(test.expected, actual) {
t.Errorf("%s\n got: %#v\nexpected: %#v\n%s'", test.name, actual, test.expected, test.input)
}
}
}

View file

@ -47,7 +47,11 @@ func (d *Document) parseKeyword(i int, stop stopFn) (int, Node) {
return consumed, node
}
}
if _, ok := d.BufferSettings[k.Key]; ok {
d.BufferSettings[k.Key] = strings.Join([]string{d.BufferSettings[k.Key], k.Value}, "\n")
} else {
d.BufferSettings[k.Key] = k.Value
}
return 1, k
}