Skip to content

Commit

Permalink
Merge pull request #10 from sondregronas/feature/codefence-support-an…
Browse files Browse the repository at this point in the history
…d-bugfix

Bugfix + don't convert callouts within codefences
  • Loading branch information
sondregronas authored Feb 23, 2024
2 parents 416f52b + 61b31e7 commit 91b48a7
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 2 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "mkdocs-callouts"
version = "1.11.1"
version = "1.12.0"
keywords = ["mkdocs", "mkdocs-plugin", "markdown", "callouts", "admonitions", "obsidian"]
description = "A simple plugin that converts Obsidian style callouts and converts them into mkdocs supported 'admonitions' (a.k.a. callouts)."
readme = "README.md"
Expand Down
11 changes: 10 additions & 1 deletion src/mkdocs_callouts/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ def __init__(self, convert_aliases: bool = True, breakless_lists: bool = True):
self.text_in_prev_line: bool = False
self.list_in_prev_line: bool = False

# Check that the callout isn't inside a codefence
self.in_codefence: bool = False

def _parse_block_syntax(self, block) -> str:
"""
Converts the callout syntax from obsidian into the mkdocs syntax
Expand Down Expand Up @@ -115,7 +118,7 @@ def _convert_content(self, line: str) -> str:
if match and self.indent_levels:
# Get the last indent level and remove any higher levels when the current line
# has a lower indent level than the last line.
if match.group(1).count('>') < self.indent_levels[-1]:
while match.group(1).count('>') < self.indent_levels[-1]:
self.indent_levels = self.indent_levels[:-1]
indent = '\t' * self.indent_levels[-1]
line = re.sub(rf'^ ?(?:> ?){{{self.indent_levels[-1]}}} ?', indent, line)
Expand All @@ -132,6 +135,12 @@ def convert_line(self, line: str) -> str:
returns _convert_block if line matches that of a callout block syntax,
if line is not a block syntax, it will return _convert_content.
"""
if line.startswith('```'):
self.in_codefence = not self.in_codefence
if self.in_codefence:
# Reset the indent levels if the callout is inside a codefence
self.indent_levels = list()
return line
return self._convert_block(line) or self._convert_content(line)

def parse(self, markdown: str) -> str:
Expand Down
29 changes: 29 additions & 0 deletions tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,32 @@ def test_breakless_lists():
mkdown = '> [!INFO]\n> text\n> 1. item 1\n> 2. item 2'
result = '!!! info\n\ttext\n\t\n\t1. item 1\n\t2. item 2'
assert (parser.parse(mkdown) == result)

def test_edgecase_in_nested_callouts():
# Go from 1, 2, 3 callouts back to 1
mkdown = '> [!INFO]\n> > [!INFO]\n> > > [!INFO]\n> > > Text\n> Text'
result = '!!! info\n\t!!! info\n\t\t!!! info\n\t\t\tText\n\tText'
assert (convert(mkdown) == result)

def test_callout_in_codeblocks():
mkdown = '```markdown\n> [!INFO]\n> Text\n```'
assert (convert(mkdown) == mkdown)

# Edgecase where the callout might continue after a codeblock
mkdown = '> [!INFO]\n```\n> code\n```\n> Text'
result = '!!! info\n```\n> code\n```\n> Text'
assert (convert(mkdown) == result)

mkdown = '> [!INFO]\n```\n> code\n```\n> Blockquote\n> [!INFO]\n> Text'
result = '!!! info\n```\n> code\n```\n> Blockquote\n!!! info\n\tText'
assert (convert(mkdown) == result)


@pytest.mark.xfail
def test_callout_in_codeblocks_within_callout():
# A codefence within a callout containing a callout will still be converted
# Though it's probably not worth the effort to handle this edgecase in the parser
# Given how unlikely it is to occur in practice
mkdown = '> [!INFO]\n> ```\n> [!INFO]\n> ```'
result = '!!! info\n\t```\n> [!INFO]\n\t```'
assert (convert(mkdown) == result)

0 comments on commit 91b48a7

Please sign in to comment.