Skip to content

Commit b55410f

Browse files
fix: parse empty list item with trailing space (#3984)
A bare list marker followed only by a space (e.g. `- ` or `1. `) was not recognized as a list and fell through to a paragraph, so `- ` rendered as `<p>- </p>` instead of an empty list item. A marker with no trailing space (`-`, `1.`) and an empty item that continues an existing list were already handled correctly, making the behavior inconsistent. The block `list` rule required content after the marker's whitespace (`[ \t][^\n]+?`), so a marker followed only by whitespace failed to match. Relax it to `[ \t][^\n]*?` so a marker with trailing whitespace and no content still starts a list, matching the CommonMark reference and markdown-it. To keep CommonMark's rule that an empty list item cannot interrupt a paragraph, the paragraph list-interrupt patterns now require a non-blank character after the marker, so `foo\n+ ` stays a single paragraph while `foo\n- bar` still interrupts.
1 parent c6e667b commit b55410f

3 files changed

Lines changed: 33 additions & 4 deletions

File tree

src/rules.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ const def = edit(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +
136136
.replace('title', /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/)
137137
.getRegex();
138138

139-
const list = edit(/^(bull)([ \t][^\n]+?)?(?:\n|$)/)
139+
const list = edit(/^(bull)([ \t][^\n]*?)?(?:\n|$)/)
140140
.replace(/bull/g, bullet)
141141
.getRegex();
142142

@@ -170,7 +170,7 @@ const paragraph = edit(_paragraph)
170170
.replace('|table', '')
171171
.replace('blockquote', ' {0,3}>')
172172
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
173-
.replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // only lists starting from 1 can interrupt
173+
.replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]') // only non-empty lists starting from 1 can interrupt
174174
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
175175
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
176176
.getRegex();
@@ -214,7 +214,7 @@ const gfmTable = edit(
214214
.replace('blockquote', ' {0,3}>')
215215
.replace('code', '(?: {4}| {0,3}\t)[^\\n]')
216216
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
217-
.replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // only lists starting from 1 can interrupt
217+
.replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // any bullet ends the table rows
218218
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
219219
.replace('tag', _tag) // tables can be interrupted by type (6) html blocks
220220
.getRegex();
@@ -230,7 +230,7 @@ const blockGfm: Record<BlockKeys, RegExp> = {
230230
.replace('table', gfmTable) // interrupt paragraphs with table
231231
.replace('blockquote', ' {0,3}>')
232232
.replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
233-
.replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // only lists starting from 1 can interrupt
233+
.replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]') // only non-empty lists starting from 1 can interrupt
234234
.replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)')
235235
.replace('tag', _tag) // pars can be interrupted by type (6) html blocks
236236
.getRegex(),
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<ul>
2+
<li></li>
3+
</ul>
4+
<ul>
5+
<li></li>
6+
</ul>
7+
<ol>
8+
<li></li>
9+
</ol>
10+
<ol>
11+
<li></li>
12+
</ol>
13+
<p>foo
14+
+</p>
15+
<p>bar
16+
1.</p>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-
2+
3+
*
4+
5+
1.
6+
7+
1)
8+
9+
foo
10+
+
11+
12+
bar
13+
1.

0 commit comments

Comments
 (0)