Repository: raphlinus/pulldown-cmark (rust)

Issue:
Lists are parsed in places that cmark doesn't see them
Fuzzing result

Events from pulldown-cmark:

```rust
";\n*\n%\n" -> [
  Start(Paragraph)
    Text(Borrowed(";"))
  End(Paragraph)
  Start(List(None))
    Start(Item)
    End(Item)
  End(List(false))
  Start(Paragraph)
    Text(Borrowed("%"))
  End(Paragraph)
]
```

Events from pandoc:

```rust
";\n*\n%\n" -> [
  Start(Paragraph)
    Text(Boxed(";"))
    SoftBreak
    Text(Boxed("*"))
    SoftBreak
    Text(Boxed("%"))
  End(Paragraph)
]
```

Events from commonmark.js:

```rust
";\n*\n%\n" -> [
  Start(Paragraph)
    Text(Boxed(";"))
    SoftBreak
    Text(Boxed("*"))
    SoftBreak
    Text(Boxed("%"))
  End(Paragraph)
]
```


Pull request description:
Fix spec violation on empty-list-item-cannot-interrupt-paragrph rule
Fixes #680

This was definitely a spec violation. **Emphasis mine.**

> The following rules define [list items](https://spec.commonmark.org/0.30/#list-items):
>
>  1. Basic case. If a sequence of lines Ls constitute a sequence of blocks Bs starting with a character other than a space or tab, and M is a list marker of width W followed by 1 ≤ N ≤ 4 spaces of indentation, then the result of prepending M and the following spaces to the first line of Ls, and indenting subsequent lines of Ls by W + N spaces, is a list item with Bs as its contents. The type of the list item (bullet or ordered) is determined by the type of its list marker. If the list item is ordered, then it is also assigned a start number, based on the ordered list marker.
>
>     Exceptions:
>      1. When the first list item in a list interrupts a paragraph—that is, when it starts on a line that would otherwise count as paragraph continuation text—then (a) **the lines Ls must not begin with a blank line**, and (b) if the list item is ordered, the start number must be 1.

Patch:
diff --git a/specs/regression.txt b/specs/regression.txt
index a23395d..4486a1e 100644
--- a/specs/regression.txt
+++ b/specs/regression.txt
@@ -1258,10 +1258,34 @@ ISSUE 657
 <p>*<em><em>R]</em>-</em></p>
 ````````````````````````````````
 
-The below example adds a trailing space.
-
 ```````````````````````````````` example
 ****foo*bar*baz****
 .
 <p><strong><em><em>foo</em>bar</em>baz</strong>**</p>
 ````````````````````````````````
+
+
+ISSUE 680
+
+```````````````````````````````` example
+;
+*
+%
+.
+<p>;
+*
+%</p>
+````````````````````````````````
+
+The below example adds a trailing space.
+
+```````````````````````````````` example
+;
+* 
+%
+.
+<p>;
+* 
+%</p>
+````````````````````````````````
+
diff --git a/src/firstpass.rs b/src/firstpass.rs
index 2f8b58a..8b0c3c5 100644
--- a/src/firstpass.rs
+++ b/src/firstpass.rs
@@ -1492,7 +1492,7 @@ fn scan_paragraph_interrupt_no_table(bytes: &[u8], current_container: bool, gfm_
             // we don't allow interruption by either empty lists or
             // numbered lists starting at an index other than 1
             (delim == b'*' || delim == b'-' || delim == b'+' || index == 1)
-                && !scan_empty_list(&bytes[ix..])
+                && scan_blank_line(&bytes[ix..]).is_none()
         })
         || bytes.starts_with(b"<")
             && (get_html_end_tag(&bytes[1..]).is_some() || starts_html_block_type_6(&bytes[1..]))
diff --git a/src/scanners.rs b/src/scanners.rs
index d74d6ca..2284d33 100644
--- a/src/scanners.rs
+++ b/src/scanners.rs
@@ -699,20 +699,7 @@ pub(crate) fn scan_blockquote_start(data: &[u8]) -> Option<usize> {
     }
 }
 
-/// This already assumes the list item has been scanned.
-pub(crate) fn scan_empty_list(data: &[u8]) -> bool {
-    let mut ix = 0;
-    for _ in 0..2 {
-        if let Some(bytes) = scan_blank_line(&data[ix..]) {
-            ix += bytes;
-        } else {
-            return false;
-        }
-    }
-    true
-}
-
-// return number of bytes scanned, delimiter, start index, and indent
+/// return number of bytes scanned, delimiter, start index, and indent
 pub(crate) fn scan_listitem(bytes: &[u8]) -> Option<(usize, u8, usize, usize)> {
     let mut c = *bytes.get(0)?;
     let (w, start) = match c {


Test patch:
diff --git a/tests/suite/regression.rs b/tests/suite/regression.rs
index b1f121d..472019f 100644
--- a/tests/suite/regression.rs
+++ b/tests/suite/regression.rs
@@ -1501,3 +1501,31 @@ fn regression_test_100() {
 
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_101() {
+    let original = r##";
+*
+%
+"##;
+    let expected = r##"<p>;
+*
+%</p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_102() {
+    let original = r##";
+* 
+%
+"##;
+    let expected = r##"<p>;
+* 
+%</p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}