Rollup merge of #93217 - willcrichton:example-analyzer, r=GuillaumeGomez

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

This PR improves a few aspects of the scrape examples feature in Rustdoc.
* Only function names and not the full call expression are highlighted.
* For call-sites with multiline arguments, the minimized code viewer will scroll to the top of the call-site rather than the middle if the argument is larger than the viewer size, ensuring that the function name is visible.
* This fixes an issue where the line numbers column had a visible x-scroll bar.

r? `@GuillaumeGomez`
This commit is contained in:
Dylan DPC 2022-04-13 17:35:32 +02:00 committed by GitHub
commit db61452b7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 342 additions and 88 deletions

View file

@ -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;
}
@ -840,8 +840,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;
}
@ -2038,21 +2038,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;
@ -2077,14 +2101,6 @@ 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%;
@ -2092,7 +2108,6 @@ details.rustdoc-toggle[open] > summary.hideme::after {
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 {
@ -2102,12 +2117,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 {
@ -2126,34 +2135,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 summary {
color: #999;
.more-examples-toggle .hide-more {
margin-left: 25px;
margin-bottom: 5px;
cursor: pointer;
}
.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 {
@ -2169,13 +2181,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%;
}

View file

@ -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);
}
@ -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;
}

View file

@ -478,6 +478,17 @@ div.files > .selected {
border-bottom-color: #ddd;
}
.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);
}
@ -491,8 +502,8 @@ div.files > .selected {
background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
}
.toggle-line-inner {
background: #616161;
background: #999;
}
.toggle-line:hover .toggle-line-inner {
background: #898989;
background: #c5c5c5;
}

View file

@ -462,3 +462,33 @@ div.files > .selected {
.setting-line > .title {
border-bottom-color: #D5D5D5;
}
.scraped-example-list .scrape-help {
border-color: #555;
color: #333;
}
.scraped-example-list .scrape-help:hover {
border-color: black;
color: black;
}
.more-examples-toggle summary, .more-examples-toggle .hide-more {
color: #999;
}
.scraped-example .example-wrap .rust span.highlight {
background: #fcffd6;
}
.scraped-example .example-wrap .rust span.highlight.focus {
background: #f6fdb0;
}
.scraped-example:not(.expanded) .code-wrapper:before {
background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}
.scraped-example:not(.expanded) .code-wrapper:after {
background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}
.toggle-line-inner {
background: #ccc;
}
.toggle-line:hover .toggle-line-inner {
background: #999;
}

View file

@ -1,14 +1,28 @@
/* global addClass, hasClass, removeClass, onEach */
(function () {
// Scroll code block to put the given code location in the middle of the viewer
// Number of lines shown when code viewer is not expanded
const MAX_LINES = 10;
// Scroll code block to the given code location
function scrollToLoc(elt, loc) {
var wrapper = elt.querySelector(".code-wrapper");
var halfHeight = wrapper.offsetHeight / 2;
var lines = elt.querySelector('.line-numbers');
var offsetMid = (lines.children[loc[0]].offsetTop
+ lines.children[loc[1]].offsetTop) / 2;
var scrollOffset = offsetMid - halfHeight;
var scrollOffset;
// If the block is greater than the size of the viewer,
// then scroll to the top of the block. Otherwise scroll
// to the middle of the block.
if (loc[1] - loc[0] > MAX_LINES) {
var line = Math.max(0, loc[0] - 1);
scrollOffset = lines.children[line].offsetTop;
} else {
var wrapper = elt.querySelector(".code-wrapper");
var halfHeight = wrapper.offsetHeight / 2;
var offsetMid = (lines.children[loc[0]].offsetTop
+ lines.children[loc[1]].offsetTop) / 2;
scrollOffset = offsetMid - halfHeight;
}
lines.scrollTo(0, scrollOffset);
elt.querySelector(".rust").scrollTo(0, scrollOffset);
}
@ -70,8 +84,10 @@
onEach(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
// Allow users to click the left border of the <details> section to close it,
// since the section can be large and finding the [+] button is annoying.
toggle.querySelector('.toggle-line').addEventListener('click', function() {
toggle.open = false;
toggle.querySelectorAll('.toggle-line, .hide-more').forEach(button => {
button.addEventListener('click', function() {
toggle.open = false;
});
});
var moreExamples = toggle.querySelectorAll('.scraped-example');

View file

@ -0,0 +1,34 @@
Rustdoc will automatically scrape examples of documented items from the `examples/` directory of a project. These examples will be included within the generated documentation for that item. For example, if your library contains a public function:
```rust
// src/lib.rs
pub fn a_func() {}
```
And you have an example calling this function:
```rust
// examples/ex.rs
fn main() {
a_crate::a_func();
}
```
Then this code snippet will be included in the documentation for `a_func`.
## How to read scraped examples
Scraped examples are shown as blocks of code from a given file. The relevant item will be highlighted. If the file is larger than a couple lines, only a small window will be shown which you can expand by clicking &varr; in the top-right. If a file contains multiple instances of an item, you can use the &pr; and &sc; buttons to toggle through each instance.
If there is more than one file that contains examples, then you should click "More examples" to see these examples.
## How Rustdoc scrapes examples
When you run `cargo doc`, Rustdoc will analyze all the crates that match Cargo's `--examples` filter for instances of items that occur in the crates being documented. Then Rustdoc will include the source code of these instances in the generated documentation.
Rustdoc has a few techniques to ensure this doesn'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.