From ef4e303c990f423c3234ec40d1249e6c5024fcfa Mon Sep 17 00:00:00 2001 From: inchang-ing <197932532+inchang-ing@users.noreply.github.com> Date: Mon, 28 Sep 2026 21:44:52 +0800 Subject: [PATCH 1/2] fix(block): HTML block start outside a list item closes the list 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 --- markdown_it/rules_block/html_block.py | 10 +++++++ markdown_it/rules_block/paragraph.py | 12 +++++++++ tests/test_html_block_list.py | 38 +++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/test_html_block_list.py 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..3494dacf 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,17 @@ 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..34dcdc1e --- /dev/null +++ b/tests/test_html_block_list.py @@ -0,0 +1,38 @@ +"""Regression tests for HTML blocks interacting with list items (#434).""" + +import pytest + +from markdown_it import MarkdownIt + + +@pytest.fixture +def md() -> MarkdownIt: + return MarkdownIt("commonmark", {"html": True}) + + +def test_html_block_under_indented_closes_list(md: MarkdownIt) -> None: + """A type-7 HTML block at outer indentation ends the list, like cmark.""" + assert md.render("## Opts\n\n- a\n
\n## Next\n") == ( + "

Opts

\n" + "\n" + "
\n## Next\n" + ) + + +def test_html_block_at_item_indent_is_lazy_continuation(md: MarkdownIt) -> None: + """At the item's content indent, type 7 cannot interrupt the paragraph.""" + assert md.render("- a\n
\n") == "\n" + + +def test_html_block_cannot_interrupt_top_level_paragraph(md: MarkdownIt) -> None: + assert md.render("para\n
\n") == "

para\n

\n" + + +def test_plain_text_is_still_lazy_continuation(md: MarkdownIt) -> None: + assert md.render("- a\nb\n") == "\n" + + +def test_type_6_html_block_closes_list(md: MarkdownIt) -> None: + assert md.render("- a\n
\n") == "\n
\n" From 597b89691bda90bcf57655921c4c86f10c6cc58e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Sep 2026 13:45:51 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- markdown_it/rules_block/paragraph.py | 5 ++--- tests/test_html_block_list.py | 6 +----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/markdown_it/rules_block/paragraph.py b/markdown_it/rules_block/paragraph.py index 3494dacf..6b540e40 100644 --- a/markdown_it/rules_block/paragraph.py +++ b/markdown_it/rules_block/paragraph.py @@ -42,9 +42,8 @@ def paragraph(state: StateBlock, startLine: int, endLine: int, silent: bool) -> # 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) + if state.sCount[nextLine] < state.blkIndent and html_block_starts( + state, nextLine ): break diff --git a/tests/test_html_block_list.py b/tests/test_html_block_list.py index 34dcdc1e..66761d7d 100644 --- a/tests/test_html_block_list.py +++ b/tests/test_html_block_list.py @@ -13,11 +13,7 @@ def md() -> MarkdownIt: def test_html_block_under_indented_closes_list(md: MarkdownIt) -> None: """A type-7 HTML block at outer indentation ends the list, like cmark.""" assert md.render("## Opts\n\n- a\n
\n## Next\n") == ( - "

Opts

\n" - "\n" - "
\n## Next\n" + "

Opts

\n\n
\n## Next\n" )