diff options
Diffstat (limited to 'mdit_py_plugins/texmath/index.py')
-rw-r--r-- | mdit_py_plugins/texmath/index.py | 307 |
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("<","<") + # 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": "$$", + }, + ], + }, +} |