Fix multiline emphasis
I didn't have a test case for this and broke it when i introduced Line nodes to support printing back to org mode. Oops
This commit is contained in:
parent
6de03e0d13
commit
6637e63892
9 changed files with 104 additions and 71 deletions
25
org/block.go
25
org/block.go
|
@ -24,31 +24,30 @@ func lexBlock(line string) (token, bool) {
|
|||
return nilToken, false
|
||||
}
|
||||
|
||||
func isRawTextBlock(name string) bool { return name == "SRC" || name == "EXAMPLE" }
|
||||
|
||||
func (d *Document) parseBlock(i int, parentStop stopFn) (int, Node) {
|
||||
t, start, nodes := d.tokens[i], i, []Node{}
|
||||
t, start, lines := d.tokens[i], i, []string{}
|
||||
name, parameters := t.content, strings.Fields(t.matches[3])
|
||||
trim := trimIndentUpTo(d.tokens[i].lvl)
|
||||
stop := func(d *Document, i int) bool {
|
||||
return parentStop(d, i) || (d.tokens[i].kind == "endBlock" && d.tokens[i].content == name)
|
||||
}
|
||||
if name == "SRC" || name == "EXAMPLE" {
|
||||
for i++; !stop(d, i); i++ {
|
||||
nodes = append(nodes, Line{[]Node{Text{trim(d.tokens[i].matches[0])}}})
|
||||
block, consumed, i := Block{name, parameters, nil}, 0, i+1
|
||||
if isRawTextBlock(name) {
|
||||
for ; !stop(d, i); i++ {
|
||||
lines = append(lines, trim(d.tokens[i].matches[0]))
|
||||
}
|
||||
consumed = i - start
|
||||
block.Children = []Node{Text{strings.Join(lines, "\n")}}
|
||||
} else {
|
||||
for i++; !stop(d, i); {
|
||||
consumed, node := d.parseParagraph(i, stop)
|
||||
if consumed == 0 {
|
||||
break
|
||||
}
|
||||
i += consumed
|
||||
nodes = append(nodes, node)
|
||||
}
|
||||
consumed, block.Children = d.parseMany(i, stop)
|
||||
consumed++ // line with BEGIN
|
||||
}
|
||||
if parentStop(d, i) {
|
||||
return 0, nil
|
||||
}
|
||||
return i + 1 - start, Block{name, parameters, nodes}
|
||||
return consumed + 1, block
|
||||
}
|
||||
|
||||
func trimIndentUpTo(max int) func(string) string {
|
||||
|
|
41
org/html.go
41
org/html.go
|
@ -86,15 +86,14 @@ func (w *HTMLWriter) writeNodes(ns ...Node) {
|
|||
w.writeParagraph(n)
|
||||
case HorizontalRule:
|
||||
w.writeHorizontalRule(n)
|
||||
case Line:
|
||||
w.writeLine(n)
|
||||
|
||||
case Text:
|
||||
w.writeText(n)
|
||||
case Emphasis:
|
||||
w.writeEmphasis(n)
|
||||
case Linebreak:
|
||||
w.writeLinebreak(n)
|
||||
case ExplicitLineBreak:
|
||||
w.writeExplicitLineBreak(n)
|
||||
case LineBreak:
|
||||
w.writeLineBreak(n)
|
||||
case RegularLink:
|
||||
w.writeRegularLink(n)
|
||||
case FootnoteLink:
|
||||
|
@ -110,18 +109,15 @@ func (w *HTMLWriter) writeNodes(ns ...Node) {
|
|||
func (w *HTMLWriter) writeBlock(b Block) {
|
||||
switch b.Name {
|
||||
case "SRC":
|
||||
lines, lang := []string{}, "text"
|
||||
source, lang := b.Children[0].(Text).Content, "text"
|
||||
if len(b.Parameters) >= 1 {
|
||||
lang = b.Parameters[0]
|
||||
}
|
||||
for _, n := range b.Children {
|
||||
lines = append(lines, n.(Line).Children[0].(Text).Content)
|
||||
}
|
||||
w.WriteString(w.HighlightCodeBlock(strings.Join(lines, "\n"), lang) + "\n")
|
||||
w.WriteString(w.HighlightCodeBlock(source, lang) + "\n")
|
||||
case "EXAMPLE":
|
||||
w.WriteString(`<pre class="example">` + "\n")
|
||||
w.writeNodes(b.Children...)
|
||||
w.WriteString("</pre>\n")
|
||||
w.WriteString("\n</pre>\n")
|
||||
case "QUOTE":
|
||||
w.WriteString("<blockquote>\n")
|
||||
w.writeNodes(b.Children...)
|
||||
|
@ -134,7 +130,6 @@ func (w *HTMLWriter) writeBlock(b Block) {
|
|||
w.WriteString(fmt.Sprintf(`<div class="%s-block">`, strings.ToLower(b.Name)) + "\n")
|
||||
w.writeNodes(b.Children...)
|
||||
w.WriteString("</div>\n")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,7 +177,11 @@ func (w *HTMLWriter) writeEmphasis(e Emphasis) {
|
|||
w.WriteString(tags[1])
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) writeLinebreak(l Linebreak) {
|
||||
func (w *HTMLWriter) writeLineBreak(l LineBreak) {
|
||||
w.WriteString("\n")
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) writeExplicitLineBreak(l ExplicitLineBreak) {
|
||||
w.WriteString("<br>\n")
|
||||
}
|
||||
|
||||
|
@ -228,20 +227,16 @@ func (w *HTMLWriter) writeListItem(li ListItem) {
|
|||
w.WriteString("</li>\n")
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) writeLine(l Line) {
|
||||
if len(l.Children) != 0 {
|
||||
w.writeNodes(l.Children...)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) writeParagraph(p Paragraph) {
|
||||
if len(p.Children) == 1 && p.Children[0].(Line).Children == nil {
|
||||
if isEmptyLineParagraph(p) {
|
||||
return
|
||||
}
|
||||
w.WriteString("<p>\n")
|
||||
w.WriteString("<p>")
|
||||
if _, ok := p.Children[0].(LineBreak); !ok {
|
||||
w.WriteString("\n")
|
||||
}
|
||||
w.writeNodes(p.Children...)
|
||||
w.WriteString("</p>\n")
|
||||
w.WriteString("\n</p>\n")
|
||||
}
|
||||
|
||||
func (w *HTMLWriter) writeHorizontalRule(h HorizontalRule) {
|
||||
|
|
|
@ -9,7 +9,8 @@ import (
|
|||
|
||||
type Text struct{ Content string }
|
||||
|
||||
type Linebreak struct{}
|
||||
type LineBreak struct{ Count int }
|
||||
type ExplicitLineBreak struct{}
|
||||
|
||||
type Emphasis struct {
|
||||
Kind string
|
||||
|
@ -51,6 +52,8 @@ func (d *Document) parseInline(input string) (nodes []Node) {
|
|||
consumed, node = d.parseRegularLinkOrFootnoteReference(input, current)
|
||||
case '\\':
|
||||
consumed, node = d.parseExplicitLineBreak(input, current)
|
||||
case '\n':
|
||||
consumed, node = d.parseLineBreak(input, current)
|
||||
case ':':
|
||||
rewind, consumed, node = d.parseAutoLink(input, current)
|
||||
current -= rewind
|
||||
|
@ -75,13 +78,20 @@ func (d *Document) parseInline(input string) (nodes []Node) {
|
|||
return nodes
|
||||
}
|
||||
|
||||
func (d *Document) parseLineBreak(input string, start int) (int, Node) {
|
||||
i := start
|
||||
for ; input[i] == '\n'; i++ {
|
||||
}
|
||||
return i - start, LineBreak{i - start}
|
||||
}
|
||||
|
||||
func (d *Document) parseExplicitLineBreak(input string, start int) (int, Node) {
|
||||
if start == 0 || input[start-1] == '\n' || start+1 >= len(input) || input[start+1] != '\\' {
|
||||
return 0, nil
|
||||
}
|
||||
for i := start + 1; ; i++ {
|
||||
for i := start + 2; ; i++ {
|
||||
if i == len(input)-1 || input[i] == '\n' {
|
||||
return i + 1 - start, Linebreak{}
|
||||
return i + 1 - start, ExplicitLineBreak{}
|
||||
}
|
||||
if !unicode.IsSpace(rune(input[i])) {
|
||||
break
|
||||
|
@ -118,8 +128,7 @@ func (d *Document) parseFootnoteReference(input string, start int) (int, Node) {
|
|||
name, definition := m[1], m[3]
|
||||
link := FootnoteLink{name, nil}
|
||||
if definition != "" {
|
||||
paragraph := Paragraph{[]Node{Line{d.parseInline(definition)}}}
|
||||
link.Definition = &FootnoteDefinition{name, []Node{paragraph}, true}
|
||||
link.Definition = &FootnoteDefinition{name, []Node{Paragraph{d.parseInline(definition)}}, true}
|
||||
d.Footnotes.add(name, link.Definition)
|
||||
}
|
||||
return len(m[0]), link
|
||||
|
|
|
@ -66,7 +66,7 @@ func (d *Document) parseAffiliated(i int, stop stopFn) (int, Node) {
|
|||
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]}
|
||||
consumed, node = 1, Paragraph{nodes[:1]}
|
||||
}
|
||||
} else {
|
||||
consumed, node = d.parseOne(i, stop)
|
||||
|
|
38
org/org.go
38
org/org.go
|
@ -76,15 +76,14 @@ func (w *OrgWriter) writeNodes(ns ...Node) {
|
|||
w.writeParagraph(n)
|
||||
case HorizontalRule:
|
||||
w.writeHorizontalRule(n)
|
||||
case Line:
|
||||
w.writeLine(n)
|
||||
|
||||
case Text:
|
||||
w.writeText(n)
|
||||
case Emphasis:
|
||||
w.writeEmphasis(n)
|
||||
case Linebreak:
|
||||
w.writeLinebreak(n)
|
||||
case LineBreak:
|
||||
w.writeLineBreak(n)
|
||||
case ExplicitLineBreak:
|
||||
w.writeExplicitLineBreak(n)
|
||||
case RegularLink:
|
||||
w.writeRegularLink(n)
|
||||
case FootnoteLink:
|
||||
|
@ -133,7 +132,14 @@ func (w *OrgWriter) writeBlock(b Block) {
|
|||
w.WriteString(" " + strings.Join(b.Parameters, " "))
|
||||
}
|
||||
w.WriteString("\n")
|
||||
w.writeNodes(b.Children...)
|
||||
|
||||
if isRawTextBlock(b.Name) {
|
||||
for _, line := range strings.Split(b.Children[0].(Text).Content, "\n") {
|
||||
w.WriteString(w.indent + line + "\n")
|
||||
}
|
||||
} else {
|
||||
w.writeNodes(b.Children...)
|
||||
}
|
||||
w.WriteString(w.indent + "#+END_" + b.Name + "\n")
|
||||
}
|
||||
|
||||
|
@ -160,6 +166,7 @@ func (w *OrgWriter) writeFootnoteDefinition(f FootnoteDefinition) {
|
|||
|
||||
func (w *OrgWriter) writeParagraph(p Paragraph) {
|
||||
w.writeNodes(p.Children...)
|
||||
w.WriteString("\n")
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeKeyword(k Keyword) {
|
||||
|
@ -221,14 +228,6 @@ func (w *OrgWriter) writeHorizontalRule(hr HorizontalRule) {
|
|||
w.WriteString(w.indent + "-----\n")
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeLine(l Line) {
|
||||
if len(l.Children) != 0 {
|
||||
w.WriteString(w.indent)
|
||||
w.writeNodes(l.Children...)
|
||||
}
|
||||
w.WriteString("\n")
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeText(t Text) { w.WriteString(t.Content) }
|
||||
|
||||
func (w *OrgWriter) writeEmphasis(e Emphasis) {
|
||||
|
@ -241,16 +240,19 @@ func (w *OrgWriter) writeEmphasis(e Emphasis) {
|
|||
w.WriteString(borders[1])
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeLinebreak(l Linebreak) {
|
||||
w.WriteString(`\\`)
|
||||
func (w *OrgWriter) writeLineBreak(l LineBreak) {
|
||||
w.WriteString(strings.Repeat("\n"+w.indent, l.Count))
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeExplicitLineBreak(l ExplicitLineBreak) {
|
||||
w.WriteString(`\\` + "\n" + w.indent)
|
||||
}
|
||||
|
||||
func (w *OrgWriter) writeFootnoteLink(l FootnoteLink) {
|
||||
w.WriteString("[fn:" + l.Name)
|
||||
if l.Definition != nil {
|
||||
w.WriteString(":")
|
||||
line := l.Definition.Children[0].(Paragraph).Children[0].(Line)
|
||||
w.writeNodes(line.Children...)
|
||||
w.writeNodes(l.Definition.Children[0].(Paragraph).Children...)
|
||||
}
|
||||
w.WriteString("]")
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package org
|
|||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Line struct{ Children []Node }
|
||||
type Paragraph struct{ Children []Node }
|
||||
type HorizontalRule struct{}
|
||||
|
||||
|
@ -26,16 +26,16 @@ func lexHorizontalRule(line string) (token, bool) {
|
|||
}
|
||||
|
||||
func (d *Document) parseParagraph(i int, parentStop stopFn) (int, Node) {
|
||||
lines, start := []Node{Line{d.parseInline(d.tokens[i].content)}}, i
|
||||
lines, start := []string{d.tokens[i].content}, i
|
||||
i++
|
||||
stop := func(d *Document, i int) bool {
|
||||
return parentStop(d, i) || d.tokens[i].kind != "text" || d.tokens[i].content == ""
|
||||
}
|
||||
for ; !stop(d, i); i++ {
|
||||
lines = append(lines, Line{d.parseInline(d.tokens[i].content)})
|
||||
lines = append(lines, d.tokens[i].content)
|
||||
}
|
||||
consumed := i - start
|
||||
return consumed, Paragraph{lines}
|
||||
return consumed, Paragraph{d.parseInline(strings.Join(lines, "\n"))}
|
||||
}
|
||||
|
||||
func (d *Document) parseHorizontalRule(i int, parentStop stopFn) (int, Node) {
|
||||
|
|
24
org/testdata/example.html
vendored
24
org/testdata/example.html
vendored
|
@ -106,7 +106,6 @@ unordered list item 4
|
|||
<li>
|
||||
<p>
|
||||
<em>emphasis</em> and a hard line break <br>
|
||||
|
||||
see?
|
||||
</p>
|
||||
</li>
|
||||
|
@ -147,6 +146,25 @@ links with slashes do not become <em>emphasis</em>: <a href="https://somelinksho
|
|||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<code class="verbatim">multiline emphasis is
|
||||
supported - and respects MaxEmphasisNewLines (default: 1)</code>
|
||||
<em>so this
|
||||
is emphasized</em>
|
||||
</p>
|
||||
<p>
|
||||
/but
|
||||
this
|
||||
is
|
||||
not emphasized/
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
empty emphasis markers like ++ // __ and so on are ignored
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
subscript<sub>sub</sub> and superscript<sup>super</sup>
|
||||
</p>
|
||||
</li>
|
||||
|
@ -204,7 +222,9 @@ captioned soure block
|
|||
</p>
|
||||
</div>
|
||||
<div class="captioned">
|
||||
<p>
|
||||
<video src="my-video.mp4" title="my-video.mp4">my-video.mp4</video>
|
||||
</p>
|
||||
<p class="caption">
|
||||
captioned link (video in this case)
|
||||
</p>
|
||||
|
@ -258,7 +278,7 @@ either <code>this</code> or <code>that</code> foo.
|
|||
either <code>this</code>
|
||||
or <code>that</code> foo.
|
||||
</p>
|
||||
<h3><a href="https://github.com/chaseadamsio/goorgeous/issues/50">#50</a>: Linebreaks in lists are preserved</h3>
|
||||
<h3><a href="https://github.com/chaseadamsio/goorgeous/issues/50">#50</a>: LineBreaks in lists are preserved</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
|
|
12
org/testdata/example.org
vendored
12
org/testdata/example.org
vendored
|
@ -54,6 +54,16 @@ this one is cheating a little as tags are ALWAYS printed right aligned to a give
|
|||
- links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/
|
||||
- _underlined_ *bold* =verbatim= ~code~ +strikethrough+
|
||||
- *bold string with an *asterisk inside*
|
||||
- =multiline emphasis is
|
||||
supported - and respects MaxEmphasisNewLines (default: 1)=
|
||||
/so this
|
||||
is emphasized/
|
||||
|
||||
/but
|
||||
this
|
||||
is
|
||||
not emphasized/
|
||||
- empty emphasis markers like ++ // __ and so on are ignored
|
||||
- subscript_{sub} and superscript^{super}
|
||||
- links
|
||||
1. regular link [[https://example.com]] link without description
|
||||
|
@ -121,7 +131,7 @@ crazy ain't it?
|
|||
either ~this~ or ~that~ foo.
|
||||
either ~this~
|
||||
or ~that~ foo.
|
||||
*** DONE [[https://github.com/chaseadamsio/goorgeous/issues/50][#50]]: Linebreaks in lists are preserved
|
||||
*** DONE [[https://github.com/chaseadamsio/goorgeous/issues/50][#50]]: LineBreaks in lists are preserved
|
||||
- this list item
|
||||
has
|
||||
multiple
|
||||
|
|
|
@ -12,10 +12,8 @@ func isSecondBlankLine(d *Document, i int) bool {
|
|||
}
|
||||
|
||||
func isEmptyLineParagraph(n Node) bool {
|
||||
if p, _ := n.(Paragraph); len(p.Children) == 1 {
|
||||
return len(p.Children[0].(Line).Children) == 0
|
||||
}
|
||||
return false
|
||||
p, ok := n.(Paragraph)
|
||||
return ok && len(p.Children) == 0
|
||||
}
|
||||
|
||||
func isImageOrVideoLink(n Node) bool {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue