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@@ -147,6 +146,25 @@ links with slashes do not become emphasis: multiline emphasis is +supported - and respects MaxEmphasisNewLines (default: 1) +so this +is emphasized + + emphasis and a hard line break
- see?+/but +this +is +not emphasized/ +
+ ++ ++empty emphasis markers like ++ // __ and so on are ignored +
++ @@ -204,7 +222,9 @@ captioned soure blocksubscriptsub and superscriptsuper
++
captioned link (video in this case)
@@ -258,7 +278,7 @@ eitherthis
orthat
foo. eitherthis
orthat
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 {