go-org-orgwiki/org/headline.go
Niklas Fasching 9a0a9c11eb Export WriteNodesAsString on writer interface
WriteNodesAsString is simple enough to implement but exposing it is helpful in
the implementation of extending writers and we don't aim to keep writer a small
interface so let's expose it.
2019-11-02 23:44:16 +01:00

101 lines
2.4 KiB
Go

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
Priority string
Properties *PropertyDrawer
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", len(m[1]), 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 = t.lvl
headline.Index = d.addHeadline(&headline)
text := t.content
todoKeywords := 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 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].(PropertyDrawer); ok {
headline.Properties = &d
nodes = nodes[1:]
}
}
headline.Children = nodes
return consumed + 1, headline
}
func (h Headline) ID() string {
if customID, ok := h.Properties.Get("CUSTOM_ID"); ok {
return customID
}
return fmt.Sprintf("headline-%d", h.Index)
}
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 orgWriter.WriteNodesAsString(n) }