-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mdx_math.py
147 lines (124 loc) · 5.74 KB
/
mdx_math.py
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
# -*- coding: utf-8 -*-
'''
Math extension for Python-Markdown
==================================
Adds support for displaying math formulas using
[MathJax](http://www.mathjax.org/).
Author: 2015-2020, Dmitry Shachnev <mitya57@gmail.com>.
'''
from xml.etree.ElementTree import Element
from markdown.inlinepatterns import InlineProcessor
from markdown.extensions import Extension
from markdown.preprocessors import Preprocessor
from markdown.util import AtomicString
def _wrap_node(node, preview_text, wrapper_tag):
preview = Element('span', {'class': 'MathJax_Preview'})
preview.text = AtomicString(preview_text)
wrapper = Element(wrapper_tag)
wrapper.extend([preview, node])
return wrapper
class InlineMathPattern(InlineProcessor):
def handleMatch(self, m, data):
node = Element('script')
node.set('type', self._content_type)
node.text = AtomicString(m.group(2))
if self._add_preview:
node = _wrap_node(node, m.group(0), 'span')
return node, m.start(0), m.end(0)
class DisplayMathPattern(InlineProcessor):
def handleMatch(self, m, data):
node = Element('script')
node.set('type', '%s; mode=display' % self._content_type)
if '\\begin' in m.group(1):
node.text = AtomicString(m.group(0))
else:
node.text = AtomicString(m.group(2))
if self._add_preview:
node = _wrap_node(node, m.group(0), 'div')
return node, m.start(0), m.end(0)
class GitLabPreprocessor(Preprocessor):
"""
Preprocessor for GitLab-style standalone syntax:
```math
math goes here
```
"""
def run(self, lines):
inside_math_block = False
math_block_start = None
math_blocks = []
for line_number, line in enumerate(lines):
if line.strip() == '```math' and not inside_math_block:
math_block_start = line_number
inside_math_block = True
if line.strip() == '```' and inside_math_block:
math_blocks.append((math_block_start, line_number))
inside_math_block = False
for math_block_start, math_block_end in reversed(math_blocks):
math_lines = lines[math_block_start + 1:math_block_end]
math_content = '\n'.join(math_lines)
html = '<script type="%s; mode=display">\n%s\n</script>\n'
html %= (self._content_type, math_content)
placeholder = self.md.htmlStash.store(html)
lines[math_block_start:math_block_end + 1] = [placeholder]
return lines
class MathExtension(Extension):
def __init__(self, *args, **kwargs):
self.config = {
'enable_dollar_delimiter':
[False, 'Enable single-dollar delimiter'],
'add_preview': [False, 'Add a preview node before each math node'],
'use_asciimath':
[False, 'Use AsciiMath syntax instead of TeX syntax'],
'use_gitlab_delimiters':
[False, 'Use GitLab-style $`...`$ delimiters'],
}
super(MathExtension, self).__init__(*args, **kwargs)
def extendMarkdown(self, md):
add_preview = self.getConfig('add_preview')
use_asciimath = self.getConfig('use_asciimath')
use_gitlab_delimiters = self.getConfig('use_gitlab_delimiters')
content_type = 'math/asciimath' if use_asciimath else 'math/tex'
inlinemathpatterns = (
InlineMathPattern(r'(?<!\\|\$)(\$)([^\$]+)(\$)'), # $...$
InlineMathPattern(r'(?<!\\)(\\\()(.+?)(\\\))') # \(...\)
)
mathpatterns = (
DisplayMathPattern(r'(?<!\\)(\$\$)([^\$]+)(\$\$)'), # $$...$$
DisplayMathPattern(r'(?<!\\)(\\\[)(.+?)(\\\])'), # \[...\]
DisplayMathPattern( # \begin...\end
r'(?<!\\)(\\begin{([a-z]+?\*?)})(.+?)(\\end{\2})')
)
if not self.getConfig('enable_dollar_delimiter'):
inlinemathpatterns = inlinemathpatterns[1:]
if use_asciimath:
mathpatterns = mathpatterns[:-1] # \begin...\end is TeX only
if use_gitlab_delimiters:
# https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#math
inlinemathpatterns = (
InlineMathPattern(r'(?<!\\)(\$`)([^`]+)(`\$)'), # $`...`$
)
mathpatterns = ()
preprocessor = GitLabPreprocessor(md)
preprocessor._content_type = content_type
# we should have higher priority than 'fenced_code_block' which
# has 25
md.preprocessors.register(preprocessor, 'math-gitlab', 27)
for i, pattern in enumerate(mathpatterns):
pattern._add_preview = add_preview
pattern._content_type = content_type
# we should have higher priority than 'escape' which has 180
# also begin/end pattern should have lower priority than all others
priority = 184 if i == 2 else 185
md.inlinePatterns.register(pattern, 'math-%d' % i, priority)
for i, pattern in enumerate(inlinemathpatterns):
pattern._add_preview = add_preview
pattern._content_type = content_type
# to use gitlab delimiters, we should have higher priority than
# 'backtick' which has 190
priority = 195 if use_gitlab_delimiters else 185
md.inlinePatterns.register(pattern, 'math-inline-%d' % i, priority)
if self.getConfig('enable_dollar_delimiter'):
md.ESCAPED_CHARS.append('$')
def makeExtension(*args, **kwargs):
return MathExtension(*args, **kwargs)