Repository: pulldown-cmark/pulldown-cmark (rust)

Issue:
[fuzz result] code block terminators can't be indented four spaces
Try these two things out in [the dingus](https://spec.commonmark.org/dingus/?text=%20~~~%0A%20%20%20%20~~~%0A). The first one has a string, that looks like a closing fence, indented four spaces. The second one is only indented three spaces.

```markdown
 ~~~
    ~~~
```

```markdown
 ~~~
   ~~~
```

[The spec](https://spec.commonmark.org/0.31.2/#fenced-code-blocks) definitely wants the second one to be an empty code block, but it's a bit ambiguous about the first:

> The closing code fence may be preceded by up to three spaces of indentation, and may be followed only by spaces or tabs, which are ignored.

Pulldown-cmark checks "spaces of indentation" relative to the indentation level of the *opening fence*, but the reference implementation, [commonmark-hs](https://pandoc.org/try/?params=%7B%22text%22%3A%22+%7E%7E%7E%5Cn++++%7E%7E%7E%5Cn%22%2C%22to%22%3A%22html5%22%2C%22from%22%3A%22commonmark%22%2C%22standalone%22%3Afalse%2C%22embed-resources%22%3Afalse%2C%22table-of-contents%22%3Afalse%2C%22number-sections%22%3Afalse%2C%22citeproc%22%3Afalse%2C%22html-math-method%22%3A%22plain%22%2C%22wrap%22%3A%22auto%22%2C%22highlight-style%22%3Anull%2C%22files%22%3A%7B%7D%2C%22template%22%3Anull%7D) [and markdown-it](https://markdown-it.github.io/#md3=%7B%22source%22%3A%22%20~~~%5Cn%20%20%20%20~~~%22%2C%22defaults%22%3A%7B%22html%22%3Afalse%2C%22xhtmlOut%22%3Afalse%2C%22breaks%22%3Afalse%2C%22langPrefix%22%3A%22language-%22%2C%22linkify%22%3Atrue%2C%22typographer%22%3Atrue%2C%22_highlight%22%3Atrue%2C%22_strict%22%3Afalse%2C%22_view%22%3A%22html%22%7D%7D) all seem to check relative to the *start of the line*.

<details>

Events from pulldown-cmark:

```rust
" ~~~#4\n    ~~~\n" -> [
  Start(CodeBlock(Fenced(Borrowed(""))))
  End(CodeBlock)
]
```

Events from pandoc:

```rust
" ~~~#4\n    ~~~\n" -> [
  Start(CodeBlock(Fenced(Borrowed(""))))
    Text(Boxed(" ~~~"))
  End(CodeBlock)
]
```

Events from commonmark.js:

```rust
" ~~~#4\n    ~~~\n" -> [
  Start(CodeBlock(Fenced(Borrowed(""))))
    Text(Boxed(" ~~~"))
  End(CodeBlock)
]
```

AST from [pandoc](https://pandoc.org/try/?params=%7B%22text%22%3A%22%20~~~%234%5Cn%20%20%20%20~~~%5Cn%22%2C%22to%22%3A%22json%22%2C%22from%22%3A%22commonmark%22%7D):

```text
Pandoc {
    meta: {},
    blocks: [
        CodeBlock(
            Attr(
                "",
                [
                    "#4",
                ],
                [],
            ),
            "   ~~~",
        ),
    ],
}
```


Pull request description:
Check indentation on the closing fence relative to the line
Fixes #861

This changes the interpretation of this document:

````markdown
 ```
    ```
````

Without this fix, pulldown-cmark thinks the second line has three spaces of indentation, and is a valid closing code fence. Most other commonmark implementations interpret it as having four, because they start counting at the beginning of the line instead of the opening fence's indentation level.

Patch:
diff --git a/pulldown-cmark/specs/regression.txt b/pulldown-cmark/specs/regression.txt
index 92547b0..daf90c8 100644
--- a/pulldown-cmark/specs/regression.txt
+++ b/pulldown-cmark/specs/regression.txt
@@ -2525,3 +2525,20 @@ title
 junk</p>
 </blockquote>
 ````````````````````````````````
+
+ISSUE 861
+
+```````````````````````````````` example
+ ~~~
+    ~~~
+.
+<pre><code>   ~~~
+</code></pre>
+````````````````````````````````
+
+```````````````````````````````` example
+ ~~~
+   ~~~
+.
+<pre><code></code></pre>
+````````````````````````````````
diff --git a/pulldown-cmark/src/firstpass.rs b/pulldown-cmark/src/firstpass.rs
index e2eb6c3..e15cdd8 100644
--- a/pulldown-cmark/src/firstpass.rs
+++ b/pulldown-cmark/src/firstpass.rs
@@ -1148,7 +1148,7 @@ impl<'a, 'b> FirstPass<'a, 'b> {
             }
             line_start.scan_space(indent);
             let mut close_line_start = line_start.clone();
-            if !close_line_start.scan_space(4) {
+            if !close_line_start.scan_space(4 - indent) {
                 let close_ix = ix + close_line_start.bytes_scanned();
                 if let Some(n) = scan_closing_code_fence(&bytes[close_ix..], fence_ch, n_fence_char)
                 {


Test patch:
diff --git a/pulldown-cmark/tests/suite/regression.rs b/pulldown-cmark/tests/suite/regression.rs
index 75660e8..41f2568 100644
--- a/pulldown-cmark/tests/suite/regression.rs
+++ b/pulldown-cmark/tests/suite/regression.rs
@@ -3016,3 +3016,26 @@ junk</p>
 
     test_markdown_html(original, expected, false, false, false);
 }
+
+#[test]
+fn regression_test_191() {
+    let original = r##" ~~~
+    ~~~
+"##;
+    let expected = r##"<pre><code>   ~~~
+</code></pre>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}
+
+#[test]
+fn regression_test_192() {
+    let original = r##" ~~~
+   ~~~
+"##;
+    let expected = r##"<pre><code></code></pre>
+"##;
+
+    test_markdown_html(original, expected, false, false, false);
+}