Source code for markdown_environments.cited_blockquote

import re
import xml.etree.ElementTree as etree

from markdown.blockprocessors import BlockProcessor
from markdown.extensions import Extension

from . import utils


class CitedBlockquoteProcessor(BlockProcessor):

    START_PATTERN = re.compile(r"^\\begin{cited_blockquote}", flags=re.MULTILINE)
    END_PATTERN = re.compile(r"^\\end{cited_blockquote}", flags=re.MULTILINE)
    CITATION_START_PATTERN = re.compile(r"^\\begin{citation}", flags=re.MULTILINE)
    CITATION_END_PATTERN = re.compile(r"^\\end{citation}", flags=re.MULTILINE)

    def __init__(self, *args, html_class: str, citation_html_class: str, **kwargs):
        super().__init__(*args, **kwargs)
        self.html_class = html_class
        self.citation_html_class = citation_html_class

    def test(self, parent, block):
        return self.START_PATTERN.match(block)

    def run(self, parent, blocks):
        org_blocks = list(blocks)

        # remove blockquote starting delim
        blocks[0] = self.START_PATTERN.sub("", blocks[0])

        # find and remove citation starting delim
        delim_found = False
        citation_start_i = None
        for i, block in enumerate(blocks):
            if self.CITATION_START_PATTERN.match(block):
                delim_found = True
                # remove ending delim and note which block citation started on
                # (as citation content itself is an unknown number of blocks)
                citation_start_i = i
                blocks[i] = self.CITATION_START_PATTERN.sub("", block)
                break
        # if no starting delim for citation, restore and do nothing
        if not delim_found:
            blocks.clear()
            blocks.extend(org_blocks)
            return False

        # find and remove citation ending delim, and extract element
        # start search at citation starting delim; citation is at end so this is a good optimization
        delim_found = False
        for i, block in enumerate(blocks[citation_start_i:], start=citation_start_i):
            if self.CITATION_END_PATTERN.search(block):
                delim_found = True
                # remove ending delim
                blocks[i] = self.CITATION_END_PATTERN.sub("", block)
                # build HTML for citation
                citation_elem = etree.Element("cite")
                if self.citation_html_class != "":
                    citation_elem.set("class", self.citation_html_class)
                blocks[i] = blocks[i].rstrip() # remove trailing whitespace from the newline into `\end{}`
                self.parser.parseBlocks(citation_elem, blocks[citation_start_i:i + 1])
                # remove used blocks
                for _ in range(citation_start_i, i + 1):
                    blocks.pop(citation_start_i)
                break
        # if no ending delim for citation, restore and do nothing
        if not delim_found:
            blocks.clear()
            blocks.extend(org_blocks)
            return False

        # find and remove blockquote ending delim, and extract element
        delim_found = False
        for i, block in enumerate(blocks):
            if self.END_PATTERN.search(block):
                delim_found = True
                # remove ending delim
                blocks[i] = self.END_PATTERN.sub("", block)
                # build HTML for blockquote
                blockquote_elem = etree.SubElement(parent, "blockquote")
                if self.html_class != "":
                    blockquote_elem.set("class", self.html_class)
                self.parser.parseBlocks(blockquote_elem, blocks[:i + 1])
                parent.append(citation_elem) # make sure citation comes at the end
                # remove used blocks
                for _ in range(i + 1):
                    blocks.pop(0)
                break
        # if no ending delim for blockquote, restore and do nothing
        if not delim_found:
            blocks.clear()
            blocks.extend(org_blocks)
            return False
        return True


[docs] class CitedBlockquoteExtension(Extension): r""" A blockquote with a citation/quote attribution underneath. Usage: .. code-block:: py import markdown from markdown_environments import CitedBlockquoteExtension input_text = ... output_text = markdown.markdown(input_text, extensions=[ CitedBlockquoteExtension(html_class="give", citation_html_class="you") ]) Markdown usage: .. code-block:: md \begin{cited_blockquote} <quote> \begin{citation} <citation> \end{citation} \end{cited_blockquote} becomes… .. code-block:: html <blockquote class="[html_class]"> [quote] </blockquote> <cite class="[citation_html_class]"> [citation] </cite> Note: The `citation` block can be placed anywhere within the `cited_blockquote` block, as long as, of course, there are blank lines before and after the `citation` block. """
[docs] def __init__(self, **kwargs): """ Initialize cited blockquote extension, with configuration options passed as the following keyword arguments: - **html_class** (*str*) -- HTML `class` attribute to add to blockquotes. Defaults to `""`. - **citation_html_class** (*str*) -- HTML `class` attribute to add to captions. Defaults to `""`. """ self.config = { "html_class": [ "", "HTML `class` attribute to add to cited blockquote. Defaults to `\"\"`." ], "citation_html_class": [ "", "HTML `class` attribute to add to cited blockquote's citation. Defaults to `\"\"`." ] } utils.init_extension_with_configs(self, **kwargs)
def extendMarkdown(self, md): md.parser.blockprocessors.register( CitedBlockquoteProcessor(md.parser, **self.getConfigs()), "cited_blockquote", 105 )
def makeExtension(**kwargs): return CitedBlockquoteExtension(**kwargs)