Repository: rust-lang/mdBook (language: rust)

Issue:
Line hiding in Rust code blocks is inconsistent with `rustdoc`
### Problem

Lines in Rust code blocks beginning with a `#` and followed immediately by a character other than `!`, `[`, `#`, or ` ` are hidden by `mdbook` but shown by `rustdoc`.

For example, the following code block is rendered as empty by `mdbook`:
````
```rust
#fn main() {}
```
````

Meanwhile, the following code block in a doc comment:
````rust
/// ```
/// #fn main() {}
/// ```
pub struct Foo;
````
is rendered as
<img width="193" alt="Image" src="https://github.com/user-attachments/assets/aad8d94f-6eb0-4810-a35f-b4202dcd98cd" />

### Steps

1. `mdbook init`
2. `echo '```rust\n#fn main() {}\n```' > src/chapter_1.md`
3. `mdbook serve`
4. Observe an empty code block. When the "Show hidden lines" button is clicked, the code block contains `fn main() {}`

### Possible Solution(s)

`mdbook` should implement `rustdoc`'s behavior since [the docs claim it does](https://rust-lang.github.io/mdBook/format/mdbook.html?highlight=hide#hiding-code-lines):
> For the Rust language, you can use the # character as a prefix which will hide lines [like you would with Rustdoc](https://doc.rust-lang.org/stable/rustdoc/write-documentation/documentation-tests.html#hiding-portions-of-the-example).

### Notes

[`rustdoc`'s rules](https://doc.rust-lang.org/stable/rustdoc/write-documentation/documentation-tests.html#hiding-portions-of-the-example) are:
- If a line begins with `# ` (note the space following the `#`), the line is hidden
- Two consecutive hashes `##` can be used to prevent the hiding of a line that would otherwise be hidden
  - To write a non-hidden line that starts with `# `, write `## ` which will render as `# `

Is `mdbook` willing to change this in a non-breaking release since it seems like a bug according to the docs, or would you rather wait until 0.5?

### Version

```text
mdbook v0.4.43
```


Discussion / hints:
Yea, I'd like to see a PR synchronizing with rustdoc's rules.


Fix patch:
diff --git a/guide/src/format/mdbook.md b/guide/src/format/mdbook.md
index 9bb94615ce..a354112920 100644
--- a/guide/src/format/mdbook.md
+++ b/guide/src/format/mdbook.md
@@ -4,7 +4,8 @@
 
 There is a feature in mdBook that lets you hide code lines by prepending them with a specific prefix.
 
-For the Rust language, you can use the `#` character as a prefix which will hide lines [like you would with Rustdoc][rustdoc-hide].
+For the Rust language, you can prefix lines with `# ` (`#` followed by a space) to hide them [like you would with Rustdoc][rustdoc-hide].
+This prefix can be escaped with `##` to prevent the hiding of a line that should begin with the literal string `# ` (see [Rustdoc's docs][rustdoc-hide] for more details)
 
 [rustdoc-hide]: https://doc.rust-lang.org/stable/rustdoc/write-documentation/documentation-tests.html#hiding-portions-of-the-example
 
diff --git a/src/renderer/html_handlebars/hbs_renderer.rs b/src/renderer/html_handlebars/hbs_renderer.rs
index d0149fb523..2150c37f63 100644
--- a/src/renderer/html_handlebars/hbs_renderer.rs
+++ b/src/renderer/html_handlebars/hbs_renderer.rs
@@ -931,7 +931,7 @@ fn add_playground_pre(
                             // we need to inject our own main
                             let (attrs, code) = partition_source(code);
 
-                            format!("# #![allow(unused)]\n{attrs}#fn main() {{\n{code}#}}").into()
+                            format!("# #![allow(unused)]\n{attrs}# fn main() {{\n{code}# }}").into()
                         };
                         content
                     }
@@ -1003,12 +1003,9 @@ fn hide_lines_rust(content: &str) -> String {
                 result += &caps[3];
                 result += newline;
                 continue;
-            } else if &caps[2] != "!" && &caps[2] != "[" {
+            } else if matches!(&caps[2], "" | " ") {
                 result += "<span class=\"boring\">";
                 result += &caps[1];
-                if &caps[2] != " " {
-                    result += &caps[2];
-                }
                 result += &caps[3];
                 result += newline;
                 result += "</span>";
@@ -1134,7 +1131,7 @@ mod tests {
     fn add_playground() {
         let inputs = [
           ("<code class=\"language-rust\">x()</code>",
-           "<pre class=\"playground\"><code class=\"language-rust\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
+           "<pre class=\"playground\"><code class=\"language-rust\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
           ("<code class=\"language-rust\">fn main() {}</code>",
            "<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>"),
           ("<code class=\"language-rust editable\">let s = \"foo\n # bar\n\";</code>",
@@ -1164,7 +1161,7 @@ mod tests {
     fn add_playground_edition2015() {
         let inputs = [
           ("<code class=\"language-rust\">x()</code>",
-           "<pre class=\"playground\"><code class=\"language-rust edition2015\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
+           "<pre class=\"playground\"><code class=\"language-rust edition2015\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
           ("<code class=\"language-rust\">fn main() {}</code>",
            "<pre class=\"playground\"><code class=\"language-rust edition2015\">fn main() {}</code></pre>"),
           ("<code class=\"language-rust edition2015\">fn main() {}</code>",
@@ -1188,7 +1185,7 @@ mod tests {
     fn add_playground_edition2018() {
         let inputs = [
           ("<code class=\"language-rust\">x()</code>",
-           "<pre class=\"playground\"><code class=\"language-rust edition2018\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
+           "<pre class=\"playground\"><code class=\"language-rust edition2018\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
           ("<code class=\"language-rust\">fn main() {}</code>",
            "<pre class=\"playground\"><code class=\"language-rust edition2018\">fn main() {}</code></pre>"),
           ("<code class=\"language-rust edition2015\">fn main() {}</code>",
@@ -1212,7 +1209,7 @@ mod tests {
     fn add_playground_edition2021() {
         let inputs = [
             ("<code class=\"language-rust\">x()</code>",
-             "<pre class=\"playground\"><code class=\"language-rust edition2021\"># #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>"),
+             "<pre class=\"playground\"><code class=\"language-rust edition2021\"># #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>"),
             ("<code class=\"language-rust\">fn main() {}</code>",
              "<pre class=\"playground\"><code class=\"language-rust edition2021\">fn main() {}</code></pre>"),
             ("<code class=\"language-rust edition2015\">fn main() {}</code>",
@@ -1237,8 +1234,12 @@ mod tests {
     fn hide_lines_language_rust() {
         let inputs = [
           (
-           "<pre class=\"playground\"><code class=\"language-rust\">\n# #![allow(unused)]\n#fn main() {\nx()\n#}</code></pre>",
+           "<pre class=\"playground\"><code class=\"language-rust\">\n# #![allow(unused)]\n# fn main() {\nx()\n# }</code></pre>",
            "<pre class=\"playground\"><code class=\"language-rust\">\n<span class=\"boring\">#![allow(unused)]\n</span><span class=\"boring\">fn main() {\n</span>x()\n<span class=\"boring\">}</span></code></pre>",),
+          // # must be followed by a space for a line to be hidden
+          (
+           "<pre class=\"playground\"><code class=\"language-rust\">\n#fn main() {\nx()\n#}</code></pre>",
+           "<pre class=\"playground\"><code class=\"language-rust\">\n#fn main() {\nx()\n#}</code></pre>",),
           (
            "<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>",
            "<pre class=\"playground\"><code class=\"language-rust\">fn main() {}</code></pre>",),


Test patch: