Spaces:
Running
Running
# Definition List Extension for Python-Markdown | |
# ============================================= | |
# Adds parsing of Definition Lists to Python-Markdown. | |
# See https://Python-Markdown.github.io/extensions/definition_lists | |
# for documentation. | |
# Original code Copyright 2008 [Waylan Limberg](http://achinghead.com) | |
# All changes Copyright 2008-2014 The Python Markdown Project | |
# License: [BSD](https://opensource.org/licenses/bsd-license.php) | |
""" | |
Adds parsing of Definition Lists to Python-Markdown. | |
See the [documentation](https://Python-Markdown.github.io/extensions/definition_lists) | |
for details. | |
""" | |
from __future__ import annotations | |
from . import Extension | |
from ..blockprocessors import BlockProcessor, ListIndentProcessor | |
import xml.etree.ElementTree as etree | |
import re | |
class DefListProcessor(BlockProcessor): | |
""" Process Definition Lists. """ | |
RE = re.compile(r'(^|\n)[ ]{0,3}:[ ]{1,3}(.*?)(\n|$)') | |
NO_INDENT_RE = re.compile(r'^[ ]{0,3}[^ :]') | |
def test(self, parent: etree.Element, block: str) -> bool: | |
return bool(self.RE.search(block)) | |
def run(self, parent: etree.Element, blocks: list[str]) -> bool | None: | |
raw_block = blocks.pop(0) | |
m = self.RE.search(raw_block) | |
terms = [term.strip() for term in | |
raw_block[:m.start()].split('\n') if term.strip()] | |
block = raw_block[m.end():] | |
no_indent = self.NO_INDENT_RE.match(block) | |
if no_indent: | |
d, theRest = (block, None) | |
else: | |
d, theRest = self.detab(block) | |
if d: | |
d = '{}\n{}'.format(m.group(2), d) | |
else: | |
d = m.group(2) | |
sibling = self.lastChild(parent) | |
if not terms and sibling is None: | |
# This is not a definition item. Most likely a paragraph that | |
# starts with a colon at the beginning of a document or list. | |
blocks.insert(0, raw_block) | |
return False | |
if not terms and sibling.tag == 'p': | |
# The previous paragraph contains the terms | |
state = 'looselist' | |
terms = sibling.text.split('\n') | |
parent.remove(sibling) | |
# Acquire new sibling | |
sibling = self.lastChild(parent) | |
else: | |
state = 'list' | |
if sibling is not None and sibling.tag == 'dl': | |
# This is another item on an existing list | |
dl = sibling | |
if not terms and len(dl) and dl[-1].tag == 'dd' and len(dl[-1]): | |
state = 'looselist' | |
else: | |
# This is a new list | |
dl = etree.SubElement(parent, 'dl') | |
# Add terms | |
for term in terms: | |
dt = etree.SubElement(dl, 'dt') | |
dt.text = term | |
# Add definition | |
self.parser.state.set(state) | |
dd = etree.SubElement(dl, 'dd') | |
self.parser.parseBlocks(dd, [d]) | |
self.parser.state.reset() | |
if theRest: | |
blocks.insert(0, theRest) | |
class DefListIndentProcessor(ListIndentProcessor): | |
""" Process indented children of definition list items. """ | |
# Definition lists need to be aware of all list types | |
ITEM_TYPES = ['dd', 'li'] | |
""" Include `dd` in list item types. """ | |
LIST_TYPES = ['dl', 'ol', 'ul'] | |
""" Include `dl` is list types. """ | |
def create_item(self, parent: etree.Element, block: str) -> None: | |
""" Create a new `dd` or `li` (depending on parent) and parse the block with it as the parent. """ | |
dd = etree.SubElement(parent, 'dd') | |
self.parser.parseBlocks(dd, [block]) | |
class DefListExtension(Extension): | |
""" Add definition lists to Markdown. """ | |
def extendMarkdown(self, md): | |
""" Add an instance of `DefListProcessor` to `BlockParser`. """ | |
md.parser.blockprocessors.register(DefListIndentProcessor(md.parser), 'defindent', 85) | |
md.parser.blockprocessors.register(DefListProcessor(md.parser), 'deflist', 25) | |
def makeExtension(**kwargs): # pragma: no cover | |
return DefListExtension(**kwargs) | |