Repository: raphlinus/pulldown-cmark (rust)

Issue:
Indented table rendering as text
I'm not sure whether to call this a bug or not, as from my quick scan of the spec, I didn't find anywhere where it allows or disallows indenting a table.

Considering that pulldown-cmark allows for indented code blocks, then I assume intended tables should also be valid.

```rust
use pulldown_cmark::{html, Options, Parser};

// Notice the 2 spaces before the pipes
let text = "
  | A   | B   |
  | --- | --- |
  | 1   | 2   |

  ```rust
  println!("Foo Bar");
  ```
";

let mut options = Options::empty();
options.insert(Options::ENABLE_TABLES);
let parser = Parser::new_ext(text, options);

let mut out = String::new();
html::push_html(&mut out, parser);
println!("{}", out);
```

Which outputs the following:

```html
<p>| A   | B   |
| --- | --- |
| 1   | 2   |</p>
<pre><code class="language-rust">println!(&quot;Foo Bar&quot;);
</code></pre>
```

While I expected the following:

```html
<table><thead><tr><th>A</th><th>B</th></tr></thead><tbody>     
<tr><td>1</td><td>2</td></tr>
</tbody></table>
<pre><code class="language-rust">println!(&quot;Foo Bar&quot;);
</code></pre>
```

Here's the same table indented with 2 whitespace rendered here on GitHub:

  | A   | B   |
  | --- | --- |
  | 1   | 2   |

It also renders how I'd expect in VSCode's Markdown preview:

![image](https://github.com/raphlinus/pulldown-cmark/assets/17464404/964a1155-b7fb-4881-8285-0beb1317df69)


Pull request description:
Skip leading space on leading pipe in `scan_table_head`
Fixes #656

Patch:
diff --git a/examples/events.rs b/examples/events.rs
new file mode 100644
index 0000000..45159d3
--- /dev/null
+++ b/examples/events.rs
@@ -0,0 +1,22 @@
+use std::io::Read;
+
+use pulldown_cmark::{Event, Parser};
+
+/// Show all events from the text on stdin.
+fn main() {
+    let mut text = String::new();
+    std::io::stdin().read_to_string(&mut text).unwrap();
+
+    eprintln!("{text:?} -> [");
+    let mut width = 0;
+    for event in Parser::new(&text) {
+        if let Event::End(_) = event {
+            width -= 2;
+        }
+        eprintln!("  {:width$}{event:?}", "");
+        if let Event::Start(_) = event {
+            width += 2;
+        }
+    }
+    eprintln!("]");
+}
diff --git a/specs/regression.txt b/specs/regression.txt
index ee8ebe8..7a3ce74 100644
--- a/specs/regression.txt
+++ b/specs/regression.txt
@@ -914,3 +914,143 @@ foo~~bar~~
 <p>foo<del>bar</del></p>
 ````````````````````````````````
 
+ISSUE 551
+
+```````````````````````````````` example
+*~~__emphasis strike strong__~~* ~~*__strike emphasis strong__*~~
+.
+<p><em><del><strong>emphasis strike strong</strong></del></em> <del><em><strong>strike emphasis strong</strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+*~~__emphasis strike strong__~~* ~~*__`strike emphasis strong code`__*~~
+.
+<p><em><del><strong>emphasis strike strong</strong></del></em> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+*~~`emphasis strike code`~~* ~~*__strike emphasis strong__*~~
+.
+<p><em><del><code>emphasis strike code</code></del></em> <del><em><strong>strike emphasis strong</strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+*~~`emphasis strike code`~~* ~~*__`strike emphasis strong code`__*~~
+.
+<p><em><del><code>emphasis strike code</code></del></em> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+**~~_strong strike emphasis_~~** ~~*__strike emphasis strong__*~~
+.
+<p><strong><del><em>strong strike emphasis</em></del></strong> <del><em><strong>strike emphasis strong</strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+**~~_strong strike emphasis_~~** ~~*__`strike emphasis strong code`__*~~
+.
+<p><strong><del><em>strong strike emphasis</em></del></strong> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+**~~`strong strike code`~~** ~~*__strike emphasis strong__*~~
+.
+<p><strong><del><code>strong strike code</code></del></strong> <del><em><strong>strike emphasis strong</strong></em></del></p>
+````````````````````````````````
+
+```````````````````````````````` example
+**~~`strong strike code`~~** ~~*__`strike emphasis strong code`__*~~
+.
+<p><strong><del><code>strong strike code</code></del></strong> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+````````````````````````````````
+
+ISSUE 656
+
+```````````````````````````````` example
+  | foo | bar  |
+  |-----|------|
+  | baz | alef |
+
+  | foo | bar  |
+  |-----|------|
+| baz | alef |
+
+  | foo | bar  |
+|-----|------|
+  | baz | alef |
+
+| foo | bar  |
+  |-----|------|
+  | baz | alef |
+
+  | foo | bar  |
+|-----|------|
+| baz | alef |
+
+| foo | bar  |
+|-----|------|
+  | baz | alef |
+
+| foo | bar  |
+|-----|------|
+| baz | alef |
+
+| foo | bar  |
+  |-----|------|
+| baz | alef |
+
+    | foo | bar  |
+    |-----|------|
+    | baz | alef |
+
+| foo | bar  |
+    |-----|------|
+| baz | alef |
+
+→| foo | bar  |
+→|-----|------|
+→| baz | alef |
+
+| foo | bar  |
+→|-----|------|
+| baz | alef |
+.
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<pre><code>| foo | bar  |
+|-----|------|
+| baz | alef |
+</code></pre>
+<p>| foo | bar  |
+    |-----|------|
+| baz | alef |</p>
+<pre><code>| foo | bar  |
+|-----|------|
+| baz | alef |
+</code></pre>
+<p>| foo | bar  |
+→|-----|------|
+| baz | alef |</p>
+````````````````````````````````
diff --git a/src/parse.rs b/src/parse.rs
index 1573d31..4d4c624 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -1062,7 +1062,14 @@ impl InlineStack {
         }
     }
 
