Repository: pulldown-cmark/pulldown-cmark
Pull request #873 (2024-03-25 17:55:38)

[0.11] Don't exit `scan_attribute` with the ix pointing at block quote
This is https://github.com/pulldown-cmark/pulldown-cmark/pull/858, but forward-ported to 0.11.


Patch:
diff --git a/pulldown-cmark/specs/regression.txt b/pulldown-cmark/specs/regression.txt
index 5aca680..92547b0 100644
--- a/pulldown-cmark/specs/regression.txt
+++ b/pulldown-cmark/specs/regression.txt
@@ -2511,3 +2511,17 @@ ISSUE 853
 .
 <p><a href="((()))">linky</a></p>
 ````````````````````````````````
+
+ISSUE 857
+
+```````````````````````````````` example
+><span
+title
+>junk
+.
+<blockquote>
+<p>&lt;span
+title
+junk</p>
+</blockquote>
+````````````````````````````````
diff --git a/pulldown-cmark/src/scanners.rs b/pulldown-cmark/src/scanners.rs
index 728d233..73bf33a 100644
--- a/pulldown-cmark/src/scanners.rs
+++ b/pulldown-cmark/src/scanners.rs
@@ -915,18 +915,18 @@ fn scan_attribute(
     buffer_ix: &mut usize,
 ) -> Option<usize> {
     ix += scan_attribute_name(&data[ix..])?;
-    let n_whitespace =
-        scan_whitespace_with_newline_handler(data, ix, newline_handler, buffer, buffer_ix)? - ix;
-    ix += n_whitespace;
+    let ix_after_attribute = ix;
+    ix = scan_whitespace_with_newline_handler_without_buffer(data, ix, newline_handler)?;
     if scan_ch(&data[ix..], b'=') == 1 {
+        ix = scan_whitespace_with_newline_handler(data, ix_after_attribute, newline_handler, buffer, buffer_ix)?;
         ix += 1;
         ix = scan_whitespace_with_newline_handler(data, ix, newline_handler, buffer, buffer_ix)?;
         ix = scan_attribute_value(data, ix, newline_handler, buffer, buffer_ix)?;
-    } else if n_whitespace > 0 {
+        Some(ix)
+    } else {
         // Leave whitespace for next attribute.
-        ix -= 1;
+        Some(ix_after_attribute)
     }
-    Some(ix)
 }
 
 /// Scans whitespace and possibly newlines according to the
@@ -962,6 +962,35 @@ fn scan_whitespace_with_newline_handler(
     Some(i)
 }
 
+/// Scans whitespace and possible newlines according to the behavior defined
+/// by the newline handler.
+///
+/// Unlike [`scan_whitespace_with_newline_handler`], this function doesn't
+/// copy skipped data into a buffer. Typically, if this function
+/// returns `Some`, a call to `scan_whitespace_with_newline_handler` will
+/// soon follow.
+fn scan_whitespace_with_newline_handler_without_buffer(
+    data: &[u8],
+    mut i: usize,
+    newline_handler: Option<&dyn Fn(&[u8]) -> usize>,
+) -> Option<usize> {
+    while i < data.len() {
+        if !is_ascii_whitespace(data[i]) {
+            return Some(i);
+        }
+        if let Some(eol_bytes) = scan_eol(&data[i..]) {
+            let handler = newline_handler?;
+            i += eol_bytes;
+            let skipped_bytes = handler(&data[i..]);
+            i += skipped_bytes;
+        } else {
+            i += 1;
+        }
+    }
+
+    Some(i)
+}
+
 /// Returns the index immediately following the attribute value on success.
 fn scan_attribute_value(
     data: &[u8],


Test patch:
diff --git a/pulldown-cmark/tests/suite/regression.rs b/pulldown-cmark/tests/suite/regression.rs
index d5e1fad..75660e8 100644
--- a/pulldown-cmark/tests/suite/regression.rs
+++ b/pulldown-cmark/tests/suite/regression.rs
@@ -3000,3 +3000,19 @@ fn regression_test_189() {
 
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_190() {
+    let original = r##"><span
+title
+>junk
+"##;
+    let expected = r##"<blockquote>
+<p>&lt;span
+title
+junk</p>
+</blockquote>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}