diff --git a/mdit_py_plugins/dollarmath/index.py b/mdit_py_plugins/dollarmath/index.py index 240bbba..5fe0381 100644 --- a/mdit_py_plugins/dollarmath/index.py +++ b/mdit_py_plugins/dollarmath/index.py @@ -1,18 +1,22 @@ import re -from typing import Callable +from typing import Any, Callable, Dict, Optional from markdown_it import MarkdownIt -from markdown_it.common.utils import isWhiteSpace +from markdown_it.common.utils import escapeHtml, isWhiteSpace from markdown_it.rules_block import StateBlock from markdown_it.rules_inline import StateInline def dollarmath_plugin( md: MarkdownIt, + *, allow_labels: bool = True, allow_space: bool = True, allow_digits: bool = True, double_inline: bool = False, + label_normalizer: Optional[Callable[[str], str]] = None, + renderer: Optional[Callable[[str, Dict[str, Any]], str]] = None, + label_renderer: Optional[Callable[[str], str]] = None, ) -> None: """Plugin for parsing dollar enclosed math, e.g. inline: ``$a=1$``, block: ``$$b=2$$`` @@ -27,39 +31,64 @@ def dollarmath_plugin( before/after the opening/closing ``$``, e.g. ``1$`` or ``$2``. This is useful when also using currency. :param double_inline: Search for double-dollar math within inline contexts + :param label_normalizer: Function to normalize the label, + by default replaces whitespace with `-` + :param renderer: Function to render content: `(str, {"display_mode": bool}) -> str`, + by default escapes HTML + :param label_renderer: Function to render labels, by default creates anchor """ + if label_normalizer is None: + label_normalizer = lambda label: re.sub(r"\s+", "-", label) md.inline.ruler.before( "escape", "math_inline", math_inline_dollar(allow_space, allow_digits, double_inline), ) - md.add_render_rule("math_inline", render_math_inline) + md.block.ruler.before( + "fence", "math_block", math_block_dollar(allow_labels, label_normalizer) + ) - md.block.ruler.before("fence", "math_block", math_block_dollar(allow_labels)) - md.add_render_rule("math_block", render_math_block) - md.add_render_rule("math_block_eqno", render_math_block_eqno) + # TODO the current render rules are really just for testing + # would be good to allow "proper" math rendering, + # e.g. https://github.com/roniemartinez/latex2mathml + if renderer is None: + _renderer = lambda content, _: escapeHtml(content) + else: + _renderer = renderer -# TODO the current render rules are really just for testing -# would be good to allow "proper" math rendering, e.g. https://github.com/roniemartinez/latex2mathml + if label_renderer is None: + _label_renderer = ( + lambda label: f'' # noqa: E501 + ) + else: + _label_renderer = label_renderer + def render_math_inline(self, tokens, idx, options, env) -> str: + content = _renderer(str(tokens[idx].content).strip(), {"display_mode": False}) + return f'{content}' -def render_math_inline(self, tokens, idx, options, env) -> str: - return "<{0}>{1}".format( - "eqn" if tokens[idx].markup == "$$" else "eq", tokens[idx].content - ) + def render_math_inline_double(self, tokens, idx, options, env) -> str: + content = _renderer(str(tokens[idx].content).strip(), {"display_mode": True}) + return f'
{content}
' + def render_math_block(self, tokens, idx, options, env) -> str: + content = _renderer(str(tokens[idx].content).strip(), {"display_mode": True}) + return f'
\n{content}\n
\n' -def render_math_block(self, tokens, idx, options, env) -> str: - return "
\n{0}\n
\n".format(tokens[idx].content) + def render_math_block_label(self, tokens, idx, options, env) -> str: + content = _renderer(str(tokens[idx].content).strip(), {"display_mode": True}) + _id = tokens[idx].info + label = _label_renderer(tokens[idx].info) + return f'
\n{label}\n{content}\n
\n' + md.add_render_rule("math_inline", render_math_inline) + md.add_render_rule("math_inline_double", render_math_inline_double) -def render_math_block_eqno(self, tokens, idx, options, env) -> str: - return '
\n{0}\n({1})\n
\n'.format( - tokens[idx].content, tokens[idx].info - ) + md.add_render_rule("math_block", render_math_block) + md.add_render_rule("math_block_label", render_math_block_label) def is_escaped(state: StateInline, back_pos: int, mod: int = 0) -> bool: @@ -146,7 +175,7 @@ def _math_inline_dollar(state: StateInline, silent: bool) -> bool: # find closing $ pos = state.pos + 1 + (1 if is_double else 0) found_closing = False - while True: + while not found_closing: try: end = state.srcCharCode.index(0x24, pos) except ValueError: @@ -167,7 +196,6 @@ def _math_inline_dollar(state: StateInline, silent: bool) -> bool: end += 1 found_closing = True - break if not found_closing: return False @@ -199,7 +227,9 @@ def _math_inline_dollar(state: StateInline, silent: bool) -> bool: return False if not silent: - token = state.push("math_inline", "math", 0) + token = state.push( + "math_inline_double" if is_double else "math_inline", "math", 0 + ) token.content = text token.markup = "$$" if is_double else "$" @@ -216,6 +246,7 @@ def _math_inline_dollar(state: StateInline, silent: bool) -> bool: def math_block_dollar( allow_labels: bool = True, + label_normalizer: Optional[Callable[[str], str]] = None, ) -> Callable[[StateBlock, int, int, bool], bool]: """Generate block dollar rule.""" @@ -249,7 +280,7 @@ def _math_block_dollar( # search for end of block on same line lineText = state.src[startPos:end] if len(lineText.strip()) > 3: - lineText = state.src[startPos:end] + if lineText.strip().endswith("$$"): haveEndMarker = True end = end - 2 - (len(lineText) - len(lineText.strip())) @@ -295,13 +326,13 @@ def _math_block_dollar( state.line = nextLine + (1 if haveEndMarker else 0) - token = state.push("math_block_eqno" if label else "math_block", "math", 0) + token = state.push("math_block_label" if label else "math_block", "math", 0) token.block = True token.content = state.src[startPos + 2 : end] token.markup = "$$" token.map = [startLine, state.line] if label: - token.info = label + token.info = label if label_normalizer is None else label_normalizer(label) return True diff --git a/setup.cfg b/setup.cfg index ee42a10..79eeabd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,4 +60,4 @@ strict_equality = True [flake8] max-line-length = 100 -extend-ignore = E203 +extend-ignore = E203,E731 diff --git a/tests/fixtures/dollar_math.md b/tests/fixtures/dollar_math.md index dd0f423..a1c2d43 100644 --- a/tests/fixtures/dollar_math.md +++ b/tests/fixtures/dollar_math.md @@ -16,35 +16,35 @@ single character inline equation. (valid=True) . $a$ . -

