Add support for latex fragments
This commit is contained in:
parent
de302bbc0a
commit
76b157b8ce
8 changed files with 137 additions and 10 deletions
|
@ -12,9 +12,6 @@ in general, have a look at the Makefile - it's short enough.
|
||||||
* not yet implemented
|
* not yet implemented
|
||||||
** deadlines and scheduling
|
** deadlines and scheduling
|
||||||
see https://orgmode.org/manual/Deadlines-and-scheduling.html
|
see https://orgmode.org/manual/Deadlines-and-scheduling.html
|
||||||
** latex fragments
|
|
||||||
see https://orgmode.org/manual/LaTeX-fragments.html
|
|
||||||
+ mathjax for html_writer ?
|
|
||||||
** more types of links
|
** more types of links
|
||||||
see https://orgmode.org/manual/External-links.html & https://orgmode.org/manual/Internal-links.html
|
see https://orgmode.org/manual/External-links.html & https://orgmode.org/manual/Internal-links.html
|
||||||
- radio target <<<MyTarget>>>
|
- radio target <<<MyTarget>>>
|
||||||
|
|
|
@ -247,6 +247,12 @@ func (w *HTMLWriter) WriteEmphasis(e Emphasis) {
|
||||||
w.WriteString(tags[1])
|
w.WriteString(tags[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *HTMLWriter) WriteLatexFragment(l LatexFragment) {
|
||||||
|
w.WriteString(l.OpeningPair)
|
||||||
|
WriteNodes(w, l.Content...)
|
||||||
|
w.WriteString(l.ClosingPair)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *HTMLWriter) WriteStatisticToken(s StatisticToken) {
|
func (w *HTMLWriter) WriteStatisticToken(s StatisticToken) {
|
||||||
w.WriteString(fmt.Sprintf(`<code class="statistic">[%s]</code>`, s.Content))
|
w.WriteString(fmt.Sprintf(`<code class="statistic">[%s]</code>`, s.Content))
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,12 @@ type Emphasis struct {
|
||||||
Content []Node
|
Content []Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LatexFragment struct {
|
||||||
|
OpeningPair string
|
||||||
|
ClosingPair string
|
||||||
|
Content []Node
|
||||||
|
}
|
||||||
|
|
||||||
type FootnoteLink struct {
|
type FootnoteLink struct {
|
||||||
Name string
|
Name string
|
||||||
Definition *FootnoteDefinition
|
Definition *FootnoteDefinition
|
||||||
|
@ -51,10 +57,17 @@ var subScriptSuperScriptRegexp = regexp.MustCompile(`^([_^]){([^{}]+?)}`)
|
||||||
var timestampRegexp = regexp.MustCompile(`^<(\d{4}-\d{2}-\d{2})( [A-Za-z]+)?( \d{2}:\d{2})?( \+\d+[dwmy])?>`)
|
var timestampRegexp = regexp.MustCompile(`^<(\d{4}-\d{2}-\d{2})( [A-Za-z]+)?( \d{2}:\d{2})?( \+\d+[dwmy])?>`)
|
||||||
var footnoteRegexp = regexp.MustCompile(`^\[fn:([\w-]*?)(:(.*?))?\]`)
|
var footnoteRegexp = regexp.MustCompile(`^\[fn:([\w-]*?)(:(.*?))?\]`)
|
||||||
var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`)
|
var statisticsTokenRegexp = regexp.MustCompile(`^\[(\d+/\d+|\d+%)\]`)
|
||||||
|
var latexFragmentRegexp = regexp.MustCompile(`(?s)^\\begin{(\w+)}(.*)\\end{(\w+)}`)
|
||||||
|
|
||||||
var timestampFormat = "2006-01-02 Mon 15:04"
|
var timestampFormat = "2006-01-02 Mon 15:04"
|
||||||
var datestampFormat = "2006-01-02 Mon"
|
var datestampFormat = "2006-01-02 Mon"
|
||||||
|
|
||||||
|
var latexFragmentPairs = map[string]string{
|
||||||
|
`\(`: `\)`,
|
||||||
|
`\[`: `\]`,
|
||||||
|
`$$`: `$$`,
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Document) parseInline(input string) (nodes []Node) {
|
func (d *Document) parseInline(input string) (nodes []Node) {
|
||||||
previous, current := 0, 0
|
previous, current := 0, 0
|
||||||
for current < len(input) {
|
for current < len(input) {
|
||||||
|
@ -73,7 +86,9 @@ func (d *Document) parseInline(input string) (nodes []Node) {
|
||||||
case '<':
|
case '<':
|
||||||
consumed, node = d.parseTimestamp(input, current)
|
consumed, node = d.parseTimestamp(input, current)
|
||||||
case '\\':
|
case '\\':
|
||||||
consumed, node = d.parseExplicitLineBreak(input, current)
|
consumed, node = d.parseExplicitLineBreakOrLatexFragment(input, current)
|
||||||
|
case '$':
|
||||||
|
consumed, node = d.parseLatexFragment(input, current)
|
||||||
case '\n':
|
case '\n':
|
||||||
consumed, node = d.parseLineBreak(input, current)
|
consumed, node = d.parseLineBreak(input, current)
|
||||||
case ':':
|
case ':':
|
||||||
|
@ -128,14 +143,38 @@ func (d *Document) parseLineBreak(input string, start int) (int, Node) {
|
||||||
return i - start, LineBreak{i - start}
|
return i - start, LineBreak{i - start}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Document) parseExplicitLineBreak(input string, start int) (int, Node) {
|
func (d *Document) parseExplicitLineBreakOrLatexFragment(input string, start int) (int, Node) {
|
||||||
if start == 0 || input[start-1] == '\n' || start+2 >= len(input) || input[start+1] != '\\' {
|
switch {
|
||||||
|
case start+2 >= len(input):
|
||||||
|
case input[start+1] == '\\' && start != 0 && input[start-1] != '\n':
|
||||||
|
for i := start + 2; unicode.IsSpace(rune(input[i])); i++ {
|
||||||
|
if i >= len(input) || input[i] == '\n' {
|
||||||
|
return i + 1 - start, ExplicitLineBreak{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case input[start+1] == '(' || input[start+1] == '[':
|
||||||
|
return d.parseLatexFragment(input, start)
|
||||||
|
case strings.Index(input[start:], `\begin{`) == 0:
|
||||||
|
if m := latexFragmentRegexp.FindStringSubmatch(input[start:]); m != nil {
|
||||||
|
if open, content, close := m[1], m[2], m[3]; open == close {
|
||||||
|
openingPair, closingPair := `\begin{`+open+`}`, `\end{`+close+`}`
|
||||||
|
i := strings.Index(input[start:], closingPair)
|
||||||
|
return i + len(closingPair), LatexFragment{openingPair, closingPair, d.parseRawInline(content)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Document) parseLatexFragment(input string, start int) (int, Node) {
|
||||||
|
if start+2 >= len(input) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
for i := start + 2; unicode.IsSpace(rune(input[i])); i++ {
|
openingPair := input[start : start+2]
|
||||||
if i >= len(input) || input[i] == '\n' {
|
closingPair := latexFragmentPairs[openingPair]
|
||||||
return i + 1 - start, ExplicitLineBreak{}
|
if i := strings.Index(input[start+2:], closingPair); i != -1 {
|
||||||
}
|
content := d.parseRawInline(input[start+2 : start+2+i])
|
||||||
|
return i + 2 + 2, LatexFragment{openingPair, closingPair, content}
|
||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
@ -312,6 +351,7 @@ func (n LineBreak) String() string { return orgWriter.nodesAsString(n) }
|
||||||
func (n ExplicitLineBreak) String() string { return orgWriter.nodesAsString(n) }
|
func (n ExplicitLineBreak) String() string { return orgWriter.nodesAsString(n) }
|
||||||
func (n StatisticToken) String() string { return orgWriter.nodesAsString(n) }
|
func (n StatisticToken) String() string { return orgWriter.nodesAsString(n) }
|
||||||
func (n Emphasis) String() string { return orgWriter.nodesAsString(n) }
|
func (n Emphasis) String() string { return orgWriter.nodesAsString(n) }
|
||||||
|
func (n LatexFragment) String() string { return orgWriter.nodesAsString(n) }
|
||||||
func (n FootnoteLink) String() string { return orgWriter.nodesAsString(n) }
|
func (n FootnoteLink) String() string { return orgWriter.nodesAsString(n) }
|
||||||
func (n RegularLink) String() string { return orgWriter.nodesAsString(n) }
|
func (n RegularLink) String() string { return orgWriter.nodesAsString(n) }
|
||||||
func (n Timestamp) String() string { return orgWriter.nodesAsString(n) }
|
func (n Timestamp) String() string { return orgWriter.nodesAsString(n) }
|
||||||
|
|
|
@ -266,6 +266,12 @@ func (w *OrgWriter) WriteEmphasis(e Emphasis) {
|
||||||
w.WriteString(borders[1])
|
w.WriteString(borders[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *OrgWriter) WriteLatexFragment(l LatexFragment) {
|
||||||
|
w.WriteString(l.OpeningPair)
|
||||||
|
WriteNodes(w, l.Content...)
|
||||||
|
w.WriteString(l.ClosingPair)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *OrgWriter) WriteStatisticToken(s StatisticToken) {
|
func (w *OrgWriter) WriteStatisticToken(s StatisticToken) {
|
||||||
w.WriteString(fmt.Sprintf("[%s]", s.Content))
|
w.WriteString(fmt.Sprintf("[%s]", s.Content))
|
||||||
}
|
}
|
||||||
|
|
45
org/testdata/latex.html
vendored
Normal file
45
org/testdata/latex.html
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
<p>
|
||||||
|
without latex delimiters the <code class="verbatim">_{i=1}</code> and <code class="verbatim">_n</code> in <code class="verbatim">\sum_{i=1}^n a_n</code> are interpreted as subscripts.
|
||||||
|
we support <code class="verbatim">\(...\)</code>, <code class="verbatim">\[...\]</code>, <code class="verbatim">$$...$$</code> and <code class="verbatim">\begin{$env}...\end{$env}</code> as latex fragment delimiters.
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
∑<sub>i=1</sub>^n a_n (without latex delimiter)
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
\(\sum_{i=1}^n a_n\)
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
\[\sum_{i=1}^n a_n\]
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
$$\sum_{i=1}^n a_n$$
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
\begin{xyz}\sum_{i=1}^n a_n\end{xyz}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
\begin{xyz}
|
||||||
|
\sum_{i=1}^n a_n
|
||||||
|
\end{xyz}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
\begin{xyz}
|
||||||
|
foobar
|
||||||
|
\end{xyz}
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
15
org/testdata/latex.org
vendored
Normal file
15
org/testdata/latex.org
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
without latex delimiters the =_{i=1}= and =_n= in =\sum_{i=1}^n a_n= are interpreted as subscripts.
|
||||||
|
we support =\(...\)=, =\[...\]=, =$$...$$= and =\begin{$env}...\end{$env}= as latex fragment delimiters.
|
||||||
|
|
||||||
|
- \sum_{i=1}^n a_n (without latex delimiter)
|
||||||
|
- \(\sum_{i=1}^n a_n\)
|
||||||
|
- \[\sum_{i=1}^n a_n\]
|
||||||
|
- $$\sum_{i=1}^n a_n$$
|
||||||
|
- \begin{xyz}\sum_{i=1}^n a_n\end{xyz}
|
||||||
|
- \begin{xyz}
|
||||||
|
\sum_{i=1}^n a_n
|
||||||
|
\end{xyz}
|
||||||
|
|
||||||
|
- \begin{xyz}
|
||||||
|
foobar
|
||||||
|
\end{xyz}
|
15
org/testdata/latex.pretty_org
vendored
Normal file
15
org/testdata/latex.pretty_org
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
without latex delimiters the =_{i=1}= and =_n= in =\sum_{i=1}^n a_n= are interpreted as subscripts.
|
||||||
|
we support =\(...\)=, =\[...\]=, =$$...$$= and =\begin{$env}...\end{$env}= as latex fragment delimiters.
|
||||||
|
|
||||||
|
- \sum_{i=1}^n a_n (without latex delimiter)
|
||||||
|
- \(\sum_{i=1}^n a_n\)
|
||||||
|
- \[\sum_{i=1}^n a_n\]
|
||||||
|
- $$\sum_{i=1}^n a_n$$
|
||||||
|
- \begin{xyz}\sum_{i=1}^n a_n\end{xyz}
|
||||||
|
- \begin{xyz}
|
||||||
|
\sum_{i=1}^n a_n
|
||||||
|
\end{xyz}
|
||||||
|
|
||||||
|
- \begin{xyz}
|
||||||
|
foobar
|
||||||
|
\end{xyz}
|
|
@ -25,6 +25,7 @@ type Writer interface {
|
||||||
WriteParagraph(Paragraph)
|
WriteParagraph(Paragraph)
|
||||||
WriteText(Text)
|
WriteText(Text)
|
||||||
WriteEmphasis(Emphasis)
|
WriteEmphasis(Emphasis)
|
||||||
|
WriteLatexFragment(LatexFragment)
|
||||||
WriteStatisticToken(StatisticToken)
|
WriteStatisticToken(StatisticToken)
|
||||||
WriteExplicitLineBreak(ExplicitLineBreak)
|
WriteExplicitLineBreak(ExplicitLineBreak)
|
||||||
WriteLineBreak(LineBreak)
|
WriteLineBreak(LineBreak)
|
||||||
|
@ -71,6 +72,8 @@ func WriteNodes(w Writer, nodes ...Node) {
|
||||||
w.WriteText(n)
|
w.WriteText(n)
|
||||||
case Emphasis:
|
case Emphasis:
|
||||||
w.WriteEmphasis(n)
|
w.WriteEmphasis(n)
|
||||||
|
case LatexFragment:
|
||||||
|
w.WriteLatexFragment(n)
|
||||||
case StatisticToken:
|
case StatisticToken:
|
||||||
w.WriteStatisticToken(n)
|
w.WriteStatisticToken(n)
|
||||||
case ExplicitLineBreak:
|
case ExplicitLineBreak:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue