summaryrefslogtreecommitdiffstats
path: root/mdit_py_plugins/texmath/index.py
diff options
context:
space:
mode:
Diffstat (limited to 'mdit_py_plugins/texmath/index.py')
-rw-r--r--mdit_py_plugins/texmath/index.py307
1 files changed, 307 insertions, 0 deletions
diff --git a/mdit_py_plugins/texmath/index.py b/mdit_py_plugins/texmath/index.py
new file mode 100644
index 0000000..ecf178c
--- /dev/null
+++ b/mdit_py_plugins/texmath/index.py
@@ -0,0 +1,307 @@
+import re
+from typing import Optional
+
+from markdown_it import MarkdownIt
+from markdown_it.common.utils import charCodeAt
+
+
+def texmath_plugin(md: MarkdownIt, delimiters="dollars", macros: Optional[dict] = None):
+ """Plugin ported from
+ `markdown-it-texmath <https://github.com/goessner/markdown-it-texmath>`__.
+
+ It parses TeX math equations set inside opening and closing delimiters:
+
+ .. code-block:: md
+
+ $\\alpha = \\frac{1}{2}$
+
+ :param delimiters: one of: brackets, dollars, gitlab, julia, kramdown
+
+ """
+ macros = macros or {}
+
+ if delimiters in rules:
+ for rule_inline in rules[delimiters]["inline"]:
+ md.inline.ruler.before(
+ "escape", rule_inline["name"], make_inline_func(rule_inline)
+ )
+
+ def render_math_inline(self, tokens, idx, options, env):
+ return rule_inline["tmpl"].format(
+ render(tokens[idx].content, False, macros)
+ )
+
+ md.add_render_rule(rule_inline["name"], render_math_inline)
+
+ for rule_block in rules[delimiters]["block"]:
+ md.block.ruler.before(
+ "fence", rule_block["name"], make_block_func(rule_block)
+ )
+
+ def render_math_block(self, tokens, idx, options, env):
+ return rule_block["tmpl"].format(
+ render(tokens[idx].content, True, macros), tokens[idx].info
+ )
+
+ md.add_render_rule(rule_block["name"], render_math_block)
+
+
+def applyRule(rule, string: str, begin, inBlockquote):
+
+ if not (
+ string.startswith(rule["tag"], begin)
+ and (rule["pre"](string, begin) if "pre" in rule else True)
+ ):
+ return False
+
+ match = rule["rex"].match(string[begin:]) # type: re.Match
+
+ if not match or match.start() != 0:
+ return False
+
+ lastIndex = match.end() + begin - 1
+ if "post" in rule:
+ if not (
+ rule["post"](string, lastIndex) # valid post-condition
+ # remove evil blockquote bug (https:#github.com/goessner/mdmath/issues/50)
+ and (not inBlockquote or "\n" not in match.group(1))
+ ):
+ return False
+ return match
+
+
+def make_inline_func(rule):
+ def _func(state, silent):
+ res = applyRule(rule, state.src, state.pos, False)
+ if res:
+ if not silent:
+ token = state.push(rule["name"], "math", 0)
+ token.content = res[1] # group 1 from regex ..
+ token.markup = rule["tag"]
+
+ state.pos += res.end()
+
+ return bool(res)
+
+ return _func
+
+
+def make_block_func(rule):
+ def _func(state, begLine, endLine, silent):
+ begin = state.bMarks[begLine] + state.tShift[begLine]
+ res = applyRule(rule, state.src, begin, state.parentType == "blockquote")
+ if res:
+ if not silent:
+ token = state.push(rule["name"], "math", 0)
+ token.block = True
+ token.content = res[1]
+ token.info = res[len(res.groups())]
+ token.markup = rule["tag"]
+
+ line = begLine
+ endpos = begin + res.end() - 1
+
+ while line < endLine:
+ if endpos >= state.bMarks[line] and endpos <= state.eMarks[line]:
+ # line for end of block math found ...
+ state.line = line + 1
+ break
+ line += 1
+
+ state.pos = begin + res.end()
+
+ return bool(res)
+
+ return _func
+
+
+def dollar_pre(str, beg):
+ prv = charCodeAt(str[beg - 1], 0) if beg > 0 else False
+ return (
+ (not prv) or prv != 0x5C and (prv < 0x30 or prv > 0x39) # no backslash,
+ ) # no decimal digit .. before opening '$'
+
+
+def dollar_post(string, end):
+ try:
+ nxt = string[end + 1] and charCodeAt(string[end + 1], 0)
+ except IndexError:
+ return True
+ return (
+ (not nxt) or (nxt < 0x30) or (nxt > 0x39)
+ ) # no decimal digit .. after closing '$'
+
+
+def render(tex, displayMode, macros):
+ return tex
+ # TODO better HTML renderer port for math
+ # try:
+ # res = katex.renderToString(tex,{throwOnError:False,displayMode,macros})
+ # except:
+ # res = tex+": "+err.message.replace("<","&lt;")
+ # return res
+
+
+# def use(katex): # math renderer used ...
+# texmath.katex = katex; # ... katex solely at current ...
+# return texmath;
+# }
+
+
+# All regexes areg global (g) and sticky (y), see:
+# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
+
+rules: dict = {
+ "brackets": {
+ "inline": [
+ {
+ "name": "math_inline",
+ "rex": re.compile(r"^\\\((.+?)\\\)", re.DOTALL),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "\\(",
+ }
+ ],
+ "block": [
+ {
+ "name": "math_block_eqno",
+ "rex": re.compile(
+ r"^\\\[(((?!\\\]|\\\[)[\s\S])+?)\\\]\s*?\(([^)$\r\n]+?)\)", re.M
+ ),
+ "tmpl": '<section class="eqno"><eqn>{0}</eqn><span>({1})</span></section>',
+ "tag": "\\[",
+ },
+ {
+ "name": "math_block",
+ "rex": re.compile(r"^\\\[([\s\S]+?)\\\]", re.M),
+ "tmpl": "<section>\n<eqn>{0}</eqn>\n</section>\n",
+ "tag": "\\[",
+ },
+ ],
+ },
+ "gitlab": {
+ "inline": [
+ {
+ "name": "math_inline",
+ "rex": re.compile(r"^\$`(.+?)`\$"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "$`",
+ }
+ ],
+ "block": [
+ {
+ "name": "math_block_eqno",
+ "rex": re.compile(
+ r"^`{3}math\s+?([^`]+?)\s+?`{3}\s*?\(([^)$\r\n]+?)\)", re.M
+ ),
+ "tmpl": '<section class="eqno">\n<eqn>{0}</eqn><span>({1})</span>\n</section>\n', # noqa: E501
+ "tag": "```math",
+ },
+ {
+ "name": "math_block",
+ "rex": re.compile(r"^`{3}math\s+?([^`]+?)\s+?`{3}", re.M),
+ "tmpl": "<section>\n<eqn>{0}</eqn>\n</section>\n",
+ "tag": "```math",
+ },
+ ],
+ },
+ "julia": {
+ "inline": [
+ {
+ "name": "math_inline",
+ "rex": re.compile(r"^`{2}([^`]+?)`{2}"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "``",
+ },
+ {
+ "name": "math_inline",
+ "rex": re.compile(r"^\$(\S[^$\r\n]*?[^\s\\]{1}?)\$"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "$",
+ "pre": dollar_pre,
+ "post": dollar_post,
+ },
+ {
+ "name": "math_single",
+ "rex": re.compile(r"^\$([^$\s\\]{1}?)\$"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "$",
+ "pre": dollar_pre,
+ "post": dollar_post,
+ },
+ ],
+ "block": [
+ {
+ "name": "math_block_eqno",
+ "rex": re.compile(
+ r"^`{3}math\s+?([^`]+?)\s+?`{3}\s*?\(([^)$\r\n]+?)\)", re.M
+ ),
+ "tmpl": '<section class="eqno"><eqn>{0}</eqn><span>({1})</span></section>',
+ "tag": "```math",
+ },
+ {
+ "name": "math_block",
+ "rex": re.compile(r"^`{3}math\s+?([^`]+?)\s+?`{3}", re.M),
+ "tmpl": "<section><eqn>{0}</eqn></section>",
+ "tag": "```math",
+ },
+ ],
+ },
+ "kramdown": {
+ "inline": [
+ {
+ "name": "math_inline",
+ "rex": re.compile(r"^\${2}([^$\r\n]*?)\${2}"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "$$",
+ }
+ ],
+ "block": [
+ {
+ "name": "math_block_eqno",
+ "rex": re.compile(r"^\${2}([^$]*?)\${2}\s*?\(([^)$\r\n]+?)\)", re.M),
+ "tmpl": '<section class="eqno"><eqn>{0}</eqn><span>({1})</span></section>',
+ "tag": "$$",
+ },
+ {
+ "name": "math_block",
+ "rex": re.compile(r"^\${2}([^$]*?)\${2}", re.M),
+ "tmpl": "<section><eqn>{0}</eqn></section>",
+ "tag": "$$",
+ },
+ ],
+ },
+ "dollars": {
+ "inline": [
+ {
+ "name": "math_inline",
+ "rex": re.compile(r"^\$(\S[^$]*?[^\s\\]{1}?)\$"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "$",
+ "pre": dollar_pre,
+ "post": dollar_post,
+ },
+ {
+ "name": "math_single",
+ "rex": re.compile(r"^\$([^$\s\\]{1}?)\$"),
+ "tmpl": "<eq>{0}</eq>",
+ "tag": "$",
+ "pre": dollar_pre,
+ "post": dollar_post,
+ },
+ ],
+ "block": [
+ {
+ "name": "math_block_eqno",
+ "rex": re.compile(r"^\${2}([^$]*?)\${2}\s*?\(([^)$\r\n]+?)\)", re.M),
+ "tmpl": '<section class="eqno">\n<eqn>{0}</eqn><span>({1})</span>\n</section>\n', # noqa: E501
+ "tag": "$$",
+ },
+ {
+ "name": "math_block",
+ "rex": re.compile(r"^\${2}([^$]*?)\${2}", re.M),
+ "tmpl": "<section>\n<eqn>{0}</eqn>\n</section>\n",
+ "tag": "$$",
+ },
+ ],
+ },
+}