From 3018ace8d0179fb23847ddb47aaa91d7c7b6b279 Mon Sep 17 00:00:00 2001
From: Niklas Fasching
Date: Thu, 16 Apr 2020 14:28:40 +0200
Subject: [PATCH] Implement inline source blocks
https://orgmode.org/manual/Structure-of-Code-Blocks.html
---
main.go | 5 ++++-
org/html_writer.go | 40 ++++++++++++++++++++++------------
org/inline.go | 33 +++++++++++++++++++++++-----
org/org_writer.go | 10 +++++++++
org/testdata/inline.html | 11 ++++++++++
org/testdata/inline.org | 1 +
org/testdata/inline.pretty_org | 1 +
org/writer.go | 3 +++
8 files changed, 83 insertions(+), 21 deletions(-)
diff --git a/main.go b/main.go
index 6500a95..3f31f3c 100644
--- a/main.go
+++ b/main.go
@@ -46,7 +46,7 @@ func main() {
fmt.Fprint(os.Stdout, out)
}
-func highlightCodeBlock(source, lang string) string {
+func highlightCodeBlock(source, lang string, inline bool) string {
var w strings.Builder
l := lexers.Get(lang)
if l == nil {
@@ -55,5 +55,8 @@ func highlightCodeBlock(source, lang string) string {
l = chroma.Coalesce(l)
it, _ := l.Tokenise(nil, source)
_ = html.New().Format(&w, styles.Get("friendly"), it)
+ if inline {
+ return `` + "\n" + w.String() + "\n" + `
`
+ }
return `` + "\n" + w.String() + "\n" + `
`
}
diff --git a/org/html_writer.go b/org/html_writer.go
index 8a35e17..1f4bb9c 100644
--- a/org/html_writer.go
+++ b/org/html_writer.go
@@ -16,7 +16,7 @@ import (
// HTMLWriter exports an org document into a html document.
type HTMLWriter struct {
ExtendingWriter Writer
- HighlightCodeBlock func(source, lang string) string
+ HighlightCodeBlock func(source, lang string, inline bool) string
strings.Builder
document *Document
@@ -61,7 +61,10 @@ func NewHTMLWriter() *HTMLWriter {
document: &Document{Configuration: defaultConfig},
log: defaultConfig.Log,
htmlEscape: true,
- HighlightCodeBlock: func(source, lang string) string {
+ HighlightCodeBlock: func(source, lang string, inline bool) string {
+ if inline {
+ return fmt.Sprintf("", html.EscapeString(source))
+ }
return fmt.Sprintf("", html.EscapeString(source))
},
footnotes: &footnotes{
@@ -103,24 +106,14 @@ func (w *HTMLWriter) WriteComment(Comment) {}
func (w *HTMLWriter) WritePropertyDrawer(PropertyDrawer) {}
func (w *HTMLWriter) WriteBlock(b Block) {
- content := ""
- if isRawTextBlock(b.Name) {
- builder, htmlEscape := w.Builder, w.htmlEscape
- w.Builder, w.htmlEscape = strings.Builder{}, false
- WriteNodes(w, b.Children...)
- out := w.String()
- w.Builder, w.htmlEscape = builder, htmlEscape
- content = strings.TrimRightFunc(out, unicode.IsSpace)
- } else {
- content = w.WriteNodesAsString(b.Children...)
- }
+ content := w.blockContent(b.Name, b.Children)
switch name := b.Name; {
case name == "SRC":
lang := "text"
if len(b.Parameters) >= 1 {
lang = strings.ToLower(b.Parameters[0])
}
- content = w.HighlightCodeBlock(content, lang)
+ content = w.HighlightCodeBlock(content, lang, false)
w.WriteString(fmt.Sprintf("\n%s\n
\n", lang, content))
case name == "EXAMPLE":
w.WriteString(`` + "\n" + html.EscapeString(content) + "\n
\n")
@@ -139,6 +132,12 @@ func (w *HTMLWriter) WriteBlock(b Block) {
}
}
+func (w *HTMLWriter) WriteInlineBlock(b InlineBlock) {
+ content := w.blockContent(b.Name, b.Children)
+ lang := strings.ToLower(b.Parameters[0])
+ w.WriteString(fmt.Sprintf("\n%s\n
", lang, content))
+}
+
func (w *HTMLWriter) WriteDrawer(d Drawer) {
WriteNodes(w, d.Children...)
}
@@ -483,6 +482,19 @@ func (w *HTMLWriter) withHTMLAttributes(input string, kvs ...string) string {
return out.String()
}
+func (w *HTMLWriter) blockContent(name string, children []Node) string {
+ if isRawTextBlock(name) {
+ builder, htmlEscape := w.Builder, w.htmlEscape
+ w.Builder, w.htmlEscape = strings.Builder{}, false
+ WriteNodes(w, children...)
+ out := w.String()
+ w.Builder, w.htmlEscape = builder, htmlEscape
+ return strings.TrimRightFunc(out, unicode.IsSpace)
+ } else {
+ return w.WriteNodesAsString(children...)
+ }
+}
+
func setHTMLAttribute(attributes []h.Attribute, k, v string) []h.Attribute {
for i, a := range attributes {
if strings.ToLower(a.Key) == strings.ToLower(k) {
diff --git a/org/inline.go b/org/inline.go
index 4ba0a96..f0f90b4 100644
--- a/org/inline.go
+++ b/org/inline.go
@@ -30,6 +30,12 @@ type Emphasis struct {
Content []Node
}
+type InlineBlock struct {
+ Name string
+ Parameters []string
+ Children []Node
+}
+
type LatexFragment struct {
OpeningPair string
ClosingPair string
@@ -58,6 +64,7 @@ var timestampRegexp = regexp.MustCompile(`^<(\d{4}-\d{2}-\d{2})( [A-Za-z]+)?( \d
var footnoteRegexp = regexp.MustCompile(`^\[fn:([\w-]*?)(:(.*?))?\]`)
var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`)
var latexFragmentRegexp = regexp.MustCompile(`(?s)^\\begin{(\w+)}(.*)\\end{(\w+)}`)
+var inlineBlockRegexp = regexp.MustCompile(`src_(\w+)(\[(.*)\])?{(.*)}`)
var timestampFormat = "2006-01-02 Mon 15:04"
var datestampFormat = "2006-01-02 Mon"
@@ -77,7 +84,7 @@ func (d *Document) parseInline(input string) (nodes []Node) {
case '^':
consumed, node = d.parseSubOrSuperScript(input, current)
case '_':
- consumed, node = d.parseSubScriptOrEmphasis(input, current)
+ rewind, consumed, node = d.parseSubScriptOrEmphasisOrInlineBlock(input, current)
case '*', '/', '+':
consumed, node = d.parseEmphasis(input, current, false)
case '=', '~':
@@ -94,8 +101,8 @@ func (d *Document) parseInline(input string) (nodes []Node) {
consumed, node = d.parseLineBreak(input, current)
case ':':
rewind, consumed, node = d.parseAutoLink(input, current)
- current -= rewind
}
+ current -= rewind
if consumed != 0 {
if current > previous {
nodes = append(nodes, Text{input[previous:current], false})
@@ -144,6 +151,16 @@ func (d *Document) parseLineBreak(input string, start int) (int, Node) {
return i - start, LineBreak{i - start}
}
+func (d *Document) parseInlineBlock(input string, start int) (int, int, Node) {
+ if !(strings.HasSuffix(input[:start], "src") && (start-4 < 0 || unicode.IsSpace(rune(input[start-4])))) {
+ return 0, 0, nil
+ }
+ if m := inlineBlockRegexp.FindStringSubmatch(input[start-4:]); m != nil {
+ return 3, len(m[0]), InlineBlock{"src", strings.Fields(m[1] + " " + m[3]), d.parseRawInline(m[4])}
+ }
+ return 0, 0, nil
+}
+
func (d *Document) parseExplicitLineBreakOrLatexFragment(input string, start int) (int, Node) {
switch {
case start+2 >= len(input):
@@ -190,11 +207,14 @@ func (d *Document) parseSubOrSuperScript(input string, start int) (int, Node) {
return 0, nil
}
-func (d *Document) parseSubScriptOrEmphasis(input string, start int) (int, Node) {
- if consumed, node := d.parseSubOrSuperScript(input, start); consumed != 0 {
- return consumed, node
+func (d *Document) parseSubScriptOrEmphasisOrInlineBlock(input string, start int) (int, int, Node) {
+ if rewind, consumed, node := d.parseInlineBlock(input, start); consumed != 0 {
+ return rewind, consumed, node
+ } else if consumed, node := d.parseSubOrSuperScript(input, start); consumed != 0 {
+ return 0, consumed, node
}
- return d.parseEmphasis(input, start, false)
+ consumed, node := d.parseEmphasis(input, start, false)
+ return 0, consumed, node
}
func (d *Document) parseOpeningBracket(input string, start int) (int, Node) {
@@ -355,6 +375,7 @@ func (n LineBreak) String() string { return orgWriter.WriteNodesAsString
func (n ExplicitLineBreak) String() string { return orgWriter.WriteNodesAsString(n) }
func (n StatisticToken) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Emphasis) String() string { return orgWriter.WriteNodesAsString(n) }
+func (n InlineBlock) String() string { return orgWriter.WriteNodesAsString(n) }
func (n LatexFragment) String() string { return orgWriter.WriteNodesAsString(n) }
func (n FootnoteLink) String() string { return orgWriter.WriteNodesAsString(n) }
func (n RegularLink) String() string { return orgWriter.WriteNodesAsString(n) }
diff --git a/org/org_writer.go b/org/org_writer.go
index d5a33ff..8bca476 100644
--- a/org/org_writer.go
+++ b/org/org_writer.go
@@ -97,6 +97,16 @@ func (w *OrgWriter) WriteBlock(b Block) {
w.WriteString("#+END_" + b.Name + "\n")
}
+func (w *OrgWriter) WriteInlineBlock(b InlineBlock) {
+ w.WriteString(b.Name + "_" + b.Parameters[0])
+ if len(b.Parameters) > 1 {
+ w.WriteString("[" + strings.Join(b.Parameters[1:], " ") + "]")
+ }
+ w.WriteString("{")
+ WriteNodes(w, b.Children...)
+ w.WriteString("}")
+}
+
func (w *OrgWriter) WriteDrawer(d Drawer) {
w.WriteString(w.indent + ":" + d.Name + ":\n")
WriteNodes(w, d.Children...)
diff --git a/org/testdata/inline.html b/org/testdata/inline.html
index 9801b36..3ba6474 100644
--- a/org/testdata/inline.html
+++ b/org/testdata/inline.html
@@ -43,6 +43,17 @@ links with slashes do not become emphasis:
+
+
+
+
+
+
multiline emphasis is
supported - and respects MaxEmphasisNewLines (default: 1)
so this
diff --git a/org/testdata/inline.org b/org/testdata/inline.org
index d967879..dfd2b01 100644
--- a/org/testdata/inline.org
+++ b/org/testdata/inline.org
@@ -8,6 +8,7 @@
- links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/
- _underlined_ *bold* =verbatim= ~code~ +strikethrough+
- *bold string with an *asterisk inside*
+- inline source blocks like src_html[:eval no]{hello
}
- =multiline emphasis is
supported - and respects MaxEmphasisNewLines (default: 1)=
/so this
diff --git a/org/testdata/inline.pretty_org b/org/testdata/inline.pretty_org
index f1320bb..e1bd477 100644
--- a/org/testdata/inline.pretty_org
+++ b/org/testdata/inline.pretty_org
@@ -8,6 +8,7 @@
- links with slashes do not become /emphasis/: [[https://somelinkshouldntrenderaccidentalemphasis.com]]/ /emphasis/
- _underlined_ *bold* =verbatim= ~code~ +strikethrough+
- *bold string with an *asterisk inside*
+- inline source blocks like src_html[:eval no]{hello
}
- =multiline emphasis is
supported - and respects MaxEmphasisNewLines (default: 1)=
/so this
diff --git a/org/writer.go b/org/writer.go
index 189d72e..c84ce9f 100644
--- a/org/writer.go
+++ b/org/writer.go
@@ -18,6 +18,7 @@ type Writer interface {
WriteNodeWithName(NodeWithName)
WriteHeadline(Headline)
WriteBlock(Block)
+ WriteInlineBlock(InlineBlock)
WriteExample(Example)
WriteDrawer(Drawer)
WritePropertyDrawer(PropertyDrawer)
@@ -57,6 +58,8 @@ func WriteNodes(w Writer, nodes ...Node) {
w.WriteHeadline(n)
case Block:
w.WriteBlock(n)
+ case InlineBlock:
+ w.WriteInlineBlock(n)
case Example:
w.WriteExample(n)
case Drawer: