Skip to content

Split list items off an indented block of the previous item - #1670

Open
gabesgh wants to merge 2 commits into
Python-Markdown:masterfrom
gabesgh:fix-list-item-after-indented-block
Open

gabesgh wants to merge 2 commits into
Python-Markdown:masterfrom
gabesgh:fix-list-item-after-indented-block

Conversation

@gabesgh

@gabesgh gabesgh commented Oct 21, 2026 •

Copy link
Copy Markdown

Description

Fixes #845.

When a list item comes right after an indented paragraph or nested list of the previous item, with no blank line in between, it was swallowed into that paragraph as a lazy continuation line.

ListIndentProcessor now ends the indented block at the first line that is indented less than the block and starts another item of the list at that level, so it works at any level of nesting. Those items continue their list as tight items, because no blank line comes before them (the same as Markdown.pl).

Unchanged:

  • Lazy continuation lines, and the First line of a paragraph / - second line example from the issue.
  • With sane_lists, a marker of the other list type is still treated as text.

One behaviour change: a horizontal rule or header right after those items now ends the list, the same as it already does for a list at the top level. Before, it ended up inside the previous item.

Not covered: the equivalent case with def_list, where a : definition comes right after an indented paragraph of the previous definition. I'm happy to look at that separately.

AI Assistance Disclosure

  • No AI tools were used in preparing this PR.

  • AI tools were used and I have disclosed which ones, and have fully reviewed and verified their output.

    AI tools were use to assist with the following:

    • Code
    • Code Comments
    • Documentation
    • Changelog Entry
    • Commit Message
    • The Description Above
    • Other: Tests deterministically created and verified using Claude Code.

Checklist

A list item which directly followed an indented paragraph or nested list
of the previous item, without a blank line between them, was treated as
a lazy continuation line of that block (Python-Markdown#845).

`ListIndentProcessor` now splits the block before the first unindented
line which starts another item of the list. The registered list
processors decide what counts as an item, so `sane_lists` still treats a
marker of the other list type as text. Other unindented lines remain
lazy continuations.

As no blank line comes before them, the split off items continue the
list as tight items: a `listcontinue` parser state tells
`OListProcessor` not to make the first of them loose. They are parsed
like any list block at that level, so a horizontal rule or header after
them now ends the list, as it already does at the top level.

Fixes Python-Markdown#845.

@waylan waylan left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confirmed that the new behavior matches the reference implementation.

Comment thread tests/test_syntax/blocks/test_ul.py Outdated
)

# With `sane_lists`, a marker of the other list type does not start an item.
def test_other_list_type_after_indented_paragraph_sane_lists(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is for the sane-list extension and therefore should be at tests/test_syntax/extensions/sane_lists.py Apparently all of our existing tests are in the old format at tests/extensions/sane_lists.txt. They could be refactored into the new format, but that it not necessary if they do not need to be edited.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved it and also added the test_ prefix so the test runner picks it up like the other files there

@waylan

waylan commented Oct 22, 2026

Copy link
Copy Markdown
Member

Not covered:

  • The same pattern one level deeper, where a nested item comes after a nested item's indented paragraph.

Just saw that. It should definitely be covered at the same time.

@waylan waylan added the requires-changes Awaiting updates after a review. label Oct 22, 2026
@facelessuser

Copy link
Copy Markdown
Collaborator

Not sure how this issue was not found before now. In any event, I confirmed that the new behavior matches the reference implementation.

I believe this issue was generally known; at least I was aware of it. Due to the fact that I rarely run into it, I think it was never a priority for me to look into 🤷🏻.

An item of a nested list which directly follows an indented paragraph
of the previous nested item, without a blank line, was still merged
into that paragraph. Split an indented block at the first line which
is indented less than the block and starts an item of the list at that
level of indentation, not only at unindented lines.

When the rest is still indented, it is split again in the same way,
and its items continue the nested list as tight items, as at the top
level.

Also move the sane_lists test to a test file for the extension.

Refs Python-Markdown#845.
@gabesgh
gabesgh force-pushed the fix-list-item-after-indented-block branch from 5842d7c to 1826557 Compare October 22, 2026 19:58
@gabesgh

gabesgh commented Oct 23, 2026

Copy link
Copy Markdown
Author

Thanks for the review
I moved the sane_lists test to tests/test_syntax/extensions/test_sane_lists.py and extended the fix to nested lists at any depth, with a test.
The nested output matches PHP Markdown on Babelmark. The def_list case is still open and I'm happy to do it in a separate PR.

@gabesgh
gabesgh requested a review from waylan October 23, 2026 01:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

requires-changes Awaiting updates after a review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nested block level items break list without blank line after

3 participants