Skip to content
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

Improve Rustdoc UI for scraped examples with multiline arguments, fix overflow in line numbers #93217

Merged
merged 10 commits into from
Apr 13, 2022
Merged
1 change: 1 addition & 0 deletions src/doc/rustdoc/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Linking to items by name](write-documentation/linking-to-items-by-name.md)
- [Documentation tests](write-documentation/documentation-tests.md)
- [Rustdoc-specific lints](lints.md)
- [Scraped examples](scraped-examples.md)
- [Advanced features](advanced-features.md)
- [Unstable features](unstable-features.md)
- [Deprecated features](deprecated-features.md)
Expand Down
55 changes: 55 additions & 0 deletions src/doc/rustdoc/src/scraped-examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Scraped examples
willcrichton marked this conversation as resolved.
Show resolved Hide resolved

Rustdoc has an unstable feature where it can automatically scrape examples of items being documented from the `examples/` directory of a Cargo workspace. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:

```rust,ignore (needs-other-file)
// a_crate/src/lib.rs
pub fn a_func() {}
```

And you have an example calling this function:

```rust,ignore (needs-other-file)
// a_crate/examples/ex.rs
fn main() {
a_crate::a_func();
}
```

Then this code snippet will be included in the documentation for `a_func`. This documentation is inserted by Rustdoc and cannot be manually edited by the crate author.


## How to use this feature

This feature is unstable, so you can enable it by calling Rustdoc with the unstable `rustdoc-scrape-examples` flag:

```bash
cargo doc -Zunstable-options -Zrustdoc-scrape-examples=examples
```

To enable this feature on [docs.rs](https://docs.rs), add this to your Cargo.toml:

```toml
[package.metadata.docs.rs]
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples=examples"]
```


## How it works

When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items being documented. Then Rustdoc will include the source code of these instances in the generated documentation.

Rustdoc has a few techniques to ensure these examples don't overwhelm documentation readers, and that it doesn't blow up the page size:

1. For a given item, a maximum of 5 examples are included in the page. The remaining examples are just links to source code.
2. Only one example is shown by default, and the remaining examples are hidden behind a toggle.
3. For a given file that contains examples, only the item containing the examples will be included in the generated documentation.

For a given item, Rustdoc sorts its examples based on the size of the example — smaller ones are shown first.


## FAQ

### My example is not showing up in the documentation

This feature uses Cargo's convention for finding examples. You should ensure that `cargo check --examples` includes your example file.
19 changes: 17 additions & 2 deletions src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
collect_spans_and_sources, print_sidebar, settings, AllTypes, LinkFromSrc, NameDoc, StylePath,
BASIC_KEYWORDS,
collect_spans_and_sources, print_sidebar, scrape_examples_help, settings, AllTypes,
LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS,
};

use crate::clean::{self, types::ExternalLocation, ExternalCrate};
Expand Down Expand Up @@ -551,6 +551,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
let crate_name = self.tcx().crate_name(LOCAL_CRATE);
let final_file = self.dst.join(crate_name.as_str()).join("all.html");
let settings_file = self.dst.join("settings.html");
let scrape_examples_help_file = self.dst.join("scrape-examples-help.html");

let mut root_path = self.dst.to_str().expect("invalid path").to_owned();
if !root_path.ends_with('/') {
Expand Down Expand Up @@ -606,6 +607,20 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
&self.shared.style_files,
);
self.shared.fs.write(settings_file, v)?;

if self.shared.layout.scrape_examples_extension {
page.title = "About scraped examples";
page.description = "How the scraped examples feature works in Rustdoc";
let v = layout::render(
&self.shared.layout,
&page,
"",
scrape_examples_help(&*self.shared),
&self.shared.style_files,
);
self.shared.fs.write(scrape_examples_help_file, v)?;
}

if let Some(ref redirections) = self.shared.redirections {
if !redirections.borrow().is_empty() {
let redirect_map_path =
Expand Down
36 changes: 35 additions & 1 deletion src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,10 @@ use crate::html::format::{
use crate::html::highlight;
use crate::html::markdown::{HeadingOffset, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine};
use crate::html::sources;
use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
use crate::scrape_examples::{CallData, CallLocation};
use crate::try_none;
use crate::DOC_RUST_LANG_ORG_CHANNEL;

/// A pair of name and its optional document.
crate type NameDoc = (String, Option<String>);
Expand Down Expand Up @@ -461,6 +463,34 @@ fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<S
))
}

fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
content.push_str(&format!(
"## More information\n\n\
If you want more information about this feature, please read the [corresponding chapter in the Rustdoc book]({}/rustdoc/scraped-examples.html).",
DOC_RUST_LANG_ORG_CHANNEL));

let mut ids = IdMap::default();
format!(
"<div class=\"main-heading\">\
Copy link
Member

Choose a reason for hiding this comment

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

Nice!

<h1 class=\"fqn\">\
<span class=\"in-band\">About scraped examples</span>\
</h1>\
</div>\
<div>{}</div>",
Markdown {
content: &content,
links: &[],
ids: &mut ids,
error_codes: shared.codes,
edition: shared.edition(),
playground: &shared.playground,
heading_offset: HeadingOffset::H1
}
.into_string()
)
}

fn document(
w: &mut Buffer,
cx: &Context<'_>,
Expand Down Expand Up @@ -2671,7 +2701,9 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) {
<span></span>\
<h5 id=\"{id}\">\
<a href=\"#{id}\">Examples found in repository</a>\
<a class=\"scrape-help\" href=\"{root_path}scrape-examples-help.html\">?</a>\
</h5>",
root_path = cx.root_path(),
id = id
);

