From df3fc1f9f7b84815195edb3710cbb2172cfbee13 Mon Sep 17 00:00:00 2001
From: inchang-ing <197932532+inchang-ing@users.noreply.github.com>
Date: Mon, 28 Sep 2026 13:44:52 +0000
Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20FIX:=20HTML=20block=20start=20ou?=
=?UTF-8?q?tside=20a=20list=20item=20closes=20the=20list=20(#442)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A line that starts an HTML block on an under-indented line was consumed
as a lazy continuation of the item's paragraph, because the type-7
sequence is flagged as unable to terminate a paragraph. cmark and
commonmark.js instead end the list: the line cannot be item content,
and a block start is not a lazy continuation.
End the paragraph when the next line starts an HTML block while being
under-indented for the current container, so the list closes and the
line is offered to the outer containers. At the same container level a
type-7 sequence still cannot interrupt the paragraph, and plain text
remains a lazy continuation, so the behaviors all implementations agree
on are unchanged.
Fixes #434
---
CHANGELOG.md | 1 +
markdown_it/rules_block/html_block.py | 10 +++++++++
markdown_it/rules_block/paragraph.py | 11 +++++++++
tests/test_html_block_list.py | 32 +++++++++++++++++++++++++++
4 files changed, 54 insertions(+)
create mode 100644 tests/test_html_block_list.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 040ff138..55e97a77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
* ⨠Add `--enable-tables` to the CLI for file, standard input and interactive parsing in [#422](https://github.com/executablebooks/markdown-it-py/pull/422)
* š Fix CLI interactive mode joining input lines with an extra newline, which split every line into its own paragraph and broke hard line breaks, in [#172](https://github.com/executablebooks/markdown-it-py/issues/172)
* š Fix trimming and splitting with the Python whitespace set instead of the CommonMark one, which dropped U+001CāU+001F and U+0085 from paragraphs, headings, table cells and fence info strings and let distinct reference labels resolve each other, in [#418](https://github.com/executablebooks/markdown-it-py/pull/418), thanks to [@Nexory](https://github.com/Nexory)
+* š Fix HTML blocks on under-indented lines being absorbed as lazy continuations of a list item's paragraph: a line that starts an HTML block outside the item's content indent now ends the list, like cmark and commonmark.js, in [#442](https://github.com/executablebooks/markdown-it-py/pull/442)
* š Document the Python renderer constructor contract.
* š§ Drop the optional dependency on the `commonmark` package and its benchmark, since the upstream package is unmaintained, in [#401](https://github.com/executablebooks/markdown-it-py/pull/401)
diff --git a/markdown_it/rules_block/html_block.py b/markdown_it/rules_block/html_block.py
index fe7e464e..c147794c 100644
--- a/markdown_it/rules_block/html_block.py
+++ b/markdown_it/rules_block/html_block.py
@@ -31,6 +31,16 @@
]
+def html_block_starts(state: StateBlock, line: int) -> bool:
+ """Whether `line` begins an HTML block of any type (issue #434)."""
+ pos = state.bMarks[line] + state.tShift[line]
+ maximum = state.eMarks[line]
+ if pos >= maximum or state.src[pos] != "<":
+ return False
+ lineText = state.src[pos:maximum]
+ return any(sequence[0].search(lineText) for sequence in HTML_SEQUENCES)
+
+
def html_block(state: StateBlock, startLine: int, endLine: int, silent: bool) -> bool:
LOGGER.debug(
"entering html_block: %s, %s, %s, %s", state, startLine, endLine, silent
diff --git a/markdown_it/rules_block/paragraph.py b/markdown_it/rules_block/paragraph.py
index d27b07b1..6b540e40 100644
--- a/markdown_it/rules_block/paragraph.py
+++ b/markdown_it/rules_block/paragraph.py
@@ -3,6 +3,7 @@
import logging
from ..common.utils import mdTrim
+from .html_block import html_block_starts
from .state_block import StateBlock
LOGGER = logging.getLogger(__name__)
@@ -36,6 +37,16 @@ def paragraph(state: StateBlock, startLine: int, endLine: int, silent: bool) ->
nextLine += 1
continue
+ # An HTML block start on a line under-indented for the current
+ # container is not a lazy continuation: like cmark, end the paragraph
+ # here so the line can open a block in an outer container (issue
+ # #434). At the same container level a type-7 sequence still does not
+ # interrupt the paragraph, which the terminator rules below handle.
+ if state.sCount[nextLine] < state.blkIndent and html_block_starts(
+ state, nextLine
+ ):
+ break
+
# Some tags can terminate paragraph without empty line.
terminate = False
for terminatorRule in terminatorRules:
diff --git a/tests/test_html_block_list.py b/tests/test_html_block_list.py
new file mode 100644
index 00000000..487c6d6d
--- /dev/null
+++ b/tests/test_html_block_list.py
@@ -0,0 +1,32 @@
+"""Regression tests for HTML blocks interacting with list items (#434)."""
+
+from markdown_it import MarkdownIt
+
+
+def test_html_block_under_indented_closes_list() -> None:
+ """A type-7 HTML block at outer indentation ends the list, like cmark."""
+ md = MarkdownIt("commonmark", {"html": True})
+ assert md.render("## Opts\n\n- a\n
\n## Next\n") == (
+ "
para\n