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.

org_writer.go 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. package org
  2. import (
  3. "fmt"
  4. "strings"
  5. "unicode"
  6. "unicode/utf8"
  7. )
  8. // OrgWriter export an org document into pretty printed org document.
  9. type OrgWriter struct {
  10. ExtendingWriter Writer
  11. TagsColumn int
  12. strings.Builder
  13. indent string
  14. }
  15. var emphasisOrgBorders = map[string][]string{
  16. "_": []string{"_", "_"},
  17. "*": []string{"*", "*"},
  18. "/": []string{"/", "/"},
  19. "+": []string{"+", "+"},
  20. "~": []string{"~", "~"},
  21. "=": []string{"=", "="},
  22. "_{}": []string{"_{", "}"},
  23. "^{}": []string{"^{", "}"},
  24. }
  25. func NewOrgWriter() *OrgWriter {
  26. return &OrgWriter{
  27. TagsColumn: 77,
  28. }
  29. }
  30. func (w *OrgWriter) WriterWithExtensions() Writer {
  31. if w.ExtendingWriter != nil {
  32. return w.ExtendingWriter
  33. }
  34. return w
  35. }
  36. func (w *OrgWriter) Before(d *Document) {}
  37. func (w *OrgWriter) After(d *Document) {}
  38. func (w *OrgWriter) WriteNodesAsString(nodes ...Node) string {
  39. builder := w.Builder
  40. w.Builder = strings.Builder{}
  41. WriteNodes(w, nodes...)
  42. out := w.String()
  43. w.Builder = builder
  44. return out
  45. }
  46. func (w *OrgWriter) WriteHeadline(h Headline) {
  47. start := w.Len()
  48. w.WriteString(strings.Repeat("*", h.Lvl))
  49. if h.Status != "" {
  50. w.WriteString(" " + h.Status)
  51. }
  52. if h.Priority != "" {
  53. w.WriteString(" [#" + h.Priority + "]")
  54. }
  55. w.WriteString(" ")
  56. WriteNodes(w, h.Title...)
  57. if len(h.Tags) != 0 {
  58. tString := ":" + strings.Join(h.Tags, ":") + ":"
  59. if n := w.TagsColumn - len(tString) - (w.Len() - start); n > 0 {
  60. w.WriteString(strings.Repeat(" ", n) + tString)
  61. } else {
  62. w.WriteString(" " + tString)
  63. }
  64. }
  65. w.WriteString("\n")
  66. if len(h.Children) != 0 {
  67. w.WriteString(w.indent)
  68. }
  69. if h.Properties != nil {
  70. WriteNodes(w, *h.Properties)
  71. }
  72. WriteNodes(w, h.Children...)
  73. }
  74. func (w *OrgWriter) WriteBlock(b Block) {
  75. w.WriteString(w.indent + "#+BEGIN_" + b.Name)
  76. if len(b.Parameters) != 0 {
  77. w.WriteString(" " + strings.Join(b.Parameters, " "))
  78. }
  79. w.WriteString("\n")
  80. if isRawTextBlock(b.Name) {
  81. w.WriteString(w.indent)
  82. }
  83. WriteNodes(w, b.Children...)
  84. if !isRawTextBlock(b.Name) {
  85. w.WriteString(w.indent)
  86. }
  87. w.WriteString("#+END_" + b.Name + "\n")
  88. }
  89. func (w *OrgWriter) WriteDrawer(d Drawer) {
  90. w.WriteString(w.indent + ":" + d.Name + ":\n")
  91. WriteNodes(w, d.Children...)
  92. w.WriteString(w.indent + ":END:\n")
  93. }
  94. func (w *OrgWriter) WritePropertyDrawer(d PropertyDrawer) {
  95. w.WriteString(":PROPERTIES:\n")
  96. for _, kvPair := range d.Properties {
  97. k, v := kvPair[0], kvPair[1]
  98. if v != "" {
  99. v = " " + v
  100. }
  101. w.WriteString(fmt.Sprintf(":%s:%s\n", k, v))
  102. }
  103. w.WriteString(":END:\n")
  104. }
  105. func (w *OrgWriter) WriteFootnoteDefinition(f FootnoteDefinition) {
  106. w.WriteString(fmt.Sprintf("[fn:%s]", f.Name))
  107. content := w.WriteNodesAsString(f.Children...)
  108. if content != "" && !unicode.IsSpace(rune(content[0])) {
  109. w.WriteString(" ")
  110. }
  111. w.WriteString(content)
  112. }
  113. func (w *OrgWriter) WriteParagraph(p Paragraph) {
  114. content := w.WriteNodesAsString(p.Children...)
  115. if len(content) > 0 && content[0] != '\n' {
  116. w.WriteString(w.indent)
  117. }
  118. w.WriteString(content + "\n")
  119. }
  120. func (w *OrgWriter) WriteExample(e Example) {
  121. for _, n := range e.Children {
  122. w.WriteString(w.indent + ":")
  123. if content := w.WriteNodesAsString(n); content != "" {
  124. w.WriteString(" " + content)
  125. }
  126. w.WriteString("\n")
  127. }
  128. }
  129. func (w *OrgWriter) WriteKeyword(k Keyword) {
  130. w.WriteString(w.indent + "#+" + k.Key + ":")
  131. if k.Value != "" {
  132. w.WriteString(" " + k.Value)
  133. }
  134. w.WriteString("\n")
  135. }
  136. func (w *OrgWriter) WriteInclude(i Include) {
  137. w.WriteKeyword(i.Keyword)
  138. }
  139. func (w *OrgWriter) WriteNodeWithMeta(n NodeWithMeta) {
  140. for _, ns := range n.Meta.Caption {
  141. w.WriteString("#+CAPTION: ")
  142. WriteNodes(w, ns...)
  143. w.WriteString("\n")
  144. }
  145. for _, attributes := range n.Meta.HTMLAttributes {
  146. w.WriteString("#+ATTR_HTML: ")
  147. w.WriteString(strings.Join(attributes, " ") + "\n")
  148. }
  149. WriteNodes(w, n.Node)
  150. }
  151. func (w *OrgWriter) WriteNodeWithName(n NodeWithName) {
  152. w.WriteString(fmt.Sprintf("#+NAME: %s\n", n.Name))
  153. WriteNodes(w, n.Node)
  154. }
  155. func (w *OrgWriter) WriteComment(c Comment) {
  156. w.WriteString(w.indent + "#" + c.Content + "\n")
  157. }
  158. func (w *OrgWriter) WriteList(l List) { WriteNodes(w, l.Items...) }
  159. func (w *OrgWriter) WriteListItem(li ListItem) {
  160. originalBuilder, originalIndent := w.Builder, w.indent
  161. w.Builder, w.indent = strings.Builder{}, w.indent+strings.Repeat(" ", len(li.Bullet)+1)
  162. WriteNodes(w, li.Children...)
  163. content := strings.TrimPrefix(w.String(), w.indent)
  164. w.Builder, w.indent = originalBuilder, originalIndent
  165. w.WriteString(w.indent + li.Bullet)
  166. if li.Status != "" {
  167. w.WriteString(fmt.Sprintf(" [%s]", li.Status))
  168. }
  169. if len(content) > 0 && content[0] == '\n' {
  170. w.WriteString(content)
  171. } else {
  172. w.WriteString(" " + content)
  173. }
  174. }
  175. func (w *OrgWriter) WriteDescriptiveListItem(di DescriptiveListItem) {
  176. w.WriteString(w.indent + di.Bullet)
  177. if di.Status != "" {
  178. w.WriteString(fmt.Sprintf(" [%s]", di.Status))
  179. }
  180. indent := w.indent + strings.Repeat(" ", len(di.Bullet)+1)
  181. if len(di.Term) != 0 {
  182. term := w.WriteNodesAsString(di.Term...)
  183. w.WriteString(" " + term + " ::")
  184. indent = indent + strings.Repeat(" ", len(term)+4)
  185. }
  186. originalBuilder, originalIndent := w.Builder, w.indent
  187. w.Builder, w.indent = strings.Builder{}, indent
  188. WriteNodes(w, di.Details...)
  189. details := strings.TrimPrefix(w.String(), w.indent)
  190. w.Builder, w.indent = originalBuilder, originalIndent
  191. if len(details) > 0 && details[0] == '\n' {
  192. w.WriteString(details)
  193. } else {
  194. w.WriteString(" " + details)
  195. }
  196. }
  197. func (w *OrgWriter) WriteTable(t Table) {
  198. for _, row := range t.Rows {
  199. w.WriteString(w.indent)
  200. if len(row.Columns) == 0 {
  201. w.WriteString(`|`)
  202. for i := 0; i < len(t.ColumnInfos); i++ {
  203. w.WriteString(strings.Repeat("-", t.ColumnInfos[i].Len+2))
  204. if i < len(t.ColumnInfos)-1 {
  205. w.WriteString("+")
  206. }
  207. }
  208. w.WriteString(`|`)
  209. } else {
  210. w.WriteString(`|`)
  211. for _, column := range row.Columns {
  212. w.WriteString(` `)
  213. content := w.WriteNodesAsString(column.Children...)
  214. if content == "" {
  215. content = " "
  216. }
  217. n := column.Len - utf8.RuneCountInString(content)
  218. if n < 0 {
  219. n = 0
  220. }
  221. if column.Align == "center" {
  222. if n%2 != 0 {
  223. w.WriteString(" ")
  224. }
  225. w.WriteString(strings.Repeat(" ", n/2) + content + strings.Repeat(" ", n/2))
  226. } else if column.Align == "right" {
  227. w.WriteString(strings.Repeat(" ", n) + content)
  228. } else {
  229. w.WriteString(content + strings.Repeat(" ", n))
  230. }
  231. w.WriteString(` |`)
  232. }
  233. }
  234. w.WriteString("\n")
  235. }
  236. }
  237. func (w *OrgWriter) WriteHorizontalRule(hr HorizontalRule) {
  238. w.WriteString(w.indent + "-----\n")
  239. }
  240. func (w *OrgWriter) WriteText(t Text) { w.WriteString(t.Content) }
  241. func (w *OrgWriter) WriteEmphasis(e Emphasis) {
  242. borders, ok := emphasisOrgBorders[e.Kind]
  243. if !ok {
  244. panic(fmt.Sprintf("bad emphasis %#v", e))
  245. }
  246. w.WriteString(borders[0])
  247. WriteNodes(w, e.Content...)
  248. w.WriteString(borders[1])
  249. }
  250. func (w *OrgWriter) WriteLatexFragment(l LatexFragment) {
  251. w.WriteString(l.OpeningPair)
  252. WriteNodes(w, l.Content...)
  253. w.WriteString(l.ClosingPair)
  254. }
  255. func (w *OrgWriter) WriteStatisticToken(s StatisticToken) {
  256. w.WriteString(fmt.Sprintf("[%s]", s.Content))
  257. }
  258. func (w *OrgWriter) WriteLineBreak(l LineBreak) {
  259. w.WriteString(strings.Repeat("\n"+w.indent, l.Count))
  260. }
  261. func (w *OrgWriter) WriteExplicitLineBreak(l ExplicitLineBreak) {
  262. w.WriteString(`\\` + "\n" + w.indent)
  263. }
  264. func (w *OrgWriter) WriteTimestamp(t Timestamp) {
  265. w.WriteString("<")
  266. if t.IsDate {
  267. w.WriteString(t.Time.Format(datestampFormat))
  268. } else {
  269. w.WriteString(t.Time.Format(timestampFormat))
  270. }
  271. if t.Interval != "" {
  272. w.WriteString(" " + t.Interval)
  273. }
  274. w.WriteString(">")
  275. }
  276. func (w *OrgWriter) WriteFootnoteLink(l FootnoteLink) {
  277. w.WriteString("[fn:" + l.Name)
  278. if l.Definition != nil {
  279. w.WriteString(":")
  280. WriteNodes(w, l.Definition.Children[0].(Paragraph).Children...)
  281. }
  282. w.WriteString("]")
  283. }
  284. func (w *OrgWriter) WriteRegularLink(l RegularLink) {
  285. if l.AutoLink {
  286. w.WriteString(l.URL)
  287. } else if l.Description == nil {
  288. w.WriteString(fmt.Sprintf("[[%s]]", l.URL))
  289. } else {
  290. w.WriteString(fmt.Sprintf("[[%s][%s]]", l.URL, w.WriteNodesAsString(l.Description...)))
  291. }
  292. }