beginnings of callout support

This commit is contained in:
Amy G. Bowersox 2024-08-11 00:11:19 -06:00
parent b20313fe4b
commit a33845f832

View File

@ -13,7 +13,7 @@ from urllib.parse import urlparse
import markdown
from markdown import Markdown
from markdown.blockprocessors import BlockProcessor, BlockParser
from markdown.blockprocessors import BlockProcessor, BlockParser, BlockQuoteProcessor
from markdown.extensions import Extension
from markdown.extensions.footnotes import (FootnoteExtension, FootnoteBlockProcessor, FootnoteInlineProcessor,
FootnoteTreeprocessor, FootnotePostTreeprocessor, FootnotePostprocessor)
@ -861,6 +861,71 @@ class ObsidianStyleFootnotes(FootnoteExtension):
md.postprocessors.register(FootnotePostprocessor(self), 'footnote', 25)
class ObsidianStyleBlockquotes(Extension):
class ObsidianBlockQuote(BlockQuoteProcessor):
CALLOUT = re.compile(r'^\[!([a-z]+)\]([-+])?(?:[ ]+(.*))?')
def normal_blockquote(self, parent: etree.Element, block: str) -> None:
sibling = self.lastChild(parent)
if sibling is not None and sibling.tag == "blockquote":
# Previous block was a blockquote so set that as this blocks parent
quote = sibling
else:
# This is a new blockquote. Create a new parent element.
quote = etree.SubElement(parent, 'blockquote')
# Recursively parse block with blockquote as parent.
# change parser state so blockquotes embedded in lists use `p` tags
self.parser.state.set('blockquote')
self.parser.parseChunk(quote, block)
self.parser.state.reset()
def callout_block(self, parent: etree.Element, lines: list[str]) -> None:
m = self.CALLOUT.match(lines[0])
callout_type = m.group(1)
# folding = m.group(2)
title = m.group(3)
if not title:
title = callout_type.title()
base_div = etree.SubElement(parent, 'div', {'class': 'callout', 'data-callout': callout_type})
title_div = etree.SubElement(base_div, 'div', {'class': 'callout-title'})
# TODO: add title icon here
inner_title_div = etree.SubElement(title_div, 'div', {'class': 'callout-title-inner'})
inner_title_div.text = title
content_div = etree.SubElement(base_div, 'div', {'class': 'callout-content'})
lines.pop(0)
first = True
for line in lines:
if first:
first = False
else:
etree.SubElement(content_div, 'br')
self.parser.state.set('list')
self.parser.parseBlocks(content_div, [line])
self.parser.state.reset()
def run(self, parent: etree.Element, blocks: list[str]) -> None:
block = blocks.pop(0)
lines: list[str] = []
callout = False
m = self.RE.search(block)
if m:
before = block[:m.start()] # Lines before blockquote
# Pass lines before blockquote in recursively for parsing first.
self.parser.parseBlocks(parent, [before])
# Remove `> ` from beginning of each line.
lines = [self.clean(line) for line in block[m.start():].split('\n')]
callout = (self.CALLOUT.match(lines[0]) is not None)
block = '\n'.join(lines)
if callout:
self.callout_block(parent, lines)
else:
self.normal_blockquote(parent, block)
def extendMarkdown(self, md) -> None:
md.parser.blockprocessors.register(ObsidianStyleBlockquotes.ObsidianBlockQuote(md.parser), 'quote', 20)
def create_markdown_parser(context: Context) -> markdown.Markdown:
"""
Creates a Markdown parser with all our extensions loaded.
@ -887,6 +952,7 @@ def create_markdown_parser(context: Context) -> markdown.Markdown:
MetaStripper(),
ObsidianComments(),
ObsidianStyleFootnotes(SUPERSCRIPT_TEXT='[{}]', SEPARATOR='-'),
ObsidianStyleBlockquotes(),
ObsidianImages(context),
ObsidianLinks(context),
ObsidianLists(),