+    fn trim_lower_bound(&mut self, ix: usize) {
+        self.lower_bounds[ix] = self.lower_bounds[ix].min(self.stack.len());
+    }
+
     fn push(&mut self, el: InlineEl) {
+        if el.c == b'~' {
+            self.trim_lower_bound(InlineStack::TILDES);
+        }
         self.stack.push(el)
     }
 }
diff --git a/src/scanners.rs b/src/scanners.rs
index 0adfbe8..6141f32 100644
--- a/src/scanners.rs
+++ b/src/scanners.rs
@@ -458,6 +458,7 @@ fn calc_indent(text: &[u8], max: usize) -> (usize, usize) {
     let mut offset = 0;
 
     for (i, &b) in text.iter().enumerate() {
+        offset = i;
         match b {
             b' ' => {
                 spaces += 1;
@@ -474,7 +475,6 @@ fn calc_indent(text: &[u8], max: usize) -> (usize, usize) {
             }
             _ => break,
         }
-        offset = i;
     }
 
     (offset, spaces)


Test patch:
diff --git a/tests/suite/regression.rs b/tests/suite/regression.rs
index e927e46..950de79 100644
--- a/tests/suite/regression.rs
+++ b/tests/suite/regression.rs
@@ -1068,3 +1068,176 @@ fn regression_test_74() {
 
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_75() {
+    let original = r##"*~~__emphasis strike strong__~~* ~~*__strike emphasis strong__*~~
+"##;
+    let expected = r##"<p><em><del><strong>emphasis strike strong</strong></del></em> <del><em><strong>strike emphasis strong</strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_76() {
+    let original = r##"*~~__emphasis strike strong__~~* ~~*__`strike emphasis strong code`__*~~
+"##;
+    let expected = r##"<p><em><del><strong>emphasis strike strong</strong></del></em> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_77() {
+    let original = r##"*~~`emphasis strike code`~~* ~~*__strike emphasis strong__*~~
+"##;
+    let expected = r##"<p><em><del><code>emphasis strike code</code></del></em> <del><em><strong>strike emphasis strong</strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_78() {
+    let original = r##"*~~`emphasis strike code`~~* ~~*__`strike emphasis strong code`__*~~
+"##;
+    let expected = r##"<p><em><del><code>emphasis strike code</code></del></em> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_79() {
+    let original = r##"**~~_strong strike emphasis_~~** ~~*__strike emphasis strong__*~~
+"##;
+    let expected = r##"<p><strong><del><em>strong strike emphasis</em></del></strong> <del><em><strong>strike emphasis strong</strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_80() {
+    let original = r##"**~~_strong strike emphasis_~~** ~~*__`strike emphasis strong code`__*~~
+"##;
+    let expected = r##"<p><strong><del><em>strong strike emphasis</em></del></strong> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_81() {
+    let original = r##"**~~`strong strike code`~~** ~~*__strike emphasis strong__*~~
+"##;
+    let expected = r##"<p><strong><del><code>strong strike code</code></del></strong> <del><em><strong>strike emphasis strong</strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_82() {
+    let original = r##"**~~`strong strike code`~~** ~~*__`strike emphasis strong code`__*~~
+"##;
+    let expected = r##"<p><strong><del><code>strong strike code</code></del></strong> <del><em><strong><code>strike emphasis strong code</code></strong></em></del></p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_83() {
+    let original = r##"  | foo | bar  |
+  |-----|------|
+  | baz | alef |
+
+  | foo | bar  |
+  |-----|------|
+| baz | alef |
+
+  | foo | bar  |
+|-----|------|
+  | baz | alef |
+
+| foo | bar  |
+  |-----|------|
+  | baz | alef |
+
+  | foo | bar  |
+|-----|------|
+| baz | alef |
+
+| foo | bar  |
+|-----|------|
+  | baz | alef |
+
+| foo | bar  |
+|-----|------|
+| baz | alef |
+
+| foo | bar  |
+  |-----|------|
+| baz | alef |
+
+    | foo | bar  |
+    |-----|------|
+    | baz | alef |
+
+| foo | bar  |
+    |-----|------|
+| baz | alef |
+
+	| foo | bar  |
+	|-----|------|
+	| baz | alef |
+
+| foo | bar  |
+	|-----|------|
+| baz | alef |
+"##;
+    let expected = r##"<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<table><thead><tr><th> foo </th><th> bar  </th></tr></thead>
+<tr><td> baz </td><td> alef </td></tr>
+</table>
+<pre><code>| foo | bar  |
+|-----|------|
+| baz | alef |
+</code></pre>
+<p>| foo | bar  |
+    |-----|------|
+| baz | alef |</p>
+<pre><code>| foo | bar  |
+|-----|------|
+| baz | alef |
+</code></pre>
+<p>| foo | bar  |
+	|-----|------|
+| baz | alef |</p>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}