beginnings of callout support
This commit is contained in:
parent
b20313fe4b
commit
a33845f832
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue
Block a user