-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy path__init__.py
More file actions
155 lines (124 loc) · 5.15 KB
/
__init__.py
File metadata and controls
155 lines (124 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
"""An extension to capture amsmath latex environments."""
from __future__ import annotations
from collections.abc import Callable, Sequence
import re
from typing import TYPE_CHECKING
from markdown_it import MarkdownIt
from markdown_it.common.utils import escapeHtml
from markdown_it.rules_block import StateBlock
from mdit_py_plugins.utils import is_code_block
if TYPE_CHECKING:
from markdown_it.renderer import RendererProtocol
from markdown_it.token import Token
from markdown_it.utils import EnvType, OptionsDict
# Taken from amsmath version 2.1
# http://anorien.csc.warwick.ac.uk/mirrors/CTAN/macros/latex/required/amsmath/amsldoc.pdf
ENVIRONMENTS = [
# 3.2 single equation with an automatically gen-erated number
"equation",
# 3.3 variation equation, used for equations that dont fit on a single line
"multline",
# 3.5 a group of consecutive equations when there is no alignment desired among them
"gather",
# 3.6 Used for two or more equations when vertical alignment is desired
"align",
# allows the horizontal space between equationsto be explicitly specified.
"alignat",
# stretches the space betweenthe equation columns to the maximum possible width
"flalign",
# 4.1 The pmatrix, bmatrix, Bmatrix, vmatrix and Vmatrix have (respectively)
# (),[],{},||,and ‖‖ delimiters built in.
"matrix",
"pmatrix",
"bmatrix",
"Bmatrix",
"vmatrix",
"Vmatrix",
# eqnarray is another math environment, it is not part of amsmath,
# and note that it is better to use align or equation+split instead
"eqnarray",
]
# other "non-top-level" environments:
# 3.4 the split environment is for single equations that are too long to fit on one line
# and hence must be split into multiple lines,
# it is intended for use only inside some other displayed equation structure,
# usually an equation, align, or gather environment
# 3.7 variants gathered, aligned,and alignedat are provided
# whose total width is the actual width of the contents;
# thus they can be used as a component in a containing expression
RE_OPEN = r"\\begin\{(" + "|".join(ENVIRONMENTS) + r")([\*]?)\}"
def amsmath_plugin(
md: MarkdownIt, *, renderer: Callable[[str], str] | None = None
) -> None:
"""Parses TeX math equations, without any surrounding delimiters,
only for top-level `amsmath <https://ctan.org/pkg/amsmath>`__ environments:
.. code-block:: latex
\\begin{gather*}
a_1=b_1+c_1\\\\
a_2=b_2+c_2-d_2+e_2
\\end{gather*}
:param renderer: Function to render content, by default escapes HTML
"""
md.block.ruler.before(
"blockquote",
"amsmath",
amsmath_block,
{"alt": ["paragraph", "reference", "blockquote", "list", "footnote_def"]},
)
_renderer = (lambda content: escapeHtml(content)) if renderer is None else renderer
def render_amsmath_block(
self: RendererProtocol,
tokens: Sequence[Token],
idx: int,
options: OptionsDict,
env: EnvType,
) -> str:
content = _renderer(str(tokens[idx].content))
return f'<div class="math amsmath">\n{content}\n</div>\n'
md.add_render_rule("amsmath", render_amsmath_block)
def amsmath_block(
state: StateBlock, startLine: int, endLine: int, silent: bool
) -> bool:
# note the code principally follows the logic in markdown_it/rules_block/fence.py,
# except that:
# (a) it allows for closing tag on same line as opening tag
# (b) it does not allow for opening tag without closing tag (i.e. no auto-closing)
if is_code_block(state, startLine):
return False
# does the first line contain the beginning of an amsmath environment
first_start = state.bMarks[startLine] + state.tShift[startLine]
first_end = state.eMarks[startLine]
first_text = state.src[first_start:first_end]
if not (match_open := re.match(RE_OPEN, first_text)):
return False
# construct the closing tag
environment = match_open.group(1)
numbered = match_open.group(2)
closing = rf"\end{{{match_open.group(1)}{match_open.group(2)}}}"
# start looking for the closing tag, including the current line
nextLine = startLine - 1
while True:
nextLine += 1
if nextLine >= endLine:
# reached the end of the block without finding the closing tag
return False
next_start = state.bMarks[nextLine] + state.tShift[nextLine]
next_end = state.eMarks[nextLine]
if next_start < first_end and state.sCount[nextLine] < state.blkIndent:
# non-empty line with negative indent should stop the list:
# - \begin{align}
# test
return False
if state.src[next_start:next_end].rstrip().endswith(closing):
# found the closing tag
break
state.line = nextLine + 1
if not silent:
token = state.push("amsmath", "math", 0)
token.block = True
token.content = state.getLines(
startLine, state.line, state.sCount[startLine], False
)
token.meta = {"environment": environment, "numbered": numbered}
token.map = [startLine, nextLine]
return True