Add support for descriptive lists
This commit is contained in:
parent
376bb3652a
commit
ba3cf9f948
7 changed files with 109 additions and 19 deletions
|
@ -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
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
22
org/html.go
22
org/html.go
|
@ -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
|
||||||
|
|
48
org/list.go
48
org/list.go
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
18
org/org.go
18
org/org.go
|
@ -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)
|
||||||
|
|
23
org/testdata/lists.html
vendored
23
org/testdata/lists.html
vendored
|
@ -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>
|
||||||
|
|
8
org/testdata/lists.org
vendored
8
org/testdata/lists.org
vendored
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue