From 1849701ba7d094c86a8ee9082a01bcae5dd783fe Mon Sep 17 00:00:00 2001 From: Niklas Fasching Date: Wed, 2 Nov 2022 14:16:57 +0100 Subject: [PATCH] Add support for latex blocks / environments current support for latex fragments was inline only, i.e. lines containing block elements (e.g. a line starting with `* `, i.e. a headline) will not be parsed as part of the latex fragment but the respective block element. Parsing latex blocks at the block level should fix that. Note that in any case we don't do any processing and just emit the raw latex (leaving the rendering to e.g. js). --- org/block.go | 39 ++++++++++++++++++++++++++++++++--- org/document.go | 3 +++ org/html_writer.go | 5 +++++ org/org_writer.go | 6 ++++++ org/testdata/latex.html | 14 +++++++++++-- org/testdata/latex.org | 6 ++++++ org/testdata/latex.pretty_org | 6 ++++++ org/writer.go | 3 +++ 8 files changed, 77 insertions(+), 5 deletions(-) diff --git a/org/block.go b/org/block.go index 9793f81..2f30a7a 100644 --- a/org/block.go +++ b/org/block.go @@ -1,6 +1,7 @@ package org import ( + "math" "regexp" "strings" "unicode" @@ -21,9 +22,15 @@ type Example struct { Children []Node } +type LatexBlock struct { + Content []Node +} + var exampleLineRegexp = regexp.MustCompile(`^(\s*):(\s(.*)|\s*$)`) var beginBlockRegexp = regexp.MustCompile(`(?i)^(\s*)#\+BEGIN_(\w+)(.*)`) var endBlockRegexp = regexp.MustCompile(`(?i)^(\s*)#\+END_(\w+)`) +var beginLatexBlockRegexp = regexp.MustCompile(`(?i)^(\s*)\\begin{([^}]+)}(\s*)$`) +var endLatexBlockRegexp = regexp.MustCompile(`(?i)^(\s*)\\end{([^}]+)}(\s*)$`) var resultRegexp = regexp.MustCompile(`(?i)^(\s*)#\+RESULTS:`) var exampleBlockEscapeRegexp = regexp.MustCompile(`(^|\n)([ \t]*),([ \t]*)(\*|,\*|#\+|,#\+)`) @@ -36,6 +43,15 @@ func lexBlock(line string) (token, bool) { return nilToken, false } +func lexLatexBlock(line string) (token, bool) { + if m := beginLatexBlockRegexp.FindStringSubmatch(line); m != nil { + return token{"beginLatexBlock", len(m[1]), strings.ToUpper(m[2]), m}, true + } else if m := endLatexBlockRegexp.FindStringSubmatch(line); m != nil { + return token{"endLatexBlock", len(m[1]), strings.ToUpper(m[2]), m}, true + } + return nilToken, false +} + func lexResult(line string) (token, bool) { if m := resultRegexp.FindStringSubmatch(line); m != nil { return token{"result", len(m[1]), "", m}, true @@ -85,6 +101,22 @@ func (d *Document) parseBlock(i int, parentStop stopFn) (int, Node) { return i + 1 - start, block } +func (d *Document) parseLatexBlock(i int, parentStop stopFn) (int, Node) { + t, start := d.tokens[i], i + name, rawText, trim := t.content, "", trimIndentUpTo(int(math.Max((float64(d.baseLvl)), float64(t.lvl)))) + stop := func(d *Document, i int) bool { + return i >= len(d.tokens) || (d.tokens[i].kind == "endLatexBlock" && d.tokens[i].content == name) + } + for ; !stop(d, i); i++ { + rawText += trim(d.tokens[i].matches[0]) + "\n" + } + if i >= len(d.tokens) || d.tokens[i].kind != "endLatexBlock" || d.tokens[i].content != name { + return 0, nil + } + rawText += trim(d.tokens[i].matches[0]) + return i + 1 - start, LatexBlock{d.parseRawInline(rawText)} +} + func (d *Document) parseSrcBlockResult(i int, parentStop stopFn) (int, Node) { start := i for ; !parentStop(d, i) && d.tokens[i].kind == "text" && d.tokens[i].content == ""; i++ { @@ -145,6 +177,7 @@ func (b Block) ParameterMap() map[string]string { return m } -func (n Example) String() string { return orgWriter.WriteNodesAsString(n) } -func (n Block) String() string { return orgWriter.WriteNodesAsString(n) } -func (n Result) String() string { return orgWriter.WriteNodesAsString(n) } +func (n Example) String() string { return orgWriter.WriteNodesAsString(n) } +func (n Block) String() string { return orgWriter.WriteNodesAsString(n) } +func (n LatexBlock) String() string { return orgWriter.WriteNodesAsString(n) } +func (n Result) String() string { return orgWriter.WriteNodesAsString(n) } diff --git a/org/document.go b/org/document.go index 755864b..9da27b9 100644 --- a/org/document.go +++ b/org/document.go @@ -72,6 +72,7 @@ var lexFns = []lexFn{ lexKeywordOrComment, lexFootnoteDefinition, lexExample, + lexLatexBlock, lexText, } @@ -209,6 +210,8 @@ func (d *Document) parseOne(i int, stop stopFn) (consumed int, node Node) { consumed, node = d.parseTable(i, stop) case "beginBlock": consumed, node = d.parseBlock(i, stop) + case "beginLatexBlock": + consumed, node = d.parseLatexBlock(i, stop) case "result": consumed, node = d.parseResult(i, stop) case "beginDrawer": diff --git a/org/html_writer.go b/org/html_writer.go index 28c402c..5918af6 100644 --- a/org/html_writer.go +++ b/org/html_writer.go @@ -162,6 +162,11 @@ func (w *HTMLWriter) WriteBlock(b Block) { } } +func (w *HTMLWriter) WriteLatexBlock(b LatexBlock) { + WriteNodes(w, b.Content...) + w.WriteString("\n") +} + func (w *HTMLWriter) WriteResult(r Result) { WriteNodes(w, r.Node) } func (w *HTMLWriter) WriteInlineBlock(b InlineBlock) { diff --git a/org/org_writer.go b/org/org_writer.go index 0cfaead..fce6d20 100644 --- a/org/org_writer.go +++ b/org/org_writer.go @@ -109,6 +109,12 @@ func (w *OrgWriter) WriteBlock(b Block) { } } +func (w *OrgWriter) WriteLatexBlock(b LatexBlock) { + w.WriteString(w.indent) + WriteNodes(w, b.Content...) + w.WriteString("\n") +} + func (w *OrgWriter) WriteResult(r Result) { w.WriteString("#+RESULTS:\n") WriteNodes(w, r.Node) diff --git a/org/testdata/latex.html b/org/testdata/latex.html index 58bbb8a..8659467 100644 --- a/org/testdata/latex.html +++ b/org/testdata/latex.html @@ -6,8 +6,18 @@ we support \(...\), \[...\]
  • \[\sum_{i=1}^n a_n\]
  • $$\sum_{i=1}^n a_n$$
  • \begin{xyz}\sum_{i=1}^n a_n\end{xyz}
  • -
  • \begin{xyz} +
  • +\begin{xyz} \sum_{i=1}^n a_n -\end{xyz}
  • +\end{xyz} +
  • $2 + 2$, $3 - 3$
  • +
  • +\begin{xyz} +latex block ignores block lvl elements (e.g. list - line starting with -) +a = b +- c +- d +\end{xyz} +
  • diff --git a/org/testdata/latex.org b/org/testdata/latex.org index 5a4c953..534d026 100644 --- a/org/testdata/latex.org +++ b/org/testdata/latex.org @@ -10,3 +10,9 @@ we support =\(...\)=, =\[...\]=, =$$...$$= and =\begin{$env}...\end{$env}= as la \sum_{i=1}^n a_n \end{xyz} - $2 + 2$, $3 - 3$ +- \begin{xyz} + latex block ignores block lvl elements (e.g. list - line starting with -) + a = b + - c + - d + \end{xyz} diff --git a/org/testdata/latex.pretty_org b/org/testdata/latex.pretty_org index 5a4c953..534d026 100644 --- a/org/testdata/latex.pretty_org +++ b/org/testdata/latex.pretty_org @@ -10,3 +10,9 @@ we support =\(...\)=, =\[...\]=, =$$...$$= and =\begin{$env}...\end{$env}= as la \sum_{i=1}^n a_n \end{xyz} - $2 + 2$, $3 - 3$ +- \begin{xyz} + latex block ignores block lvl elements (e.g. list - line starting with -) + a = b + - c + - d + \end{xyz} diff --git a/org/writer.go b/org/writer.go index 29b64bd..fb918e8 100644 --- a/org/writer.go +++ b/org/writer.go @@ -19,6 +19,7 @@ type Writer interface { WriteHeadline(Headline) WriteBlock(Block) WriteResult(Result) + WriteLatexBlock(LatexBlock) WriteInlineBlock(InlineBlock) WriteExample(Example) WriteDrawer(Drawer) @@ -62,6 +63,8 @@ func WriteNodes(w Writer, nodes ...Node) { w.WriteBlock(n) case Result: w.WriteResult(n) + case LatexBlock: + w.WriteLatexBlock(n) case InlineBlock: w.WriteInlineBlock(n) case Example: