| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- import re
- from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Match
- from ..util import strip_end
- if TYPE_CHECKING:
- from ..block_parser import BlockParser
- from ..core import BaseRenderer, BlockState
- from ..markdown import Markdown
- __all__ = ["def_list"]
- # https://michelf.ca/projects/php-markdown/extra/#def-list
- DEF_PATTERN = (
- r"^(?P<def_list_head>(?:[^\n]+\n)+?)"
- r"\n?(?:"
- r"\:[ \t]+.*\n"
- r"(?:[^\n]+\n)*" # lazy continue line
- r"(?:(?:[ \t]*\n)*[ \t]+[^\n]+\n)*"
- r"(?:[ \t]*\n)*"
- r")+"
- )
- DEF_RE = re.compile(DEF_PATTERN, re.M)
- DD_START_RE = re.compile(r"^:[ \t]+", re.M)
- TRIM_RE = re.compile(r"^ {0,4}", re.M)
- HAS_BLANK_LINE_RE = re.compile(r"\n[ \t]*\n$")
- def parse_def_list(block: "BlockParser", m: Match[str], state: "BlockState") -> int:
- pos = m.end()
- children = list(_parse_def_item(block, m))
- m2 = DEF_RE.match(state.src, pos)
- while m2:
- children.extend(list(_parse_def_item(block, m2)))
- pos = m2.end()
- m2 = DEF_RE.match(state.src, pos)
- state.append_token(
- {
- "type": "def_list",
- "children": children,
- }
- )
- return pos
- def _parse_def_item(block: "BlockParser", m: Match[str]) -> Iterable[Dict[str, Any]]:
- head = m.group("def_list_head")
- for line in head.splitlines():
- yield {
- "type": "def_list_head",
- "text": line,
- }
- src = m.group(0)
- end = len(head)
- m2 = DD_START_RE.search(src, end)
- assert m2 is not None
- start = m2.start()
- prev_blank_line = src[end:start] == "\n"
- while m2:
- m2 = DD_START_RE.search(src, start + 1)
- if not m2:
- break
- end = m2.start()
- text = src[start:end].replace(":", " ", 1)
- children = _process_text(block, text, prev_blank_line)
- prev_blank_line = bool(HAS_BLANK_LINE_RE.search(text))
- yield {
- "type": "def_list_item",
- "children": children,
- }
- start = end
- text = src[start:].replace(":", " ", 1)
- children = _process_text(block, text, prev_blank_line)
- yield {
- "type": "def_list_item",
- "children": children,
- }
- def _process_text(block: "BlockParser", text: str, loose: bool) -> List[Any]:
- text = TRIM_RE.sub("", text)
- state = block.state_cls()
- state.process(strip_end(text))
- # use default list rules
- block.parse(state, block.list_rules)
- tokens = state.tokens
- if not loose and len(tokens) == 1 and tokens[0]["type"] == "paragraph":
- tokens[0]["type"] = "block_text"
- return tokens
- def render_def_list(renderer: "BaseRenderer", text: str) -> str:
- return "<dl>\n" + text + "</dl>\n"
- def render_def_list_head(renderer: "BaseRenderer", text: str) -> str:
- return "<dt>" + text + "</dt>\n"
- def render_def_list_item(renderer: "BaseRenderer", text: str) -> str:
- return "<dd>" + text + "</dd>\n"
- def def_list(md: "Markdown") -> None:
- """A mistune plugin to support def list, spec defined at
- https://michelf.ca/projects/php-markdown/extra/#def-list
- Here is an example:
- .. code-block:: text
- Apple
- : Pomaceous fruit of plants of the genus Malus in
- the family Rosaceae.
- Orange
- : The fruit of an evergreen tree of the genus Citrus.
- It will be converted into HTML:
- .. code-block:: html
- <dl>
- <dt>Apple</dt>
- <dd>Pomaceous fruit of plants of the genus Malus in
- the family Rosaceae.</dd>
- <dt>Orange</dt>
- <dd>The fruit of an evergreen tree of the genus Citrus.</dd>
- </dl>
- :param md: Markdown instance
- """
- md.block.register("def_list", DEF_PATTERN, parse_def_list, before="paragraph")
- if md.renderer and md.renderer.NAME == "html":
- md.renderer.register("def_list", render_def_list)
- md.renderer.register("def_list_head", render_def_list_head)
- md.renderer.register("def_list_item", render_def_list_item)
|