Until now the footnotes section was parsed but not included in the resulting AST - his required rebuilding it in the OrgWriter. It feels cleaner to include it in the AST and only exclude it in the export
72 lines
1.8 KiB
Go
72 lines
1.8 KiB
Go
package org
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
type Headline struct {
|
|
Lvl int
|
|
Status string
|
|
Priority string
|
|
Properties Node
|
|
Title []Node
|
|
Tags []string
|
|
Children []Node
|
|
}
|
|
|
|
var headlineRegexp = regexp.MustCompile(`^([*]+)\s+(.*)`)
|
|
var tagRegexp = regexp.MustCompile(`(.*?)\s*(:[A-Za-z0-9@#%:]+:\s*$)`)
|
|
|
|
func lexHeadline(line string) (token, bool) {
|
|
if m := headlineRegexp.FindStringSubmatch(line); m != nil {
|
|
return token{"headline", 0, m[2], m}, true
|
|
}
|
|
return nilToken, false
|
|
}
|
|
|
|
func (d *Document) todoKeywords() []string {
|
|
return strings.FieldsFunc(d.Get("TODO"), func(r rune) bool {
|
|
return unicode.IsSpace(r) || r == '|'
|
|
})
|
|
}
|
|
|
|
func (d *Document) parseHeadline(i int, parentStop stopFn) (int, Node) {
|
|
t, headline := d.tokens[i], Headline{}
|
|
headline.Lvl = len(t.matches[1])
|
|
text := t.content
|
|
|
|
for _, k := range d.todoKeywords() {
|
|
if strings.HasPrefix(text, k) && len(text) > len(k) && unicode.IsSpace(rune(text[len(k)])) {
|
|
headline.Status = k
|
|
text = text[len(k)+1:]
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(text) >= 3 && text[0:2] == "[#" && strings.Contains("ABC", text[2:3]) && text[3] == ']' {
|
|
headline.Priority = text[2:3]
|
|
text = strings.TrimSpace(text[4:])
|
|
}
|
|
|
|
if m := tagRegexp.FindStringSubmatch(text); m != nil {
|
|
text = m[1]
|
|
headline.Tags = strings.FieldsFunc(m[2], func(r rune) bool { return r == ':' })
|
|
}
|
|
|
|
headline.Title = d.parseInline(text)
|
|
|
|
stop := func(d *Document, i int) bool {
|
|
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
|
|
return consumed + 1, headline
|
|
}
|