a

+

a

. inline equation with single greek character (valid=True) . $\\varphi$ . -

\\varphi

+

\\varphi

. simple equation starting and ending with numbers. (valid=True) . $1+1=2$ . -

1+1=2

+

1+1=2

. simple equation including special html character. (valid=True) . $1+1<3$ . -

1+1<3

+

1+1<3

. equation including backslashes. (valid=True) . $a \\backslash$ . -

a \\backslash

+

a \\backslash

. use of currency symbol, i.e. digits before/after opening/closing (valid=True) @@ -58,42 +58,42 @@ use of currency symbol (valid=True) . If you solve $1+2$ you get $3 . -

If you solve 1+2 you get $3

+

If you solve 1+2 you get $3

. inline fraction (valid=True) . $\\frac{1}{2}$ . -

\\frac{1}{2}

+

\\frac{1}{2}

. inline column vector (valid=True) . $\\begin{pmatrix}x\\\\y\\end{pmatrix}$ . -

\\begin{pmatrix}x\\\\y\\end{pmatrix}

+

\\begin{pmatrix}x\\\\y\\end{pmatrix}

. inline bold vector notation (valid=True) . ${\\tilde\\bold e}_\\alpha$ . -

{\\tilde\\bold e}_\\alpha

+

{\\tilde\\bold e}_\\alpha

. exponentiation (valid=True) . $a^{b}$ . -

a^{b}

+

a^{b}

. conjugate complex (valid=True) . $a^\*b$ with $a^\*$ . -

a^\*b with a^\*

+

a^\*b with a^\*

. Inline multi-line (valid=True) @@ -101,8 +101,8 @@ Inline multi-line (valid=True) a $a \not=1$ b . -

a a -\not=1 b

+

a a +\not=1 b

. Inline multi-line with newline (valid=False) @@ -119,28 +119,28 @@ single block equation, greek index (valid=True) . $$e_\\alpha$$ . -
-e_\\alpha -
+
+e_\\alpha +
. display equation on its own single line. (valid=True) . -$$1+1=2$$ +$$1+1=2$$ . -
-1+1=2 -
+
+1+1=2 +
. display equation with number on its own single line. (valid=True) . -$$1+1=2$$ (2) +$$1+1=2$$ (2) . -
-1+1=2 -(2) -
+
+ +1+1=2 +
. inline equation followed by block equation. (valid=True) @@ -149,19 +149,19 @@ ${e}_x$ $$e_\\alpha$$ . -

