diff --git a/README.org b/README.org
index 7d8bec7..c604b18 100644
--- a/README.org
+++ b/README.org
@@ -3,9 +3,6 @@ A basic org-mode parser in go
- have a org-mode AST to play around with building an org-mode language server
- hopefully add reasonable org-mode support to hugo - sadly [[https://github.com/chaseadamsio/goorgeous][goorgeous]] is broken & abandoned
* next
-- handle #+RESULTS: raw and stuff
-- hugo frontmatter - see https://gohugo.io/content-management/front-matter/
-- captions: images, tables & blocks
*** TODO [[https://github.com/chaseadamsio/goorgeous/issues/72][#72:]] Support for #+ATTR_HTML
*** TODO [[https://github.com/chaseadamsio/goorgeous/issues/46][#46]]: Support for symbols like ndash and mdash
- see org-entities replacement: see org-entities-help
@@ -15,8 +12,11 @@ A basic org-mode parser in go
- see https://orgmode.org/manual/Include-files.html
*** TODO [[https://github.com/chaseadamsio/goorgeous/issues/33][#33]]: Wrong output when mixing html with org-mode
* later
+- hugo frontmatter - see https://gohugo.io/content-management/front-matter/ -> actually seems to be handled by hugo itself
+- handle #+RESULTS: raw and stuff
- affiliated keywords: see org-element.el - org-element-affiliated-keywords
- keywords: support both multi (e.g. LINK, TODO) & normal (e.g. AUTHOR, TITLE) keywords
+ https://orgmode.org/manual/In_002dbuffer-settings.html
- links based on #+LINK
- table colgroups https://orgmode.org/worg/org-tutorials/tables.html
- table pretty printing
diff --git a/org/html.go b/org/html.go
index 0bb3879..db3ee6b 100644
--- a/org/html.go
+++ b/org/html.go
@@ -8,6 +8,7 @@ import (
type HTMLWriter struct {
stringBuilder
+ document *Document
HighlightCodeBlock func(source, lang string) string
}
@@ -44,7 +45,10 @@ func (w *HTMLWriter) emptyClone() *HTMLWriter {
return &wcopy
}
-func (w *HTMLWriter) before(d *Document) {}
+func (w *HTMLWriter) before(d *Document) {
+ w.document = d
+}
+
func (w *HTMLWriter) after(d *Document) {
w.writeFootnotes(d)
}
@@ -54,6 +58,8 @@ func (w *HTMLWriter) writeNodes(ns ...Node) {
switch n := n.(type) {
case Keyword, Comment:
continue
+ case NodeWithMeta:
+ w.writeNodeWithMeta(n)
case Headline:
w.writeHeadline(n)
case Block:
@@ -242,6 +248,19 @@ func (w *HTMLWriter) writeHorizontalRule(h HorizontalRule) {
w.WriteString("
\n")
}
+func (w *HTMLWriter) writeNodeWithMeta(m NodeWithMeta) {
+ nodeW := w.emptyClone()
+ nodeW.writeNodes(m.Node)
+ nodeString := nodeW.String()
+ if rawCaption, ok := m.Meta["CAPTION"]; ok {
+ nodes, captionW := w.document.parseInline(rawCaption), w.emptyClone()
+ captionW.writeNodes(nodes...)
+ caption := `` + "\n" + captionW.String() + "\n
\n"
+ nodeString = `` + "\n" + nodeString + caption + `
` + "\n"
+ }
+ w.WriteString(nodeString)
+}
+
func (w *HTMLWriter) writeTable(t Table) {
w.WriteString("\n")
w.writeNodes(t.Header)
diff --git a/org/keyword.go b/org/keyword.go
index 3eb8c65..68359c0 100644
--- a/org/keyword.go
+++ b/org/keyword.go
@@ -10,10 +10,16 @@ type Keyword struct {
Value string
}
+type NodeWithMeta struct {
+ Node Node
+ Meta map[string]string
+}
+
type Comment struct{ Content string }
var keywordRegexp = regexp.MustCompile(`^(\s*)#\+([^:]+):\s(.*)`)
var commentRegexp = regexp.MustCompile(`^(\s*)#(.*)`)
+var affiliatedKeywordRegexp = regexp.MustCompile(`^(CAPTION)$`)
func lexKeywordOrComment(line string) (token, bool) {
if m := keywordRegexp.FindStringSubmatch(line); m != nil {
@@ -25,12 +31,55 @@ func lexKeywordOrComment(line string) (token, bool) {
}
func (d *Document) parseKeyword(i int, stop stopFn) (int, Node) {
- t := d.tokens[i]
- k, v := t.matches[2], t.matches[3]
- d.BufferSettings[k] = strings.Join([]string{d.BufferSettings[k], v}, "\n")
- return 1, Keyword{k, v}
+ k := parseKeyword(d.tokens[i])
+ if affiliatedKeywordRegexp.MatchString(k.Key) {
+ consumed, node := d.parseAffiliated(i, stop)
+ if consumed != 0 {
+ return consumed, node
+ }
+ } else {
+ d.BufferSettings[k.Key] = strings.Join([]string{d.BufferSettings[k.Key], k.Value}, "\n")
+ }
+ return 1, k
}
func (d *Document) parseComment(i int, stop stopFn) (int, Node) {
return 1, Comment{d.tokens[i].content}
}
+
+func (d *Document) parseAffiliated(i int, stop stopFn) (int, Node) {
+ start, meta := i, map[string]string{}
+ for ; !stop(d, i) && d.tokens[i].kind == "keyword"; i++ {
+ k := parseKeyword(d.tokens[i])
+ if !affiliatedKeywordRegexp.MatchString(k.Key) {
+ return 0, nil
+ }
+ if value, ok := meta[k.Key]; ok {
+ meta[k.Key] = value + " " + k.Value
+ } else {
+ meta[k.Key] = k.Value
+ }
+ }
+ if stop(d, i) {
+ return 0, nil
+ }
+ consumed, node := 0, (Node)(nil)
+ if t := d.tokens[i]; t.kind == "text" {
+ if nodes := d.parseInline(t.content); len(nodes) == 1 && isImageOrVideoLink(nodes[0]) {
+ consumed, node = 1, Line{nodes[:1]}
+ }
+ } else {
+ consumed, node = d.parseOne(i, stop)
+ }
+ if consumed == 0 || node == nil {
+ return 0, nil
+ }
+ i += consumed
+ return i - start, NodeWithMeta{node, meta}
+}
+
+func parseKeyword(t token) Keyword {
+ k, v := t.matches[2], t.matches[3]
+ k = strings.ToUpper(k)
+ return Keyword{k, v}
+}
diff --git a/org/org.go b/org/org.go
index 4b77b43..752412d 100644
--- a/org/org.go
+++ b/org/org.go
@@ -48,6 +48,8 @@ func (w *OrgWriter) writeNodes(ns ...Node) {
w.writeComment(n)
case Keyword:
w.writeKeyword(n)
+ case NodeWithMeta:
+ w.writeNodeWithMeta(n)
case Headline:
w.writeHeadline(n)
case Block:
@@ -164,6 +166,13 @@ func (w *OrgWriter) writeKeyword(k Keyword) {
w.WriteString(w.indent + fmt.Sprintf("#+%s: %s\n", k.Key, k.Value))
}
+func (w *OrgWriter) writeNodeWithMeta(m NodeWithMeta) {
+ for k, v := range m.Meta {
+ w.writeNodes(Keyword{k, v})
+ }
+ w.writeNodes(m.Node)
+}
+
func (w *OrgWriter) writeComment(c Comment) {
w.WriteString(w.indent + "#" + c.Content)
}
diff --git a/org/testdata/example.html b/org/testdata/example.html
index 902af7c..da65ee7 100644
--- a/org/testdata/example.html
+++ b/org/testdata/example.html
@@ -188,8 +188,35 @@ auto link, i.e. not inside \[[square brackets]\]
<
+Captions
+
+Anything can be captioned. Also captions are not real, correct captions but just a paragraph below the element (bothe wrapped into a div)
+
+
+
+
+captioned soure block
+
+
+
+
+
+captioned link (video in this case)
+
+
+
+note that only that one line is captioned, not the whole paragraph
+
+
+also, normal text lines can't be captioned
+
blocks
+
a source block without a language
and a second line
and a third one
diff --git a/org/testdata/example.org b/org/testdata/example.org
index 2e77050..a7d37f8 100644
--- a/org/testdata/example.org
+++ b/org/testdata/example.org
@@ -63,7 +63,26 @@ this one is cheating a little as tags are ALWAYS printed right aligned to a give
6. regular link to https (image) [[https://www.example.com/my-img.png]]
7. auto link, i.e. not inside =\[[square brackets]\]= https://www.example.com
+** Captions
+
+Anything can be captioned. Also captions are not real, correct captions but just a paragraph below the element (bothe wrapped into a div)
+
+#+CAPTION: captioned soure block
+#+BEGIN_SRC sh
+echo "i have a caption!"
+#+END_SRC
+
+#+CAPTION: captioned link (video in this case)
+[[my-video.mp4]]
+note that only that one line is captioned, not the whole paragraph
+
+#+CAPTION: not happening!
+also, normal text lines can't be captioned
+
+
** blocks
+
+#+CAPTION: bleck!
#+BEGIN_SRC bash
echo a bash source block
#+END_SRC
diff --git a/org/util.go b/org/util.go
index 0ee6a97..eb23f16 100644
--- a/org/util.go
+++ b/org/util.go
@@ -17,3 +17,10 @@ func isEmptyLineParagraph(n Node) bool {
}
return false
}
+
+func isImageOrVideoLink(n Node) bool {
+ if l, ok := n.(RegularLink); ok && l.Kind() == "video" || l.Kind() == "image" {
+ return true
+ }
+ return false
+}