Skip to content

👌 IMPROVE: dollarmath plugin rendering #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 55 additions & 24 deletions mdit_py_plugins/dollarmath/index.py
Original file line number Diff line number Diff line change
@@ -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$$``
Expand All @@ -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'<a href="#{label}" class="mathlabel" title="Permalink to this equation">¶</a>' # 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'<span class="math inline">{content}</span>'

def render_math_inline(self, tokens, idx, options, env) -> str:
return "<{0}>{1}</{0}>".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'<div class="math inline">{content}</div>'

def render_math_block(self, tokens, idx, options, env) -> str:
content = _renderer(str(tokens[idx].content).strip(), {"display_mode": True})
return f'<div class="math block">\n{content}\n</div>\n'

def render_math_block(self, tokens, idx, options, env) -> str:
return "<section>\n<eqn>{0}</eqn>\n</section>\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'<div id="{_id}" class="math block">\n{label}\n{content}\n</div>\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 '<section>\n<eqn>{0}</eqn>\n<span class="eqno">({1})</span>\n</section>\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:
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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 "$"

Expand All @@ -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."""

Expand Down Expand Up @@ -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()))
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ strict_equality = True

[flake8]
max-line-length = 100
extend-ignore = E203
extend-ignore = E203,E731
Loading