Repository: raphlinus/pulldown-cmark (rust)

Issue:
[fuzz result] can_open/close is looking at the syntax of the containing block
Consider this markdown:

```markdown
>**#*

**#*
```

The block quote should not change the way emphasis is parsed. But in pulldown-cmark, it does:

```html
<blockquote>
<p>**#*</p>
</blockquote>
<p>*<em>#</em></p>
```

<details>

Events from pulldown-cmark:

```rust
">**#2*#\n" -> [
  Start(BlockQuote)
    Start(Paragraph)
      Text(Boxed("**#2*#"))
    End(Paragraph)
  End(BlockQuote)
]
```

Events from pandoc:

```rust
">**#2*#\n" -> [
  Start(BlockQuote)
    Start(Paragraph)
      Text(Boxed("*"))
      Start(Emphasis)
        Text(Boxed("#2"))
      End(Emphasis)
      Text(Boxed("#"))
    End(Paragraph)
  End(BlockQuote)
]
```

Events from commonmark.js:

```rust
">**#2*#\n" -> [
  Start(BlockQuote)
    Start(Paragraph)
      Text(Boxed("*"))
      Start(Emphasis)
        Text(Boxed("#2"))
      End(Emphasis)
      Text(Boxed("#"))
    End(Paragraph)
  End(BlockQuote)
]
```

