Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions src/Tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,9 +416,22 @@ export class _Tokenizer<ParserOutput = string, RendererOutput = string> {
list.raw = list.raw.trimEnd();

// Item child tokens handled here at end because we needed to have the final item to trim it first
// First pass: tokenize items and finalize list.loose from spacers before placing checkboxes
for (const item of list.items) {
this.lexer.state.top = false;
item.tokens = this.lexer.blockTokens(item.text, []);

if (!list.loose) {
// Check if list should be loose
const spacers = item.tokens.filter(t => t.type === 'space');
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => this.rules.other.anyLine.test(t.raw));

list.loose = hasMultipleLineBreaks;
Comment on lines +426 to +429

@styfle styfle Aug 15, 2026

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.

In a follow up, we could change this one be one line without

list.loose = item.tokens.some(t => t.type === 'space' && this.rules.other.anyLine.test(t.raw));

Though no need to change here since its just moving existing code

}
}

// Second pass: place task checkboxes using the final list.loose
for (const item of list.items) {
const itemToken = item.tokens[0];
if (item.task && (itemToken?.type === 'text' || itemToken?.type === 'paragraph')) {
// Remove checkbox markdown from item tokens
Expand Down Expand Up @@ -460,14 +473,6 @@ export class _Tokenizer<ParserOutput = string, RendererOutput = string> {
} else if (item.task) {
item.task = false;
}

if (!list.loose) {
// Check if list should be loose
const spacers = item.tokens.filter(t => t.type === 'space');
const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => this.rules.other.anyLine.test(t.raw));

list.loose = hasMultipleLineBreaks;
}
}

// Set all items to loose if list is loose
Expand Down
20 changes: 20 additions & 0 deletions test/specs/new/list_loose_nested_tasks.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<ul>
<li><p><input disabled="" type="checkbox"> Prepare the project</p>
<ul>
<li><input disabled="" type="checkbox"> Install dependencies</li>
<li><input checked="" disabled="" type="checkbox"> Update Marked</li>
<li><input disabled="" type="checkbox"> Check the rendering</li>
</ul>
</li>
<li><p><input checked="" disabled="" type="checkbox"> Run the tests</p>
<ul>
<li><p><input checked="" disabled="" type="checkbox"> Test lists</p>
</li>
<li><p><input disabled="" type="checkbox"> Test tables</p>
<ul>
<li><input disabled="" type="checkbox"> Test nested cases</li>
</ul>
</li>
</ul>
</li>
</ul>
11 changes: 11 additions & 0 deletions test/specs/new/list_loose_nested_tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* [ ] Prepare the project

* [ ] Install dependencies
* [x] Update Marked
* [ ] Check the rendering
* [x] Run the tests

* [x] Test lists
* [ ] Test tables

* [ ] Test nested cases