Skip to content

rustdoc codeblock hash escape #51803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/doc/rustdoc/src/documentation-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 10 additions & 10 deletions src/librustdoc/html/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
}
Expand All @@ -91,15 +91,15 @@ 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)))
Copy link
Member

@killercup killercup Jun 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things:

  1. Wait, there is no Cow-returning replacen yet? Might be a nice thing to have, since it's pretty concise. Not relevant here, though.
  2. Given that we are in the branch where the string starts with ##, isn't replacing the two hash signs with one the same as &s[1..]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Agreed.
  2. It is not the same, because of leading whitespace. We check if trimmed starts with ##, not s.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, my bad

} else if trimmed.starts_with("# ") {
// # text
Line::Hidden(&trimmed[2..])
} else if trimmed == "#" {
// We cannot handle '#text' because it could be #[attr].
Line::Hidden("")
} else {
Line::Shown(s)
Line::Shown(Cow::Borrowed(s))
}
}

Expand Down Expand Up @@ -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
Expand All @@ -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());
Expand Down Expand Up @@ -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();
Expand Down