AST from [pandoc](https://pandoc.org/try/?params=%7B%22text%22%3A%22%3E%2A%2A%232%2A%23%5Cn%22%2C%22to%22%3A%22json%22%2C%22from%22%3A%22commonmark%22%7D):

```text
Pandoc {
    meta: {},
    blocks: [
        BlockQuote(
            [
                Para(
                    [
                        Str(
                            "*",
                        ),
                        Emph(
                            [
                                Str(
                                    "#2",
                                ),
                            ],
                        ),
                        Str(
                            "#",
                        ),
                    ],
                ),
            ],
        ),
    ],
}
```


Pull request description:
Ignore block synstax when determining emphasis can_open/close
Fixes #755

Consider this markdown:

```markdown
>**#*

**#*
```

The block quote should not change the way emphasis is parsed. But in pulldown-cmark, it did:

```html
<blockquote>
<p>**#*</p>
</blockquote>
<p>*<em>#</em></p>
```

Both paragraphs now contain the same text.

Patch:
diff --git a/specs/regression.txt b/specs/regression.txt
index 566adbc..96142f6 100644
--- a/specs/regression.txt
+++ b/specs/regression.txt
@@ -1574,3 +1574,34 @@ ISSUE 753
 <p>[x\</p>
 <p>]: https://rust-lang.org</p>
 ````````````````````````````````
+
+ISSUE 755
+
+```````````````````````````````` example
+>>**#*
+>
+>**#*
+
+**#*
+
+|**#*|
+|----|
+|**#*|
+|**#*
+**#*|
+**#*
+.
+<blockquote>
+<blockquote>
+<p>*<em>#</em></p>
+</blockquote>
+<p>*<em>#</em></p>
+</blockquote>
+<p>*<em>#</em></p>
+<table><thead><tr><th>*<em>#</em></th></tr></thead><tbody>
+<tr><td>*<em>#</em></td></tr>
+<tr><td>*<em>#</em></td></tr>
+<tr><td>*<em>#</em></td></tr>
+<tr><td>*<em>#</em></td></tr>
+</tbody></table>
+````````````````````````````````
\ No newline at end of file
diff --git a/src/firstpass.rs b/src/firstpass.rs
index 82b36a4..ac397cf 100644
--- a/src/firstpass.rs
+++ b/src/firstpass.rs
@@ -628,8 +628,8 @@ impl<'a, 'b> FirstPass<'a, 'b> {
                 c @ b'*' | c @ b'_' | c @ b'~' => {
                     let string_suffix = &self.text[ix..];
                     let count = 1 + scan_ch_repeat(&string_suffix.as_bytes()[1..], c);
-                    let can_open = delim_run_can_open(self.text, string_suffix, count, ix);
-                    let can_close = delim_run_can_close(self.text, string_suffix, count, ix);
+                    let can_open = delim_run_can_open(&self.text[start..], string_suffix, count, ix - start, mode);
+                    let can_close = delim_run_can_close(&self.text[start..], string_suffix, count, ix - start, mode);
                     let is_valid_seq = c != b'~' || count <= 2;
 
                     if (can_open || can_close) && is_valid_seq {
@@ -779,8 +779,8 @@ impl<'a, 'b> FirstPass<'a, 'b> {
                 }
                 c @ b'\'' | c @ b'"' => {
                     let string_suffix = &self.text[ix..];
-                    let can_open = delim_run_can_open(self.text, string_suffix, 1, ix);
-                    let can_close = delim_run_can_close(self.text, string_suffix, 1, ix);
+                    let can_open = delim_run_can_open(&self.text[start..], string_suffix, 1, ix - start, mode);
+                    let can_close = delim_run_can_close(&self.text[start..], string_suffix, 1, ix - start, mode);
 
                     self.tree.append_text(begin_text, ix);
                     self.tree.append(Item {
@@ -1722,7 +1722,7 @@ fn surgerize_tight_list(tree: &mut Tree<Item>, list_ix: TreeIndex) {
 /// for _ delims).
 /// suffix is &s[ix..], which is passed in as an optimization, since taking
 /// a string subslice is O(n).
-fn delim_run_can_open(s: &str, suffix: &str, run_len: usize, ix: usize) -> bool {
+fn delim_run_can_open(s: &str, suffix: &str, run_len: usize, ix: usize, mode: TableParseMode) -> bool {
     let next_char = if let Some(c) = suffix.chars().nth(run_len) {
         c
     } else {
@@ -1734,6 +1734,14 @@ fn delim_run_can_open(s: &str, suffix: &str, run_len: usize, ix: usize) -> bool
     if ix == 0 {
         return true;
     }
+    if mode == TableParseMode::Active {
+        if s[..ix].ends_with("|") && !s[..ix].ends_with(r"\|") {
+            return true;
+        }
+        if next_char == '|' {
+            return false;
+        }
+    }
     let delim = suffix.chars().next().unwrap();
     // `*` and `~~` can be intraword, `_` cannot
     if (delim == '*' || delim == '~') && !is_punctuation(next_char) {
@@ -1749,7 +1757,7 @@ fn delim_run_can_open(s: &str, suffix: &str, run_len: usize, ix: usize) -> bool
 /// Determines whether the delimiter run starting at given index is
 /// right-flanking, as defined by the commonmark spec (and isn't intraword
 /// for _ delims)
-fn delim_run_can_close(s: &str, suffix: &str, run_len: usize, ix: usize) -> bool {
+fn delim_run_can_close(s: &str, suffix: &str, run_len: usize, ix: usize, mode: TableParseMode) -> bool {
     if ix == 0 {
         return false;
     }
@@ -1762,6 +1770,14 @@ fn delim_run_can_close(s: &str, suffix: &str, run_len: usize, ix: usize) -> bool
     } else {
         return true;
     };
+    if mode == TableParseMode::Active {
+        if s[..ix].ends_with("|") && !s[..ix].ends_with(r"\|") {
+            return false;
+        }
+        if next_char == '|' {
+            return true;
+        }
+    }
     let delim = suffix.chars().next().unwrap();
     // `*` and `~~` can be intraword, `_` cannot
     if (delim == '*' || delim == '~') && !is_punctuation(prev_char) {


Test patch:
diff --git a/tests/suite/regression.rs b/tests/suite/regression.rs
index 5ef5d42..c811251 100644
--- a/tests/suite/regression.rs
+++ b/tests/suite/regression.rs
@@ -1853,3 +1853,36 @@ fn regression_test_119() {
   
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_120() {
+    let original = r##">>**#*
+>
+>**#*
+
+**#*
+
+|**#*|
+|----|
+|**#*|
+|**#*
+**#*|
+**#*
+"##;
+    let expected = r##"<blockquote>
+<blockquote>
+<p>*<em>#</em></p>
+</blockquote>
+<p>*<em>#</em></p>
+</blockquote>
+<p>*<em>#</em></p>
+<table><thead><tr><th>*<em>#</em></th></tr></thead><tbody>
+<tr><td>*<em>#</em></td></tr>
+<tr><td>*<em>#</em></td></tr>
+<tr><td>*<em>#</em></td></tr>
+<tr><td>*<em>#</em></td></tr>
+</tbody></table>
+"##;
+  
+    test_markdown_html(original, expected, false, false, false);
+}
\ No newline at end of file