From 6637e6389232c2c8c565f35938baf0ec85f56c52 Mon Sep 17 00:00:00 2001
From: Niklas Fasching
Date: Mon, 10 Dec 2018 17:14:13 +0100
Subject: [PATCH] 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
---
org/block.go | 25 ++++++++++++------------
org/html.go | 41 +++++++++++++++++----------------------
org/inline.go | 19 +++++++++++++-----
org/keyword.go | 2 +-
org/org.go | 38 +++++++++++++++++++-----------------
org/paragraph.go | 8 ++++----
org/testdata/example.html | 24 +++++++++++++++++++++--
org/testdata/example.org | 12 +++++++++++-
org/util.go | 6 ++----
9 files changed, 104 insertions(+), 71 deletions(-)
diff --git a/org/block.go b/org/block.go
index c0126ec..667fb6b 100644
--- a/org/block.go
+++ b/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 {
diff --git a/org/html.go b/org/html.go
index cf16c23..20b80f5 100644
--- a/org/html.go
+++ b/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(`` + "\n")
w.writeNodes(b.Children...)
- w.WriteString("
\n")
+ w.WriteString("\n\n")
case "QUOTE":
w.WriteString("\n")
w.writeNodes(b.Children...)
@@ -134,7 +130,6 @@ func (w *HTMLWriter) writeBlock(b Block) {
w.WriteString(fmt.Sprintf(``, strings.ToLower(b.Name)) + "\n")
w.writeNodes(b.Children...)
w.WriteString("
\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("
\n")
}
@@ -228,20 +227,16 @@ func (w *HTMLWriter) writeListItem(li ListItem) {
w.WriteString("\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("\n")
+ w.WriteString("
")
+ if _, ok := p.Children[0].(LineBreak); !ok {
+ w.WriteString("\n")
+ }
w.writeNodes(p.Children...)
- w.WriteString("
\n")
+ w.WriteString("\n
\n")
}
func (w *HTMLWriter) writeHorizontalRule(h HorizontalRule) {
diff --git a/org/inline.go b/org/inline.go
index e096ae0..9979af1 100644
--- a/org/inline.go
+++ b/org/inline.go
@@ -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
diff --git a/org/keyword.go b/org/keyword.go
index 68359c0..fe27da5 100644
--- a/org/keyword.go
+++ b/org/keyword.go
@@ -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)
diff --git a/org/org.go b/org/org.go
index 752412d..87b6d58 100644
--- a/org/org.go
+++ b/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("]")
}
diff --git a/org/paragraph.go b/org/paragraph.go
index 5f94010..072a155 100644
--- a/org/paragraph.go
+++ b/org/paragraph.go
@@ -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) {
diff --git a/org/testdata/example.html b/org/testdata/example.html
index 9afe1cd..7159696 100644
--- a/org/testdata/example.html
+++ b/org/testdata/example.html
@@ -106,7 +106,6 @@ unordered list item 4
emphasis and a hard line break
-
see?
@@ -147,6 +146,25 @@ links with slashes do not become emphasis: 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
+
+
+
+
subscriptsub and superscriptsuper
@@ -204,7 +222,9 @@ captioned soure block
+
+
captioned link (video in this case)
@@ -258,7 +278,7 @@ either
this
or
that
foo.
either
this
or
that
foo.
-
#50: Linebreaks in lists are preserved
+
#50: LineBreaks in lists are preserved
-
diff --git a/org/testdata/example.org b/org/testdata/example.org
index 9b87798..d4b4c10 100644
--- a/org/testdata/example.org
+++ b/org/testdata/example.org
@@ -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
diff --git a/org/util.go b/org/util.go
index eb23f16..2ae0663 100644
--- a/org/util.go
+++ b/org/util.go
@@ -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 {