{e}_x

-
-e_\\alpha -
+

{e}_x

+
+e_\\alpha +
. underline tests (valid=True) . $$c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha$$ . -
-c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha -
+
+c{\\bold e}_x = a{\\bold e}_\\alpha - b\\tilde{\\bold e}_\\alpha +
. non-numeric character before opening $ or @@ -171,30 +171,30 @@ a$1+1=2$ $1+1=2$b c$x$d . -

a1+1=2 -1+1=2b -cxd

+

a1+1=2 +1+1=2b +cxd

. following dollar character '$' is allowed. (valid=True) . -$x$ $ +$x$ $ . -

x $

+

x $

. consecutive inline equations. (valid=True) . $x$ $y$ . -

x y

+

x y

. inline equation after '-' sign in text. (valid=True) . so-what is $x$ . -

so-what is x

+

so-what is x

. display equation with line breaks. (valid=True) @@ -203,11 +203,9 @@ $$ 1+1=2 $$ . -
- +
1+1=2 - -
+ . multiple equations (valid=True) @@ -220,16 +218,12 @@ $$ b = 2 $$ . -
- +
a = 1 - -
-
- + +
b = 2 - -
+ . equation followed by a labelled equation (valid=True) @@ -242,62 +236,58 @@ $$ b = 2 $$ (1) . -
- +
a = 1 - -
-
- + +
+ b = 2 - -(1) -
+ . multiline equation. (valid=True) . $$\\begin{matrix} - f & = & 2 + x + 3 \\ - & = & 5 + x + f & = & 2 + x + 3 \\ + & = & 5 + x \\end{matrix}$$ . -
-\\begin{matrix} - f & = & 2 + x + 3 \\ - & = & 5 + x -\\end{matrix} -
+
+\\begin{matrix} + f & = & 2 + x + 3 \\ + & = & 5 + x +\\end{matrix} +
. vector equation. (valid=True) . -$$\\begin{pmatrix}x_2 \\\\ y_2 \\end{pmatrix} = +$$\\begin{pmatrix}x_2 \\\\ y_2 \\end{pmatrix} = \\begin{pmatrix} A & B \\\\ C & D \\end{pmatrix}\\cdot \\begin{pmatrix} x_1 \\\\ y_1 \\end{pmatrix}$$ . -
-\\begin{pmatrix}x_2 \\\\ y_2 \\end{pmatrix} = -\\begin{pmatrix} A & B \\\\ C & D \\end{pmatrix}\\cdot -\\begin{pmatrix} x_1 \\\\ y_1 \\end{pmatrix} -
+
+\\begin{pmatrix}x_2 \\\\ y_2 \\end{pmatrix} = +\\begin{pmatrix} A & B \\\\ C & D \\end{pmatrix}\\cdot +\\begin{pmatrix} x_1 \\\\ y_1 \\end{pmatrix} +
. display equation with equation number. (valid=True) . $$f(x) = x^2 - 1$$ (1) . -
-f(x) = x^2 - 1 -(1) -
+
+ +f(x) = x^2 - 1 +
. inline equation following code section. (valid=True) . `code`$a-b$ . -

codea-b

+

codea-b

. equation following code block. (valid=True) @@ -309,9 +299,9 @@ $$a+b$$ .
code
 
-
-a+b -
+
+a+b +
. numbered equation following code block. (valid=True) @@ -323,10 +313,10 @@ $$a+b$$(1) .
code
 
-
-a+b -(1) -
+
+ +a+b +
. Equations in list. (valid=True) @@ -336,10 +326,10 @@ Equations in list. (valid=True) 1. $3+4$ .
    -
  1. 1+2
  2. -
  3. 2+3 +
  4. 1+2
  5. +
  6. 2+3
      -
    1. 3+4
    2. +
    3. 3+4
@@ -349,26 +339,26 @@ Inline sum. (valid=True) . $\\sum\_{i=1}^n$ . -

\\sum\_{i=1}^n

+

\\sum\_{i=1}^n

