| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- 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<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
- # 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 "<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")
- # 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)
|