From 0186545123842eb944eaef9339a8267ea97e6559 Mon Sep 17 00:00:00 2001 From: Niklas Fasching Date: Sun, 16 Dec 2018 23:50:01 +0100 Subject: [PATCH] Add basic support for drawers --- org/document.go | 3 +++ org/drawer.go | 37 +++++++++++++++++++++++++++++++++++++ org/headline.go | 19 +++++++++++++------ org/html.go | 6 ++++++ org/org.go | 11 +++++++++++ org/testdata/headlines.html | 12 ++++++++++++ org/testdata/headlines.org | 11 ++++++++++- org/testdata/misc.html | 11 ++++++++++- 8 files changed, 102 insertions(+), 8 deletions(-) create mode 100644 org/drawer.go diff --git a/org/document.go b/org/document.go index ffe56de..abd7099 100644 --- a/org/document.go +++ b/org/document.go @@ -43,6 +43,7 @@ type token struct { var lexFns = []lexFn{ lexHeadline, + lexDrawer, lexBlock, lexList, lexTable, @@ -168,6 +169,8 @@ func (d *Document) parseOne(i int, stop stopFn) (consumed int, node Node) { consumed, node = d.parseTable(i, stop) case "beginBlock": consumed, node = d.parseBlock(i, stop) + case "beginDrawer": + consumed, node = d.parseDrawer(i, stop) case "text": consumed, node = d.parseParagraph(i, stop) case "example": diff --git a/org/drawer.go b/org/drawer.go new file mode 100644 index 0000000..b1f069b --- /dev/null +++ b/org/drawer.go @@ -0,0 +1,37 @@ +package org + +import ( + "regexp" + "strings" +) + +type Drawer struct { + Name string + Children []Node +} + +var beginDrawerRegexp = regexp.MustCompile(`^(\s*):(\S+):\s*$`) +var endDrawerRegexp = regexp.MustCompile(`^(\s*):END:\s*$`) + +func lexDrawer(line string) (token, bool) { + if m := endDrawerRegexp.FindStringSubmatch(line); m != nil { + return token{"endDrawer", len(m[1]), "", m}, true + } else if m := beginDrawerRegexp.FindStringSubmatch(line); m != nil { + return token{"beginDrawer", len(m[1]), strings.ToUpper(m[2]), m}, true + } + return nilToken, false +} + +func (d *Document) parseDrawer(i int, parentStop stopFn) (int, Node) { + drawer, start := Drawer{Name: strings.ToUpper(d.tokens[i].content)}, i + i++ + stop := func(d *Document, i int) bool { + return parentStop(d, i) || d.tokens[i].kind == "endDrawer" || d.tokens[i].kind == "headline" + } + consumed, nodes := d.parseMany(i, stop) + drawer.Children = nodes + if d.tokens[i+consumed].kind == "endDrawer" { + consumed++ + } + return i + consumed - start, drawer +} diff --git a/org/headline.go b/org/headline.go index 1527f3c..c72bb2f 100644 --- a/org/headline.go +++ b/org/headline.go @@ -7,12 +7,13 @@ import ( ) type Headline struct { - Lvl int - Status string - Priority string - Title []Node - Tags []string - Children []Node + Lvl int + Status string + Priority string + Properties Node + Title []Node + Tags []string + Children []Node } var headlineRegexp = regexp.MustCompile(`^([*]+)\s+(.*)`) @@ -60,6 +61,12 @@ func (d *Document) parseHeadline(i int, parentStop stopFn) (int, Node) { return parentStop(d, i) || d.tokens[i].kind == "headline" && d.tokens[i].lvl <= headline.Lvl } consumed, nodes := d.parseMany(i+1, stop) + if len(nodes) > 0 { + if d, ok := nodes[0].(Drawer); ok && d.Name == "PROPERTIES" { + headline.Properties = d + nodes = nodes[1:] + } + } headline.Children = nodes if headline.Lvl == 1 && text == d.Footnotes.Title && d.Footnotes.ExcludeHeading { diff --git a/org/html.go b/org/html.go index 5b88d20..bbdcf4f 100644 --- a/org/html.go +++ b/org/html.go @@ -74,6 +74,8 @@ func (w *HTMLWriter) writeNodes(ns ...Node) { w.writeHeadline(n) case Block: w.writeBlock(n) + case Drawer: + w.writeDrawer(n) case FootnoteDefinition: w.writeFootnoteDefinition(n) @@ -141,6 +143,10 @@ func (w *HTMLWriter) writeBlock(b Block) { } } +func (w *HTMLWriter) writeDrawer(d Drawer) { + w.writeNodes(d.Children...) +} + func (w *HTMLWriter) writeKeyword(k Keyword) { if k.Key == "HTML" { w.WriteString(k.Value + "\n") diff --git a/org/org.go b/org/org.go index ab8306f..29894ca 100644 --- a/org/org.go +++ b/org/org.go @@ -62,6 +62,8 @@ func (w *OrgWriter) writeNodes(ns ...Node) { w.writeHeadline(n) case Block: w.writeBlock(n) + case Drawer: + w.writeDrawer(n) case FootnoteDefinition: w.writeFootnoteDefinition(n) @@ -127,6 +129,9 @@ func (w *OrgWriter) writeHeadline(h Headline) { if len(h.Children) != 0 { w.WriteString(w.indent) } + if h.Properties != nil { + w.writeNodes(h.Properties) + } w.writeNodes(h.Children...) } @@ -147,6 +152,12 @@ func (w *OrgWriter) writeBlock(b Block) { w.WriteString(w.indent + "#+END_" + b.Name + "\n") } +func (w *OrgWriter) writeDrawer(d Drawer) { + w.WriteString(w.indent + ":" + d.Name + ":\n") + w.writeNodes(d.Children...) + w.WriteString(w.indent + ":END:\n") +} + func (w *OrgWriter) writeFootnotes(d *Document) { fs := d.Footnotes if len(fs.Definitions) == 0 { diff --git a/org/testdata/headlines.html b/org/testdata/headlines.html index d180e8b..36945d2 100644 --- a/org/testdata/headlines.html +++ b/org/testdata/headlines.html @@ -9,6 +9,18 @@ Headline with todo status & priority DONE Headline with TODO status +

+the content +

Headline with tags & priority   foo bar

+

+Still outside the drawer +

+

+This is inside the drawer +

+

+Still outside the drawer +

diff --git a/org/testdata/headlines.org b/org/testdata/headlines.org index 30781d8..b478d66 100644 --- a/org/testdata/headlines.org +++ b/org/testdata/headlines.org @@ -1,5 +1,14 @@ * Simple Headline * TODO [#B] Headline with todo status & priority * DONE Headline with TODO status -* [#A] Headline with tags & priority :foo:bar: +:PROPERTIES: +:Note: property drawers are not exported as html like other drawers +:END: +the *content* +* [#A] Headline with tags & priority :foo:bar: +Still outside the drawer +:DRAWERNAME: +This is inside the drawer +:END: +Still outside the drawer diff --git a/org/testdata/misc.html b/org/testdata/misc.html index 81e0a32..550e903 100644 --- a/org/testdata/misc.html +++ b/org/testdata/misc.html @@ -46,8 +46,17 @@ src block * Simple Headline * TODO [#B] Headline with todo status & priority * DONE Headline with TODO status -* [#A] Headline with tags & priority :foo:bar: +:PROPERTIES: +:Note: property drawers are not exported as html like other drawers +:END: +the *content* +* [#A] Headline with tags & priority :foo:bar: +Still outside the drawer +:DRAWERNAME: +This is inside the drawer +:END: +Still outside the drawer