. Sum without equation number. (valid=True) . $$\\sum\_{i=1}^n$$ . -
-\\sum\_{i=1}^n -
+
+\\sum\_{i=1}^n +
. Sum with equation number. (valid=True) . $$\\sum\_{i=1}\^n$$ (2) . -
-\\sum\_{i=1}\^n -(2) -
+
+ +\\sum\_{i=1}\^n +
. equation number always vertically aligned. (valid=True) @@ -377,24 +367,24 @@ $${\\bold e}(\\varphi) = \\begin{pmatrix} \\cos\\varphi\\\\\\sin\\varphi \\end{pmatrix}$$ (3) . -
-{\\bold e}(\\varphi) = \\begin{pmatrix} +
+ +{\\bold e}(\\varphi) = \\begin{pmatrix} \\cos\\varphi\\\\\\sin\\varphi -\\end{pmatrix} -(3) -
+\\end{pmatrix} + . inline equations in blockquote. (valid=True) . -> see $a = b + c$ -> $c^2=a^2+b^2$ (2) -> $c^2=a^2+b^2$ +> see $a = b + c$ +> $c^2=a^2+b^2$ (2) +> $c^2=a^2+b^2$ .
-

see a = b + c -c^2=a^2+b^2 (2) -c^2=a^2+b^2

+

see a = b + c +c^2=a^2+b^2 (2) +c^2=a^2+b^2

. @@ -404,14 +394,14 @@ display equation in blockquote. (valid=True) > > $$ a+b=c$$ (2) > -> in blockquote. +> in blockquote. .

formula

-
- a+b=c -(2) -
+
+ +a+b=c +

in blockquote.

. @@ -425,15 +415,13 @@ $$ (abc) - ab $c=1$ d . -
- +
+ a=1 \\ b=2 - -(abc) -
+ . @@ -443,8 +431,8 @@ dollar '$' characters. (valid=True) \\$1+1=2$ $1+1=2\\$ . -

\1+1=2 -1+1=2\\

+

\1+1=2 +1+1=2\\

. empty line between text and display formula is required. (valid=False) @@ -495,49 +483,49 @@ math-escaping: internal escaped $: . $p_2 = \$1$ . -

p_2 = \$1

+

p_2 = \$1

. math-escaping: double-escaped start $: . \\$p_2 = 1$ . -

\p_2 = 1

+

\p_2 = 1

. math-escaping: double-escaped end $: . $p_2 = \\$a . -

p_2 = \\a

+

p_2 = \\a

. Inline double-dollar start: . $$a=1$$ b . -

a=1 b

+

a=1
b

. Inline double-dollar end: . a $$a=1$$ . -

a a=1

+

a

a=1

. Inline double-dollar enclosed: . a $$a=1$$ (1) b . -

a a=1 (1) b

+

a

a=1
(1) b

. Inline double-dollar, escaped: . a \$$a=1$$ b . -

a $a=1$ b

+

a $a=1$ b

. Inline mixed single/double dollars: @@ -548,9 +536,17 @@ $$ $$ i.e., $[\alpha \bar{X}, \infty)$ is a lower 1-sided $1-\alpha$ confidence bound for $\mu$. . -

Hence, for \alpha \in (0, 1), - - \mathbb P (\alpha \bar{X} \ge \mu) \le \alpha; - -i.e., [\alpha \bar{X}, \infty) is a lower 1-sided 1-\alpha confidence bound for \mu.

+

Hence, for \alpha \in (0, 1), +

\mathbb P (\alpha \bar{X} \ge \mu) \le \alpha;
+i.e., [\alpha \bar{X}, \infty) is a lower 1-sided 1-\alpha confidence bound for \mu.

+. + +display equation with label containing whitespace. (valid=True) +. +$$1+1=2$$ (a b) +. +
+ +1+1=2 +
. diff --git a/tests/test_dollarmath.py b/tests/test_dollarmath.py index 3db3a78..1cb1b12 100644 --- a/tests/test_dollarmath.py +++ b/tests/test_dollarmath.py @@ -49,7 +49,7 @@ def test_block_func(): block_func(state, 0, 10, False) print(tokens[0].as_dict()) assert tokens[0].as_dict() == { - "type": "math_block_eqno", + "type": "math_block_label", "tag": "math", "nesting": 0, "attrs": None, @@ -86,7 +86,7 @@ def test_plugin_parse(data_regression): "line,title,input,expected", read_fixture_file(FIXTURE_PATH.joinpath("dollar_math.md")), ) -def test_dollarmath_fixturess(line, title, input, expected): +def test_dollarmath_fixtures(line, title, input, expected): md = MarkdownIt("commonmark").use( dollarmath_plugin, allow_space=False, allow_digits=False, double_inline=True ) diff --git a/tests/test_dollarmath/test_plugin_parse.yml b/tests/test_dollarmath/test_plugin_parse.yml index 338d18c..789cbe7 100644 --- a/tests/test_dollarmath/test_plugin_parse.yml +++ b/tests/test_dollarmath/test_plugin_parse.yml @@ -18,7 +18,7 @@ meta: {} nesting: 0 tag: math - type: math_block_eqno + type: math_block_label - attrs: null block: true children: null