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).
This commit is contained in:
Niklas Fasching 2022-11-02 14:16:57 +01:00
parent 05a2dedbf8
commit 1849701ba7
8 changed files with 77 additions and 5 deletions

View file

@ -1,6 +1,7 @@
package org package org
import ( import (
"math"
"regexp" "regexp"
"strings" "strings"
"unicode" "unicode"
@ -21,9 +22,15 @@ type Example struct {
Children []Node Children []Node
} }
type LatexBlock struct {
Content []Node
}
var exampleLineRegexp = regexp.MustCompile(`^(\s*):(\s(.*)|\s*$)`) var exampleLineRegexp = regexp.MustCompile(`^(\s*):(\s(.*)|\s*$)`)
var beginBlockRegexp = regexp.MustCompile(`(?i)^(\s*)#\+BEGIN_(\w+)(.*)`) var beginBlockRegexp = regexp.MustCompile(`(?i)^(\s*)#\+BEGIN_(\w+)(.*)`)
var endBlockRegexp = regexp.MustCompile(`(?i)^(\s*)#\+END_(\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 resultRegexp = regexp.MustCompile(`(?i)^(\s*)#\+RESULTS:`)
var exampleBlockEscapeRegexp = regexp.MustCompile(`(^|\n)([ \t]*),([ \t]*)(\*|,\*|#\+|,#\+)`) var exampleBlockEscapeRegexp = regexp.MustCompile(`(^|\n)([ \t]*),([ \t]*)(\*|,\*|#\+|,#\+)`)
@ -36,6 +43,15 @@ func lexBlock(line string) (token, bool) {
return nilToken, false 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) { func lexResult(line string) (token, bool) {
if m := resultRegexp.FindStringSubmatch(line); m != nil { if m := resultRegexp.FindStringSubmatch(line); m != nil {
return token{"result", len(m[1]), "", m}, true 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 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) { func (d *Document) parseSrcBlockResult(i int, parentStop stopFn) (int, Node) {
start := i start := i
for ; !parentStop(d, i) && d.tokens[i].kind == "text" && d.tokens[i].content == ""; 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 return m
} }
func (n Example) 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 Block) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Result) String() string { return orgWriter.WriteNodesAsString(n) } func (n LatexBlock) String() string { return orgWriter.WriteNodesAsString(n) }
func (n Result) String() string { return orgWriter.WriteNodesAsString(n) }

View file

@ -72,6 +72,7 @@ var lexFns = []lexFn{
lexKeywordOrComment, lexKeywordOrComment,
lexFootnoteDefinition, lexFootnoteDefinition,
lexExample, lexExample,
lexLatexBlock,
lexText, lexText,
} }
@ -209,6 +210,8 @@ func (d *Document) parseOne(i int, stop stopFn) (consumed int, node Node) {
consumed, node = d.parseTable(i, stop) consumed, node = d.parseTable(i, stop)
case "beginBlock": case "beginBlock":
consumed, node = d.parseBlock(i, stop) consumed, node = d.parseBlock(i, stop)
case "beginLatexBlock":
consumed, node = d.parseLatexBlock(i, stop)
case "result": case "result":
consumed, node = d.parseResult(i, stop) consumed, node = d.parseResult(i, stop)
case "beginDrawer": case "beginDrawer":

View file

@ -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) WriteResult(r Result) { WriteNodes(w, r.Node) }
func (w *HTMLWriter) WriteInlineBlock(b InlineBlock) { func (w *HTMLWriter) WriteInlineBlock(b InlineBlock) {

View file

@ -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) { func (w *OrgWriter) WriteResult(r Result) {
w.WriteString("#+RESULTS:\n") w.WriteString("#+RESULTS:\n")
WriteNodes(w, r.Node) WriteNodes(w, r.Node)

View file

@ -6,8 +6,18 @@ we support <code class="verbatim">\(...\)</code>, <code class="verbatim">\[...\]
<li>\[\sum_{i=1}^n a_n\]</li> <li>\[\sum_{i=1}^n a_n\]</li>
<li>$$\sum_{i=1}^n a_n$$</li> <li>$$\sum_{i=1}^n a_n$$</li>
<li>\begin{xyz}\sum_{i=1}^n a_n\end{xyz}</li> <li>\begin{xyz}\sum_{i=1}^n a_n\end{xyz}</li>
<li>\begin{xyz} <li>
\begin{xyz}
\sum_{i=1}^n a_n \sum_{i=1}^n a_n
\end{xyz}</li> \end{xyz}
</li>
<li>$2 + 2$, $3 - 3$</li> <li>$2 + 2$, $3 - 3$</li>
<li>
\begin{xyz}
latex block ignores block lvl elements (e.g. list - line starting with -)
a = b
- c
- d
\end{xyz}
</li>
</ul> </ul>

View file

@ -10,3 +10,9 @@ we support =\(...\)=, =\[...\]=, =$$...$$= and =\begin{$env}...\end{$env}= as la
\sum_{i=1}^n a_n \sum_{i=1}^n a_n
\end{xyz} \end{xyz}
- $2 + 2$, $3 - 3$ - $2 + 2$, $3 - 3$
- \begin{xyz}
latex block ignores block lvl elements (e.g. list - line starting with -)
a = b
- c
- d
\end{xyz}

View file

@ -10,3 +10,9 @@ we support =\(...\)=, =\[...\]=, =$$...$$= and =\begin{$env}...\end{$env}= as la
\sum_{i=1}^n a_n \sum_{i=1}^n a_n
\end{xyz} \end{xyz}
- $2 + 2$, $3 - 3$ - $2 + 2$, $3 - 3$
- \begin{xyz}
latex block ignores block lvl elements (e.g. list - line starting with -)
a = b
- c
- d
\end{xyz}

View file

@ -19,6 +19,7 @@ type Writer interface {
WriteHeadline(Headline) WriteHeadline(Headline)
WriteBlock(Block) WriteBlock(Block)
WriteResult(Result) WriteResult(Result)
WriteLatexBlock(LatexBlock)
WriteInlineBlock(InlineBlock) WriteInlineBlock(InlineBlock)
WriteExample(Example) WriteExample(Example)
WriteDrawer(Drawer) WriteDrawer(Drawer)
@ -62,6 +63,8 @@ func WriteNodes(w Writer, nodes ...Node) {
w.WriteBlock(n) w.WriteBlock(n)
case Result: case Result:
w.WriteResult(n) w.WriteResult(n)
case LatexBlock:
w.WriteLatexBlock(n)
case InlineBlock: case InlineBlock:
w.WriteInlineBlock(n) w.WriteInlineBlock(n)
case Example: case Example: