1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
package org
import (
"fmt"
"regexp"
"strings"
"unicode"
)
type Outline struct {
*Section
last *Section
count int
}
type Section struct {
Headline *Headline
Parent *Section
Children []*Section
}
type Headline struct {
Index int
Lvl int
Status string
IsComment bool
Priority string
Properties *PropertyDrawer
Title []Node
Tags []string
Children []Node
}
var headlineRegexp = regexp.MustCompile(`^([*]+)\s+(.*)`)
var tagRegexp = regexp.MustCompile(`(.*?)\s+(:[\p{L}0-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) parseHeadline(i int, parentStop stopFn) (int, Node) {
t, headline := d.tokens[i], Headline{}
headline.Lvl = len(t.matches[1])
text := t.content
todoKeywords := trimFastTags(
strings.FieldsFunc(d.Get("TODO"), func(r rune) bool { return unicode.IsSpace(r) || r == '|' }),
)
for _, k := range 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) >= 4 && text[0:2] == "[#" && strings.Contains("ABC", text[2:3]) && text[3] == ']' {
headline.Priority = text[2:3]
text = strings.TrimSpace(text[4:])
}
if strings.HasPrefix(text, "COMMENT ") {
headline.IsComment = true
text = strings.TrimPrefix(text, "COMMENT ")
}
if m := tagRegexp.FindStringSubmatch(text); m != nil {
text = m[1]
headline.Tags = strings.FieldsFunc(m[2], func(r rune) bool { return r == ':' })
}
headline.Index = d.addHeadline(&headline)
headline.Title = d.parseInline(text)
stop := func(d *Document, i int) bool {
return parentStop(d, i) || d.tokens[i].kind == "headline" && len(d.tokens[i].matches[1]) <= headline.Lvl
}
consumed, nodes := d.parseMany(i+1, stop)
if len(nodes) > 0 {
if d, ok := nodes[0].(PropertyDrawer); ok {
headline.Properties = &d
nodes = nodes[1:]
}
}
headline.Children = nodes
return consumed + 1, headline
}
func trimFastTags(tags []string) []string {
trimmedTags := make([]string, len(tags))
for i, t := range tags {
lParen := strings.LastIndex(t, "(")
rParen := strings.LastIndex(t, ")")
end := len(t) - 1
if lParen == end-2 && rParen == end {
trimmedTags[i] = t[:end-2]
} else {
trimmedTags[i] = t
}
}
return trimmedTags
}
func (h Headline) ID() string {
if customID, ok := h.Properties.Get("CUSTOM_ID"); ok {
return customID
}
return fmt.Sprintf("headline-%d", h.Index)
}
func (h Headline) IsExcluded(d *Document) bool {
if h.IsComment {
return true
}
for _, excludedTag := range strings.Fields(d.Get("EXCLUDE_TAGS")) {
for _, tag := range h.Tags {
if tag == excludedTag {
return true
}
}
}
return false
}
func (parent *Section) add(current *Section) {
if parent.Headline == nil || parent.Headline.Lvl < current.Headline.Lvl {
parent.Children = append(parent.Children, current)
current.Parent = parent
} else {
parent.Parent.add(current)
}
}
func (n Headline) String() string { return String(n) }
|