From ba3cf9f9488dfccac8f3c81f6bd5d0bbd6b96188 Mon Sep 17 00:00:00 2001 From: Niklas Fasching Date: Mon, 17 Dec 2018 01:13:55 +0100 Subject: [PATCH] Add support for descriptive lists --- README.org | 4 +--- etc/style.css | 5 +++++ org/html.go | 22 ++++++++++++++----- org/list.go | 48 +++++++++++++++++++++++++++++++---------- org/org.go | 18 ++++++++++++++++ org/testdata/lists.html | 23 ++++++++++++++++++++ org/testdata/lists.org | 8 +++++++ 7 files changed, 109 insertions(+), 19 deletions(-) diff --git a/README.org b/README.org index 2e7f724..981e6f4 100644 --- a/README.org +++ b/README.org @@ -2,9 +2,7 @@ A basic org-mode parser in go. Take a look at [[https://niklasfasching.github.io/go-org/][github pages]] for some examples and an online org->html demo. * next -- lists - definition lists - checkboxes +- list checkboxes - more keywords: https://orgmode.org/manual/In_002dbuffer-settings.html * other differences to goorgeous - no headline ids diff --git a/etc/style.css b/etc/style.css index 285816b..9f51912 100644 --- a/etc/style.css +++ b/etc/style.css @@ -108,3 +108,8 @@ figcaption { .align-left { text-align: left; } .align-center { text-align: center; } .align-right { text-align: right; } + + +dl { font-family: monospace, monospace; } +dl > dt { font-weight: bold; } +dl > dd { margin: -1em 0 1em 1em; } diff --git a/org/html.go b/org/html.go index bbdcf4f..67fe739 100644 --- a/org/html.go +++ b/org/html.go @@ -26,11 +26,9 @@ var emphasisTags = map[string][]string{ } var listTags = map[string][]string{ - "+": []string{""}, - "-": []string{""}, - "*": []string{""}, - "number": []string{"
    ", "
"}, - "letter": []string{"
    ", "
"}, + "unordered": []string{""}, + "ordered": []string{"
    ", "
"}, + "descriptive": []string{"
", "
"}, } func NewHTMLWriter() *HTMLWriter { @@ -84,6 +82,8 @@ func (w *HTMLWriter) writeNodes(ns ...Node) { w.writeList(n) case ListItem: w.writeListItem(n) + case DescriptiveListItem: + w.writeDescriptiveListItem(n) case Table: w.writeTable(n) @@ -260,6 +260,18 @@ func (w *HTMLWriter) writeListItem(li ListItem) { w.WriteString("\n") } +func (w *HTMLWriter) writeDescriptiveListItem(di DescriptiveListItem) { + w.WriteString("
\n") + if len(di.Term) != 0 { + w.writeNodes(di.Term...) + } else { + w.WriteString("?") + } + w.WriteString("
\n") + w.writeNodes(di.Details...) + w.WriteString("
\n") +} + func (w *HTMLWriter) writeParagraph(p Paragraph) { if isEmptyLineParagraph(p) { return diff --git a/org/list.go b/org/list.go index 3ff2214..853a1b6 100644 --- a/org/list.go +++ b/org/list.go @@ -17,8 +17,15 @@ type ListItem struct { Children []Node } +type DescriptiveListItem struct { + Bullet string + Term []Node + Details []Node +} + var unorderedListRegexp = regexp.MustCompile(`^(\s*)([-]|[+]|[*])\s(.*)`) var orderedListRegexp = regexp.MustCompile(`^(\s*)(([0-9]+|[a-zA-Z])[.)])\s+(.*)`) +var descriptiveListItemRegexp = regexp.MustCompile(`\s::(\s|$)`) func lexList(line string) (token, bool) { if m := unorderedListRegexp.FindStringSubmatch(line); m != nil { @@ -39,12 +46,12 @@ func stopIndentBelow(t token, minIndent int) bool { func listKind(t token) string { switch bullet := t.matches[2]; { + case descriptiveListItemRegexp.MatchString(t.content): + return "descriptive" case bullet == "*" || bullet == "+" || bullet == "-": - return bullet - case unicode.IsLetter(rune(bullet[0])): - return "letter" - case unicode.IsDigit(rune(bullet[0])): - return "number" + return "unordered" + case unicode.IsLetter(rune(bullet[0])), unicode.IsDigit(rune(bullet[0])): + return "ordered" default: panic(fmt.Sprintf("bad list bullet '%s': %#v", bullet, t)) } @@ -52,20 +59,36 @@ func listKind(t token) string { func (d *Document) parseList(i int, parentStop stopFn) (int, Node) { start, lvl := i, d.tokens[i].lvl - list := List{Kind: listKind(d.tokens[i])} - for !parentStop(d, i) && d.tokens[i].lvl == lvl && isListToken(d.tokens[i]) && listKind(d.tokens[i]) == list.Kind { - consumed, node := d.parseListItem(i, parentStop) + stop := func(*Document, int) bool { + if parentStop(d, i) || d.tokens[i].lvl != lvl || !isListToken(d.tokens[i]) { + return true + } + if list.Kind == "descriptive" { + return false + } + return listKind(d.tokens[i]) != list.Kind + } + for !stop(d, i) { + consumed, node := d.parseListItem(list, i, parentStop) i += consumed list.Items = append(list.Items, node) } return i - start, list } -func (d *Document) parseListItem(i int, parentStop stopFn) (int, Node) { +func (d *Document) parseListItem(l List, i int, parentStop stopFn) (int, Node) { start, nodes, bullet := i, []Node{}, d.tokens[i].matches[2] - minIndent := d.tokens[i].lvl + len(bullet) - d.tokens[i] = tokenize(strings.Repeat(" ", minIndent) + d.tokens[i].content) + minIndent, dterm, content := d.tokens[i].lvl+len(bullet), "", d.tokens[i].content + if l.Kind == "descriptive" { + if m := descriptiveListItemRegexp.FindStringIndex(content); m != nil { + dterm, content = content[:m[0]], content[m[1]:] + if len(content) == 0 { + content = "\n" + } + } + } + d.tokens[i] = tokenize(strings.Repeat(" ", minIndent) + content) stop := func(d *Document, i int) bool { if parentStop(d, i) { return true @@ -78,5 +101,8 @@ func (d *Document) parseListItem(i int, parentStop stopFn) (int, Node) { i += consumed nodes = append(nodes, node) } + if l.Kind == "descriptive" { + return i - start, DescriptiveListItem{bullet, d.parseInline(dterm), nodes} + } return i - start, ListItem{bullet, nodes} } diff --git a/org/org.go b/org/org.go index 29894ca..224313c 100644 --- a/org/org.go +++ b/org/org.go @@ -72,6 +72,8 @@ func (w *OrgWriter) writeNodes(ns ...Node) { w.writeList(n) case ListItem: w.writeListItem(n) + case DescriptiveListItem: + w.writeDescriptiveListItem(n) case Table: w.writeTable(n) @@ -228,6 +230,22 @@ func (w *OrgWriter) writeListItem(li ListItem) { w.WriteString(strings.TrimPrefix(liWriter.String(), liWriter.indent)) } +func (w *OrgWriter) writeDescriptiveListItem(di DescriptiveListItem) { + diWriter := w.emptyClone() + diWriter.indent = w.indent + strings.Repeat(" ", len(di.Bullet)+1) + diWriter.writeNodes(di.Details...) + details := strings.TrimPrefix(diWriter.String(), diWriter.indent) + w.WriteString(w.indent + di.Bullet) + if len(di.Term) != 0 { + w.WriteString(" " + w.nodesAsString(di.Term...) + " ::") + } + if len(details) > 0 && details[0] == '\n' { + w.WriteString(details) + } else { + w.WriteString(" " + details) + } +} + func (w *OrgWriter) writeTable(t Table) { for _, row := range t.Rows { w.WriteString(w.indent) diff --git a/org/testdata/lists.html b/org/testdata/lists.html index fdc0d24..e93a7bf 100644 --- a/org/testdata/lists.html +++ b/org/testdata/lists.html @@ -92,3 +92,26 @@ that spans multiple lines +

+descriptive lists +

+
+
+term
+

+description +

+
+
+?
+

+without term +

+
+
+term
+

+description +

+
+
diff --git a/org/testdata/lists.org b/org/testdata/lists.org index 8c16479..bf96925 100644 --- a/org/testdata/lists.org +++ b/org/testdata/lists.org @@ -21,3 +21,11 @@ : with an example : : that spans multiple lines + + +descriptive lists + +- term :: description +- without term +- term :: + description