Skip to content

Commit cd6a5a6

Browse files
authored
Handle nesting of old-school Admonition-like extensions with Blocks (#2795)
Fixes #2794
1 parent 724e4ac commit cd6a5a6

File tree

5 files changed

+98
-4
lines changed

5 files changed

+98
-4
lines changed

docs/src/markdown/about/changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ icon: lucide/scroll-text
33
---
44
# Changelog
55

6+
## 10.17.2
7+
8+
- **FIX**: Blocks: Blocks extensions will now better handle nesting of indented style Admonitions, Details, and Tabbed
9+
and other non-conflicting blocks.
10+
611
## 10.17.1
712

813
- **FIX**: Fix an issue where Highlight can override another extension in the "registered" list in Python Markdown.

docs/src/markdown/faq.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ icon: lucide/circle-question-mark
55

66
## Function References in YAML
77

8-
> How do I specify function references in YAML if I am using a module like [MkDocs][mkdocs]?
8+
/// question
9+
How do I specify function references in YAML if I am using a module like [MkDocs][mkdocs]?
10+
///
911

1012
Pymdown Extensions has a number of extensions that expose customization via options that take function references. If
1113
you are using a project like [MkDocs][mkdocs], which allows a user to configure Python Markdown extensions via YAML,
@@ -47,7 +49,9 @@ markdown_extensions:
4749

4850
## GitHub-ish Configurations
4951

50-
> How do I get GitHub Flavored Markdown?
52+
/// question
53+
How do I get GitHub Flavored Markdown?
54+
///
5155

5256
A recommended GitHub configuration is provided below to emulate a setup that gives a GitHub feel.
5357

pymdownx/__meta__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,5 @@ def parse_version(ver: str) -> Version:
193193
return Version(major, minor, micro, release, pre, post, dev)
194194

195195

196-
__version_info__ = Version(10, 17, 1, "final")
196+
__version_info__ = Version(10, 17, 2, "final")
197197
__version__ = __version_info__._get_canonical()

pymdownx/blocks/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,22 @@ def parse_blocks(self, blocks: list[str], current_parent: etree.Element) -> None
452452
if self.stack:
453453
self.stack[-1].hungry = True
454454

455+
def capture_leaked_content(self, parent: etree.Element, entry: BlockEntry) -> None:
456+
"""
457+
Capture leaked content.
458+
459+
Old school, non-block admonitions, details,
460+
and content tabs strongly control where there content is inserted and
461+
can cause content leakage outside of the Blocks container.
462+
Look for such content and pull it back into the container if found.
463+
"""
464+
465+
last_child = self.lastChild(parent)
466+
if last_child is not None and last_child is not entry.el:
467+
target = entry.block.on_add(entry.el)
468+
parent.remove(last_child)
469+
target.append(last_child)
470+
455471
def run(self, parent: etree.Element, blocks: list[str]) -> None:
456472
"""Convert to details/summary block."""
457473

@@ -492,6 +508,10 @@ def run(self, parent: etree.Element, blocks: list[str]) -> None:
492508
for r in range(len(self.stack)):
493509
entry = self.stack[r]
494510
if entry.hungry and parent is entry.parent:
511+
512+
# Capture leaked content from old-school extensions: admonition, details, tabbed, etc.
513+
self.capture_leaked_content(parent, entry)
514+
495515
# Get the target element and parse
496516
entry.hungry = False
497517
self.parse_blocks(blocks, parent)

tests/test_extensions/test_blocks/test_general_blocks.py

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,13 @@ def test_md_in_html_inserted_correctly(self):
499499
class TestBlocksTab(util.MdCase):
500500
"""Test Blocks tab cases."""
501501

502-
extension = ['pymdownx.blocks.tab', 'pymdownx.superfences', 'markdown.extensions.def_list', 'pymdownx.details']
502+
extension = [
503+
'admonition',
504+
'pymdownx.blocks.tab',
505+
'pymdownx.superfences',
506+
'markdown.extensions.def_list',
507+
'pymdownx.details'
508+
]
503509
extension_configs = {
504510
'pymdownx.blocks.tab': {'alternate_style': True}
505511
}
@@ -897,3 +903,62 @@ def test_tabbed_complex_list_unindented_content(self):
897903
''', # noqa: E501
898904
True
899905
)
906+
907+
def test_nesting_with_legacy_style(self):
908+
"""Test nesting with legacy style admonitions."""
909+
910+
self.check_markdown(
911+
'''
912+
!!! note "Admonition 1"
913+
914+
Admonition 1
915+
916+
/// tab | Tab 1
917+
918+
Test tab 1
919+
920+
!!! note "Admonition 2"
921+
922+
Admonition 2
923+
924+
//// tab | Tab 2
925+
926+
Test tab 2
927+
928+
!!! note "Admonition 3"
929+
930+
Admonition 3
931+
932+
////
933+
///
934+
''',
935+
'''
936+
<div class="admonition note">
937+
<p class="admonition-title">Admonition 1</p>
938+
<p>Admonition 1</p>
939+
<div class="tabbed-set tabbed-alternate" data-tabs="1:1"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">Tab 1</label></div>
940+
<div class="tabbed-content">
941+
<div class="tabbed-block">
942+
<p>Test tab 1</p>
943+
<div class="admonition note">
944+
<p class="admonition-title">Admonition 2</p>
945+
<p>Admonition 2</p>
946+
<div class="tabbed-set tabbed-alternate" data-tabs="2:1"><input checked="checked" id="__tabbed_2_1" name="__tabbed_2" type="radio" /><div class="tabbed-labels"><label for="__tabbed_2_1">Tab 2</label></div>
947+
<div class="tabbed-content">
948+
<div class="tabbed-block">
949+
<p>Test tab 2</p>
950+
<div class="admonition note">
951+
<p class="admonition-title">Admonition 3</p>
952+
<p>Admonition 3</p>
953+
</div>
954+
</div>
955+
</div>
956+
</div>
957+
</div>
958+
</div>
959+
</div>
960+
</div>
961+
</div>
962+
''', # noqa: E501
963+
True
964+
)

0 commit comments

Comments
 (0)