about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-07-04 20:21:01 +0000
committerbors <bors@rust-lang.org>2018-07-04 20:21:01 +0000
commitafaa40646542ca1e8fadb7e1d34197237e0fcd19 (patch)
tree54ba3fcffed75bbca891eb65a004d1d843208568
parent4af9132a022ebe4111395139d1a4e45e285d299a (diff)
parentff2ff2b2b88f5948e1326809e70fbdf4db167097 (diff)
downloadrust-afaa40646542ca1e8fadb7e1d34197237e0fcd19.tar.gz
rust-afaa40646542ca1e8fadb7e1d34197237e0fcd19.zip
Auto merge of #51803 - lucasem:rustdoc-code-hash-escape, r=GuillaumeGomez
rustdoc codeblock hash escape

So that docstring text such as the following (in a code block) can be created ergonomically:

```rust
let s = "
    foo
    # bar
    baz
";
```

Such code in a docstring hide the <code>&nbsp;&nbsp;&nbsp;&nbsp;# bar</code> line.

Previously, using two consecutive hashes <code>&nbsp;&nbsp;&nbsp;&nbsp;## bar</code> would turn the line into _shown_ `# bar`, losing the leading whitespace. A line of code like <code>&nbsp;&nbsp;&nbsp;&nbsp;# bar</code> (such as in the example above) **could not be represented** in the docstring text.

This commit makes the two consecutive hashes not also trim the leading whitespace — the two hashes simply **escape** into a single hash and do not hide the line, leaving the rest of that line unaffected. The new docstring text to achieve the above code block is:

```rust
/// ```
/// let s = "
///     foo
///     ## bar
///     baz
/// ";
/// ```
```
-rw-r--r--src/doc/rustdoc/src/documentation-tests.md17
-rw-r--r--src/librustdoc/html/markdown.rs20
2 files changed, 27 insertions, 10 deletions
diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md
index 7639537fc55..e4af122d0cb 100644
--- a/src/doc/rustdoc/src/documentation-tests.md
+++ b/src/doc/rustdoc/src/documentation-tests.md
@@ -170,6 +170,23 @@ By repeating all parts of the example, you can ensure that your example still
 compiles, while only showing the parts that are relevant to that part of your
 explanation.
 
+The `#`-hiding of lines can be prevented by using two consecutive hashes
+`##`. This only needs to be done with with the first `#` which would've
+otherwise caused hiding. If we have a string literal like the following,
+which has a line that starts with a `#`:
+
+```rust
+let s = "foo
+## bar # baz";
+```
+
+We can document it by escaping the initial `#`:
+
+```text
+/// let s = "foo
+/// ## bar # baz";
+```
+
 
 ## Using `?` in doc tests
 
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 7088104cd7a..c77a720b16b 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -64,21 +64,21 @@ pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]);
 /// All lines are used in documentation tests.
 enum Line<'a> {
     Hidden(&'a str),
-    Shown(&'a str),
+    Shown(Cow<'a, str>),
 }
 
 impl<'a> Line<'a> {
-    fn for_html(self) -> Option<&'a str> {
+    fn for_html(self) -> Option<Cow<'a, str>> {
         match self {
             Line::Shown(l) => Some(l),
             Line::Hidden(_) => None,
         }
     }
 
-    fn for_code(self) -> &'a str {
+    fn for_code(self) -> Cow<'a, str> {
         match self {
-            Line::Shown(l) |
-            Line::Hidden(l) => l,
+            Line::Shown(l) => l,
+            Line::Hidden(l) => Cow::Borrowed(l),
         }
     }
 }
@@ -91,7 +91,7 @@ impl<'a> Line<'a> {
 fn map_line(s: &str) -> Line {
     let trimmed = s.trim();
     if trimmed.starts_with("##") {
-        Line::Shown(&trimmed[1..])
+        Line::Shown(Cow::Owned(s.replacen("##", "#", 1)))
     } else if trimmed.starts_with("# ") {
         // # text
         Line::Hidden(&trimmed[2..])
@@ -99,7 +99,7 @@ fn map_line(s: &str) -> Line {
         // We cannot handle '#text' because it could be #[attr].
         Line::Hidden("")
     } else {
-        Line::Shown(s)
+        Line::Shown(Cow::Borrowed(s))
     }
 }
 
@@ -168,7 +168,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
             }
         }
         let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
-        let text = lines.collect::<Vec<&str>>().join("\n");
+        let text = lines.collect::<Vec<Cow<str>>>().join("\n");
         PLAYGROUND.with(|play| {
             // insert newline to clearly separate it from the
             // previous block so we can shorten the html output
@@ -179,7 +179,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
                 }
                 let test = origtext.lines()
                     .map(|l| map_line(l).for_code())
-                    .collect::<Vec<&str>>().join("\n");
+                    .collect::<Vec<Cow<str>>>().join("\n");
                 let krate = krate.as_ref().map(|s| &**s);
                 let (test, _) = test::make_test(&test, krate, false,
                                            &Default::default());
@@ -477,7 +477,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector, position: Sp
                 }
                 if let Some(offset) = offset {
                     let lines = test_s.lines().map(|l| map_line(l).for_code());
-                    let text = lines.collect::<Vec<&str>>().join("\n");
+                    let text = lines.collect::<Vec<Cow<str>>>().join("\n");
                     nb_lines += doc[prev_offset..offset].lines().count();
                     let line = tests.get_line() + (nb_lines - 1);
                     let filename = tests.get_filename();