Expand Down Expand Up @@ -2723,9 +2755,10 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) {
.locations
.iter()
.map(|loc| {
let (byte_lo, byte_hi) = loc.call_expr.byte_span;
let (byte_lo, byte_hi) = loc.call_ident.byte_span;
let (line_lo, line_hi) = loc.call_expr.line_span;
let byte_range = (byte_lo - byte_min, byte_hi - byte_min);

let line_range = (line_lo - line_min, line_hi - line_min);
let (line_url, line_title) = link_to_loc(call_data, loc);

Expand Down Expand Up @@ -2841,6 +2874,7 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) {
<summary class=\"hideme\">\
<span>More examples</span>\
</summary>\
<div class=\"hide-more\">Hide additional examples</div>\
GuillaumeGomez marked this conversation as resolved.
Show resolved Hide resolved
<div class=\"more-scraped-examples\">\
<div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\
<div class=\"more-scraped-examples-inner\">"
Expand Down
81 changes: 44 additions & 37 deletions src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ h2.location a {
position: relative;
}

.docblock > :not(.information) {
.docblock > :not(.information):not(.more-examples-toggle) {
max-width: 100%;
overflow-x: auto;
}
Expand Down Expand Up @@ -836,8 +836,8 @@ h2.small-section-header > .anchor {
content: '§';
}

.docblock a:not(.srclink):not(.test-arrow):hover,
.docblock-short a:not(.srclink):not(.test-arrow):hover, .item-info a {
.docblock a:not(.srclink):not(.test-arrow):not(.scrape-help):hover,
.docblock-short a:not(.srclink):not(.test-arrow):not(.scrape-help):hover, .item-info a {
text-decoration: underline;
}

Expand Down Expand Up @@ -2034,21 +2034,45 @@ details.rustdoc-toggle[open] > summary.hideme::after {

/* Begin: styles for --scrape-examples feature */

.scraped-example-list .scrape-help {
margin-left: 10px;
padding: 0 4px;
font-weight: normal;
font-size: 12px;
position: relative;
bottom: 1px;
background: transparent;
border-width: 1px;
border-style: solid;
border-radius: 50px;
}

.scraped-example-title {
font-family: 'Fira Sans';
}

.scraped-example:not(.expanded) .code-wrapper pre.line-numbers {
overflow: hidden;
.scraped-example .code-wrapper {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}

.scraped-example:not(.expanded) .code-wrapper {
max-height: 240px;
}

.scraped-example:not(.expanded) .code-wrapper .example-wrap pre.rust {
.scraped-example:not(.expanded) .code-wrapper pre {
overflow-y: hidden;
max-height: 240px;
padding-bottom: 0;
}

.scraped-example:not(.expanded) .code-wrapper pre.line-numbers {
overflow-x: hidden;
}

.scraped-example .code-wrapper .prev {
position: absolute;
top: 0.25em;
Expand All @@ -2073,22 +2097,13 @@ details.rustdoc-toggle[open] > summary.hideme::after {
cursor: pointer;
}

.scraped-example .code-wrapper {
position: relative;
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
}

.scraped-example:not(.expanded) .code-wrapper:before {
content: " ";
width: 100%;
height: 5px;
position: absolute;
z-index: 100;
top: 0;
background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}

.scraped-example:not(.expanded) .code-wrapper:after {
Expand All @@ -2098,12 +2113,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
position: absolute;
z-index: 100;
bottom: 0;
background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}

.scraped-example:not(.expanded) .code-wrapper {
overflow: hidden;
max-height: 240px;
}

.scraped-example .code-wrapper .line-numbers {
Expand All @@ -2122,34 +2131,37 @@ details.rustdoc-toggle[open] > summary.hideme::after {
margin-bottom: 0;
}

.scraped-example:not(.expanded) .code-wrapper .example-wrap {
overflow-x: hidden;
}

.scraped-example .code-wrapper .example-wrap pre.rust {
overflow-x: inherit;
width: inherit;
overflow-y: hidden;
}

.scraped-example .example-wrap .rust span.highlight {
background: #fcffd6;
}

.scraped-example .example-wrap .rust span.highlight.focus {
background: #f6fdb0;
}

.more-examples-toggle {
max-width: calc(100% + 25px);
margin-top: 10px;
margin-left: -25px;
}

.more-examples-toggle .hide-more {
margin-left: 25px;
margin-bottom: 5px;
cursor: pointer;
}

.more-examples-toggle summary {
color: #999;
.more-examples-toggle summary, .more-examples-toggle .hide-more {
font-family: 'Fira Sans';
}

.more-scraped-examples {
margin-left: 25px;
margin-left: 5px;
display: flex;
flex-direction: row;
width: calc(100% - 25px);
}

.more-scraped-examples-inner {
Expand All @@ -2165,13 +2177,8 @@ details.rustdoc-toggle[open] > summary.hideme::after {
cursor: pointer;
}

.toggle-line:hover .toggle-line-inner {
background: #aaa;
}

.toggle-line-inner {
min-width: 2px;
background: #ddd;
height: 100%;
}

Expand Down
16 changes: 14 additions & 2 deletions src/librustdoc/html/static/css/themes/ayu.css
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,18 @@ input:checked + .slider {
background-color: #ffb454 !important;
}


.scraped-example-list .scrape-help {
border-color: #aaa;
color: #eee;
}
.scraped-example-list .scrape-help:hover {
border-color: white;
color: white;
}
.more-examples-toggle summary, .more-examples-toggle .hide-more {
color: #999;
}
.scraped-example .example-wrap .rust span.highlight {
background: rgb(91, 59, 1);
}
Expand All @@ -624,8 +636,8 @@ input:checked + .slider {
background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
}
.toggle-line-inner {
background: #616161;
background: #999;
}
.toggle-line:hover .toggle-line-inner {
background: #898989;
background: #c5c5c5;
}
Loading