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 }