You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

headline.go 2.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package org
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "unicode"
  7. )
  8. type Outline struct {
  9. *Section
  10. last *Section
  11. count int
  12. }
  13. type Section struct {
  14. Headline *Headline
  15. Parent *Section
  16. Children []*Section
  17. }
  18. type Headline struct {
  19. Index int
  20. Lvl int
  21. Status string
  22. Priority string
  23. Properties *PropertyDrawer
  24. Title []Node
  25. Tags []string
  26. Children []Node
  27. }
  28. var headlineRegexp = regexp.MustCompile(`^([*]+)\s+(.*)`)
  29. var tagRegexp = regexp.MustCompile(`(.*?)\s+(:[A-Za-z0-9_@#%:]+:\s*$)`)
  30. func lexHeadline(line string) (token, bool) {
  31. if m := headlineRegexp.FindStringSubmatch(line); m != nil {
  32. return token{"headline", len(m[1]), m[2], m}, true
  33. }
  34. return nilToken, false
  35. }
  36. func (d *Document) parseHeadline(i int, parentStop stopFn) (int, Node) {
  37. t, headline := d.tokens[i], Headline{}
  38. headline.Lvl = t.lvl
  39. headline.Index = d.addHeadline(&headline)
  40. text := t.content
  41. todoKeywords := strings.FieldsFunc(d.Get("TODO"), func(r rune) bool { return unicode.IsSpace(r) || r == '|' })
  42. for _, k := range todoKeywords {
  43. if strings.HasPrefix(text, k) && len(text) > len(k) && unicode.IsSpace(rune(text[len(k)])) {
  44. headline.Status = k
  45. text = text[len(k)+1:]
  46. break
  47. }
  48. }
  49. if len(text) >= 4 && text[0:2] == "[#" && strings.Contains("ABC", text[2:3]) && text[3] == ']' {
  50. headline.Priority = text[2:3]
  51. text = strings.TrimSpace(text[4:])
  52. }
  53. if m := tagRegexp.FindStringSubmatch(text); m != nil {
  54. text = m[1]
  55. headline.Tags = strings.FieldsFunc(m[2], func(r rune) bool { return r == ':' })
  56. }
  57. headline.Title = d.parseInline(text)
  58. stop := func(d *Document, i int) bool {
  59. return parentStop(d, i) || d.tokens[i].kind == "headline" && d.tokens[i].lvl <= headline.Lvl
  60. }
  61. consumed, nodes := d.parseMany(i+1, stop)
  62. if len(nodes) > 0 {
  63. if d, ok := nodes[0].(PropertyDrawer); ok {
  64. headline.Properties = &d
  65. nodes = nodes[1:]
  66. }
  67. }
  68. headline.Children = nodes
  69. return consumed + 1, headline
  70. }
  71. func (h Headline) ID() string {
  72. if customID, ok := h.Properties.Get("CUSTOM_ID"); ok {
  73. return customID
  74. }
  75. return fmt.Sprintf("headline-%d", h.Index)
  76. }
  77. func (parent *Section) add(current *Section) {
  78. if parent.Headline == nil || parent.Headline.Lvl < current.Headline.Lvl {
  79. parent.Children = append(parent.Children, current)
  80. current.Parent = parent
  81. } else {
  82. parent.Parent.add(current)
  83. }
  84. }
  85. func (n Headline) String() string { return orgWriter.WriteNodesAsString(n) }