Add support for descriptive lists

This commit is contained in:
Niklas Fasching 2018-12-17 01:13:55 +01:00
parent 376bb3652a
commit ba3cf9f948
7 changed files with 109 additions and 19 deletions

View file

@ -2,9 +2,7 @@
A basic org-mode parser in go. 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. Take a look at [[https://niklasfasching.github.io/go-org/][github pages]] for some examples and an online org->html demo.
* next * next
- lists - list checkboxes
definition lists
checkboxes
- more keywords: https://orgmode.org/manual/In_002dbuffer-settings.html - more keywords: https://orgmode.org/manual/In_002dbuffer-settings.html
* other differences to goorgeous * other differences to goorgeous
- no headline ids - no headline ids

View file

@ -108,3 +108,8 @@ figcaption {
.align-left { text-align: left; } .align-left { text-align: left; }
.align-center { text-align: center; } .align-center { text-align: center; }
.align-right { text-align: right; } .align-right { text-align: right; }
dl { font-family: monospace, monospace; }
dl > dt { font-weight: bold; }
dl > dd { margin: -1em 0 1em 1em; }

View file

@ -26,11 +26,9 @@ var emphasisTags = map[string][]string{
} }
var listTags = map[string][]string{ var listTags = map[string][]string{
"+": []string{"<ul>", "</ul>"}, "unordered": []string{"<ul>", "</ul>"},
"-": []string{"<ul>", "</ul>"}, "ordered": []string{"<ol>", "</ol>"},
"*": []string{"<ul>", "</ul>"}, "descriptive": []string{"<dl>", "</dl>"},
"number": []string{"<ol>", "</ol>"},
"letter": []string{"<ol>", "</ol>"},
} }
func NewHTMLWriter() *HTMLWriter { func NewHTMLWriter() *HTMLWriter {
@ -84,6 +82,8 @@ func (w *HTMLWriter) writeNodes(ns ...Node) {
w.writeList(n) w.writeList(n)
case ListItem: case ListItem:
w.writeListItem(n) w.writeListItem(n)
case DescriptiveListItem:
w.writeDescriptiveListItem(n)
case Table: case Table:
w.writeTable(n) w.writeTable(n)
@ -260,6 +260,18 @@ func (w *HTMLWriter) writeListItem(li ListItem) {
w.WriteString("</li>\n") w.WriteString("</li>\n")
} }
func (w *HTMLWriter) writeDescriptiveListItem(di DescriptiveListItem) {
w.WriteString("<dt>\n")
if len(di.Term) != 0 {
w.writeNodes(di.Term...)
} else {
w.WriteString("?")
}
w.WriteString("<dd>\n")
w.writeNodes(di.Details...)
w.WriteString("<dd>\n")
}
func (w *HTMLWriter) writeParagraph(p Paragraph) { func (w *HTMLWriter) writeParagraph(p Paragraph) {
if isEmptyLineParagraph(p) { if isEmptyLineParagraph(p) {
return return

View file

@ -17,8 +17,15 @@ type ListItem struct {
Children []Node Children []Node
} }
type DescriptiveListItem struct {
Bullet string
Term []Node
Details []Node
}
var unorderedListRegexp = regexp.MustCompile(`^(\s*)([-]|[+]|[*])\s(.*)`) var unorderedListRegexp = regexp.MustCompile(`^(\s*)([-]|[+]|[*])\s(.*)`)
var orderedListRegexp = regexp.MustCompile(`^(\s*)(([0-9]+|[a-zA-Z])[.)])\s+(.*)`) var orderedListRegexp = regexp.MustCompile(`^(\s*)(([0-9]+|[a-zA-Z])[.)])\s+(.*)`)
var descriptiveListItemRegexp = regexp.MustCompile(`\s::(\s|$)`)
func lexList(line string) (token, bool) { func lexList(line string) (token, bool) {
if m := unorderedListRegexp.FindStringSubmatch(line); m != nil { if m := unorderedListRegexp.FindStringSubmatch(line); m != nil {
@ -39,12 +46,12 @@ func stopIndentBelow(t token, minIndent int) bool {
func listKind(t token) string { func listKind(t token) string {
switch bullet := t.matches[2]; { switch bullet := t.matches[2]; {
case descriptiveListItemRegexp.MatchString(t.content):
return "descriptive"
case bullet == "*" || bullet == "+" || bullet == "-": case bullet == "*" || bullet == "+" || bullet == "-":
return bullet return "unordered"
case unicode.IsLetter(rune(bullet[0])): case unicode.IsLetter(rune(bullet[0])), unicode.IsDigit(rune(bullet[0])):
return "letter" return "ordered"
case unicode.IsDigit(rune(bullet[0])):
return "number"
default: default:
panic(fmt.Sprintf("bad list bullet '%s': %#v", bullet, t)) 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) { func (d *Document) parseList(i int, parentStop stopFn) (int, Node) {
start, lvl := i, d.tokens[i].lvl start, lvl := i, d.tokens[i].lvl
list := List{Kind: listKind(d.tokens[i])} 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 { stop := func(*Document, int) bool {
consumed, node := d.parseListItem(i, parentStop) 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 i += consumed
list.Items = append(list.Items, node) list.Items = append(list.Items, node)
} }
return i - start, list 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] start, nodes, bullet := i, []Node{}, d.tokens[i].matches[2]
minIndent := d.tokens[i].lvl + len(bullet) minIndent, dterm, content := d.tokens[i].lvl+len(bullet), "", d.tokens[i].content
d.tokens[i] = tokenize(strings.Repeat(" ", minIndent) + 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 { stop := func(d *Document, i int) bool {
if parentStop(d, i) { if parentStop(d, i) {
return true return true
@ -78,5 +101,8 @@ func (d *Document) parseListItem(i int, parentStop stopFn) (int, Node) {
i += consumed i += consumed
nodes = append(nodes, node) nodes = append(nodes, node)
} }
if l.Kind == "descriptive" {
return i - start, DescriptiveListItem{bullet, d.parseInline(dterm), nodes}
}
return i - start, ListItem{bullet, nodes} return i - start, ListItem{bullet, nodes}
} }

View file

@ -72,6 +72,8 @@ func (w *OrgWriter) writeNodes(ns ...Node) {
w.writeList(n) w.writeList(n)
case ListItem: case ListItem:
w.writeListItem(n) w.writeListItem(n)
case DescriptiveListItem:
w.writeDescriptiveListItem(n)
case Table: case Table:
w.writeTable(n) w.writeTable(n)
@ -228,6 +230,22 @@ func (w *OrgWriter) writeListItem(li ListItem) {
w.WriteString(strings.TrimPrefix(liWriter.String(), liWriter.indent)) 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) { func (w *OrgWriter) writeTable(t Table) {
for _, row := range t.Rows { for _, row := range t.Rows {
w.WriteString(w.indent) w.WriteString(w.indent)

View file

@ -92,3 +92,26 @@ that spans multiple lines
</pre> </pre>
</li> </li>
</ul> </ul>
<p>
descriptive lists
</p>
<dl>
<dt>
term<dd>
<p>
description
</p>
<dd>
<dt>
?<dd>
<p>
without term
</p>
<dd>
<dt>
term<dd>
<p>
description
</p>
<dd>
</dl>

View file

@ -21,3 +21,11 @@
: with an example : with an example
: :
: that spans multiple lines : that spans multiple lines
descriptive lists
- term :: description
- without term
- term ::
description