From 4a52a71956a8d46fcb7294ac71734504bb09bcc2 Mon Sep 17 00:00:00 2001 From: S. Solomon Darnell Date: Fri, 28 Mar 2025 21:52:21 -0500 Subject: two version of R2R are here --- .../markdown/extensions/admonition.py | 183 +++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 .venv/lib/python3.12/site-packages/markdown/extensions/admonition.py (limited to '.venv/lib/python3.12/site-packages/markdown/extensions/admonition.py') diff --git a/.venv/lib/python3.12/site-packages/markdown/extensions/admonition.py b/.venv/lib/python3.12/site-packages/markdown/extensions/admonition.py new file mode 100644 index 00000000..01c2316d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/markdown/extensions/admonition.py @@ -0,0 +1,183 @@ +# Admonition extension for Python-Markdown +# ======================================== + +# Adds rST-style admonitions. Inspired by [rST][] feature with the same name. + +# [rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions + +# See https://Python-Markdown.github.io/extensions/admonition +# for documentation. + +# Original code Copyright [Tiago Serafim](https://www.tiagoserafim.com/). + +# All changes Copyright The Python Markdown Project + +# License: [BSD](https://opensource.org/licenses/bsd-license.php) + + +""" +Adds rST-style admonitions. Inspired by [rST][] feature with the same name. + +[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions + +See the [documentation](https://Python-Markdown.github.io/extensions/admonition) +for details. +""" + +from __future__ import annotations + +from . import Extension +from ..blockprocessors import BlockProcessor +import xml.etree.ElementTree as etree +import re +from typing import TYPE_CHECKING + +if TYPE_CHECKING: # pragma: no cover + from markdown import blockparser + + +class AdmonitionExtension(Extension): + """ Admonition extension for Python-Markdown. """ + + def extendMarkdown(self, md): + """ Add Admonition to Markdown instance. """ + md.registerExtension(self) + + md.parser.blockprocessors.register(AdmonitionProcessor(md.parser), 'admonition', 105) + + +class AdmonitionProcessor(BlockProcessor): + + CLASSNAME = 'admonition' + CLASSNAME_TITLE = 'admonition-title' + RE = re.compile(r'(?:^|\n)!!! ?([\w\-]+(?: +[\w\-]+)*)(?: +"(.*?)")? *(?:\n|$)') + RE_SPACES = re.compile(' +') + + def __init__(self, parser: blockparser.BlockParser): + """Initialization.""" + + super().__init__(parser) + + self.current_sibling: etree.Element | None = None + self.content_indent = 0 + + def parse_content(self, parent: etree.Element, block: str) -> tuple[etree.Element | None, str, str]: + """Get sibling admonition. + + Retrieve the appropriate sibling element. This can get tricky when + dealing with lists. + + """ + + old_block = block + the_rest = '' + + # We already acquired the block via test + if self.current_sibling is not None: + sibling = self.current_sibling + block, the_rest = self.detab(block, self.content_indent) + self.current_sibling = None + self.content_indent = 0 + return sibling, block, the_rest + + sibling = self.lastChild(parent) + + if sibling is None or sibling.tag != 'div' or sibling.get('class', '').find(self.CLASSNAME) == -1: + sibling = None + else: + # If the last child is a list and the content is sufficiently indented + # to be under it, then the content's sibling is in the list. + last_child = self.lastChild(sibling) + indent = 0 + while last_child is not None: + if ( + sibling is not None and block.startswith(' ' * self.tab_length * 2) and + last_child is not None and last_child.tag in ('ul', 'ol', 'dl') + ): + + # The expectation is that we'll find an `
+ if sibling.tag in ('li', 'dd') and sibling.text: + text = sibling.text + sibling.text = '' + p = etree.SubElement(sibling, 'p') + p.text = text + + div = sibling + + self.parser.parseChunk(div, block) + + if theRest: + # This block contained unindented line(s) after the first indented + # line. Insert these lines as the first block of the master blocks + # list for future processing. + blocks.insert(0, theRest) + + def get_class_and_title(self, match: re.Match[str]) -> tuple[str, str | None]: + klass, title = match.group(1).lower(), match.group(2) + klass = self.RE_SPACES.sub(' ', klass) + if title is None: + # no title was provided, use the capitalized class name as title + # e.g.: `!!! note` will render + # `
Note
` + title = klass.split(' ', 1)[0].capitalize() + elif title == '': + # an explicit blank title should not be rendered + # e.g.: `!!! warning ""` will *not* render `p` with a title + title = None + return klass, title + + +def makeExtension(**kwargs): # pragma: no cover + return AdmonitionExtension(**kwargs) -- cgit v1.2.3