import re import types from typing import TYPE_CHECKING, Match from ..helpers import PREVENT_BACKSLASH from ..util import escape if TYPE_CHECKING: from ..block_parser import BlockParser from ..core import BaseRenderer, BlockState, InlineState from ..inline_parser import InlineParser from ..markdown import Markdown __all__ = ["abbr"] # https://michelf.ca/projects/php-markdown/extra/#abbr REF_ABBR = ( r"^ {0,3}\*\[(?P[^\]]+)" + PREVENT_BACKSLASH + r"\]:" r"(?P(?:[ \t]*\n(?: {3,}|\t)[^\n]+)|(?:[^\n]*))$" ) def parse_ref_abbr(block: "BlockParser", m: Match[str], state: "BlockState") -> int: ref = state.env.get("ref_abbrs") if not ref: ref = {} key = m.group("abbr_key") text = m.group("abbr_text") ref[key] = text.strip() state.env["ref_abbrs"] = ref # abbr definition can split paragraph state.append_token({"type": "blank_line"}) return m.end() + 1 def process_text(inline: "InlineParser", text: str, state: "InlineState") -> None: ref = state.env.get("ref_abbrs") if not ref: return state.append_token({"type": "text", "raw": text}) if state.tokens: last = state.tokens[-1] if last["type"] == "text": state.tokens.pop() text = last["raw"] + text abbrs_re = state.env.get("abbrs_re") if not abbrs_re: abbrs_re = re.compile(r"|".join(re.escape(k) for k in ref.keys())) state.env["abbrs_re"] = abbrs_re pos = 0 while pos < len(text): m = abbrs_re.search(text, pos) if not m: break end_pos = m.start() if end_pos > pos: hole = text[pos:end_pos] state.append_token({"type": "text", "raw": hole}) label = m.group(0) state.append_token( {"type": "abbr", "children": [{"type": "text", "raw": label}], "attrs": {"title": ref[label]}} ) pos = m.end() if pos == 0: # special case, just pure text state.append_token({"type": "text", "raw": text}) elif pos < len(text): state.append_token({"type": "text", "raw": text[pos:]}) def render_abbr(renderer: "BaseRenderer", text: str, title: str) -> str: if not title: return "" + text + "" return '' + text + "" def abbr(md: "Markdown") -> None: """A mistune plugin to support abbreviations, spec defined at https://michelf.ca/projects/php-markdown/extra/#abbr Here is an example: .. code-block:: text The HTML specification is maintained by the W3C. *[HTML]: Hyper Text Markup Language *[W3C]: World Wide Web Consortium It will be converted into HTML: .. code-block:: html The HTML specification is maintained by the W3C. :param md: Markdown instance """ md.block.register("ref_abbr", REF_ABBR, parse_ref_abbr, before="paragraph") # replace process_text md.inline.process_text = types.MethodType(process_text, md.inline) # type: ignore[method-assign] if md.renderer and md.renderer.NAME == "html": md.renderer.register("abbr", render_abbr)