Repository: Python-Markdown/markdown
Pull request #1102 (2021-02-05 19:56:33)

Fix a case where content under admonitions weren't dettabbed properly
Admonitions would not render the following case properly:

```
!!! note "Admonition"
    - Parent 1

        - Child 1
        - Child 2
```

But would render this properly:

```
!!! note "Admontion"
    - Parent 1
        - Child 1
        - Child 2
```

The problem stemmed from bad detabbing of the content, where in the first example, the child list was parsed like this:

```
  - Child 1
      - Child 2
```

This pull addresses the issue of ensuring that each block is processed properly so that content that is highly sensitive to indentation works correctly.


facelessuser: I'm ignoring the "checklinks" failure. I feel that should be fixed in a separate issue.
waylan: I think you have an error in your first example. As is, it renders to 

```html
<div class="admonition note">
<p class="admonition-title">Admonition</p>
</div>
<ul>
<li>
<p>Parent 1</p>
<ul>
<li>Child 1</li>
<li>Child 2</li>
</ul>
</li>
</ul>
```

.. which is correct, The `- Parent 1` line is not indented by four spaces and is therefore not a child of the admonition. Therefore it gets parsed as a list outside of the admonition as it should.

However, if you use proper indentation, like this:

```markdown
!!! note "Admonition"
    - Parent 1

        - Child 1
        - Child 2
```

Then we get the rather strange output:

```html
<div class="admonition note">
<p class="admonition-title">Admonition</p>
<ul>
<li>
<p>Parent 1</p>
<ul>
<li>Child 1<ul>
<li>Child 2</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
```

And yes that is a bug which needs fixing. So, thank you.
facelessuser: Sorry, that was a formatting issue for the comment, not the actual test or failure.
facelessuser: I've corrected the comment in the pull, but the issue is valid.

Patch:
diff --git a/docs/change_log/release-3.3.md b/docs/change_log/release-3.3.md
index 79e22b2..12273bc 100644
--- a/docs/change_log/release-3.3.md
+++ b/docs/change_log/release-3.3.md
@@ -101,6 +101,7 @@ The following bug fixes are included in the 3.3 release:
 * Fix unescaping of HTML characters `<>` in CodeHilite (#990).
 * Fix complex scenarios involving lists and admonitions (#1004).
 * Fix complex scenarios with nested ordered and unordered lists in a definition list (#918).
+* Fix corner cases with lists under admonitions.
 
 [spec]: https://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
 [fenced_code]: ../extensions/fenced_code_blocks.md
diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py
index 7d31a7f..8518e50 100644
--- a/markdown/blockprocessors.py
+++ b/markdown/blockprocessors.py
@@ -78,13 +78,15 @@ class BlockProcessor:
         else:
             return None
 
-    def detab(self, text):
+    def detab(self, text, length=None):
         """ Remove a tab from the front of each line of the given text. """
+        if length is None:
+            length = self.tab_length
         newtext = []
         lines = text.split('\n')
         for line in lines:
-            if line.startswith(' '*self.tab_length):
-                newtext.append(line[self.tab_length:])
+            if line.startswith(' ' * length):
+                newtext.append(line[length:])
             elif not line.strip():
                 newtext.append('')
             else:
diff --git a/markdown/extensions/admonition.py b/markdown/extensions/admonition.py
index 916d931..9c66b4f 100644
--- a/markdown/extensions/admonition.py
+++ b/markdown/extensions/admonition.py
@@ -48,7 +48,7 @@ class AdmonitionProcessor(BlockProcessor):
         self.current_sibling = None
         self.content_indention = 0
 
-    def get_sibling(self, parent, block):
+    def parse_content(self, parent, block):
         """Get sibling admontion.
 
         Retrieve the appropriate siblimg element. This can get trickly when
@@ -56,13 +56,16 @@ class AdmonitionProcessor(BlockProcessor):
 
         """
 
+        old_block = block
+        the_rest = ''
+
         # We already acquired the block via test
         if self.current_sibling is not None:
             sibling = self.current_sibling
-            block = block[self.content_indent:]
+            block, the_rest = self.detab(block, self.content_indent)
             self.current_sibling = None
             self.content_indent = 0
-            return sibling, block
+            return sibling, block, the_rest
 
         sibling = self.lastChild(parent)
 
@@ -96,17 +99,19 @@ class AdmonitionProcessor(BlockProcessor):
                 sibling = None
 
             if sibling is not None:
+                indent += self.tab_length
+                block, the_rest = self.detab(old_block, indent)
                 self.current_sibling = sibling
                 self.content_indent = indent
 
-        return sibling, block
+        return sibling, block, the_rest
 
     def test(self, parent, block):
 
         if self.RE.search(block):
             return True
         else:
-            return self.get_sibling(parent, block)[0] is not None
+            return self.parse_content(parent, block)[0] is not None
 
     def run(self, parent, blocks):
         block = blocks.pop(0)
@@ -116,10 +121,9 @@ class AdmonitionProcessor(BlockProcessor):
             if m.start() > 0:
                 self.parser.parseBlocks(parent, [block[:m.start()]])
             block = block[m.end():]  # removes the first line
+            block, theRest = self.detab(block)
         else:
-            sibling, block = self.get_sibling(parent, block)
-
-        block, theRest = self.detab(block)
+            sibling, block, theRest = self.parse_content(parent, block)
 
         if m:
             klass, title = self.get_class_and_title(m)


Test patch:
diff --git a/tests/test_syntax/extensions/test_admonition.py b/tests/test_syntax/extensions/test_admonition.py
index b8d4f06..44c70d1 100644
--- a/tests/test_syntax/extensions/test_admonition.py
+++ b/tests/test_syntax/extensions/test_admonition.py
@@ -213,3 +213,33 @@ class TestAdmonition(TestCase):
             ),
             extensions=['admonition']
         )
+
+    def test_admontion_detabbing(self):
+        self.assertMarkdownRenders(
+            self.dedent(
+                '''
+                !!! note "Admonition"
+                    - Parent 1
+
+                        - Child 1
+                        - Child 2
+                '''
+            ),
+            self.dedent(
+                '''
+                <div class="admonition note">
+                <p class="admonition-title">Admonition</p>
+                <ul>
+                <li>
+                <p>Parent 1</p>
+                <ul>
+                <li>Child 1</li>
+                <li>Child 2</li>
+                </ul>
+                </li>
+                </ul>
+                </div>
+                '''
+            ),
+            extensions=['admonition']
+        )