| 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"] |
|
|
| |
| REF_ABBR = ( |
| r"^ {0,3}\*\[(?P<abbr_key>[^\]]+)" + PREVENT_BACKSLASH + r"\]:" |
| r"(?P<abbr_text>(?:[ \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 |
| |
| 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: |
| |
| 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 "<abbr>" + text + "</abbr>" |
| return '<abbr title="' + escape(title) + '">' + text + "</abbr>" |
|
|
|
|
| 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 <abbr title="Hyper Text Markup Language">HTML</abbr> specification |
| is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>. |
| |
| :param md: Markdown instance |
| """ |
| md.block.register("ref_abbr", REF_ABBR, parse_ref_abbr, before="paragraph") |
| |
| md.inline.process_text = types.MethodType(process_text, md.inline) |
| if md.renderer and md.renderer.NAME == "html": |
| md.renderer.register("abbr", render_abbr) |
|
|