From a0e87057d6e5bcc526cd7f6f9d50ac0f704981f2 Mon Sep 17 00:00:00 2001 From: Niklas Fasching Date: Thu, 13 Dec 2018 17:44:26 +0100 Subject: [PATCH] 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. --- org/document.go | 36 ++++++++++++++++++++-- org/document_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++ org/keyword.go | 6 +++- 3 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 org/document_test.go diff --git a/org/document.go b/org/document.go index c4443ba..13b3c5a 100644 --- a/org/document.go +++ b/org/document.go @@ -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 { diff --git a/org/document_test.go b/org/document_test.go new file mode 100644 index 0000000..ca5ed2c --- /dev/null +++ b/org/document_test.go @@ -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) + } + } +} diff --git a/org/keyword.go b/org/keyword.go index d4d0399..d34074e 100644 --- a/org/keyword.go +++ b/org/keyword.go @@ -47,7 +47,11 @@ func (d *Document) parseKeyword(i int, stop stopFn) (int, Node) { return consumed, node } } - d.BufferSettings[k.Key] = strings.Join([]string{d.BufferSettings[k.Key], k.Value}, "\n") + 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 }