Skip to content

Commit 6ccd14a

Browse files
committed
Improve several aspects of the Rustdoc scrape-examples UI.
* Examples take up less screen height. * Snippets from binary crates are prioritized. * toggle-all-docs does not expand "More examples" sections.
1 parent 01fbc5a commit 6ccd14a

File tree

11 files changed

+106
-33
lines changed

11 files changed

+106
-33
lines changed

src/librustdoc/config.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ pub(crate) struct Options {
6969
pub(crate) input: PathBuf,
7070
/// The name of the crate being documented.
7171
pub(crate) crate_name: Option<String>,
72-
/// Whether or not this is a proc-macro crate
73-
pub(crate) proc_macro_crate: bool,
72+
/// The types of the crate being documented.
73+
pub(crate) crate_types: Vec<CrateType>,
7474
/// How to format errors and warnings.
7575
pub(crate) error_format: ErrorOutputType,
7676
/// Width of output buffer to truncate errors appropriately.
@@ -176,7 +176,7 @@ impl fmt::Debug for Options {
176176
f.debug_struct("Options")
177177
.field("input", &self.input)
178178
.field("crate_name", &self.crate_name)
179-
.field("proc_macro_crate", &self.proc_macro_crate)
179+
.field("crate_types", &self.crate_types)
180180
.field("error_format", &self.error_format)
181181
.field("libs", &self.libs)
182182
.field("externs", &FmtExterns(&self.externs))
@@ -667,7 +667,6 @@ impl Options {
667667
None => OutputFormat::default(),
668668
};
669669
let crate_name = matches.opt_str("crate-name");
670-
let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
671670
let playground_url = matches.opt_str("playground-url");
672671
let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
673672
let module_sorting = if matches.opt_present("sort-modules-by-appearance") {
@@ -718,7 +717,7 @@ impl Options {
718717
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
719718
let options = Options {
720719
input,
721-
proc_macro_crate,
720+
crate_types,
722721
error_format,
723722
diagnostic_width,
724723
libs,

src/librustdoc/core.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_interface::interface;
1313
use rustc_middle::hir::nested_filter;
1414
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
1515
use rustc_resolve as resolve;
16-
use rustc_session::config::{self, CrateType, ErrorOutputType};
16+
use rustc_session::config::{self, ErrorOutputType};
1717
use rustc_session::lint;
1818
use rustc_session::Session;
1919
use rustc_span::symbol::sym;
@@ -203,7 +203,7 @@ pub(crate) fn create_config(
203203
RustdocOptions {
204204
input,
205205
crate_name,
206-
proc_macro_crate,
206+
crate_types,
207207
error_format,
208208
diagnostic_width,
209209
libs,
@@ -247,8 +247,6 @@ pub(crate) fn create_config(
247247
Some((lint.name_lower(), lint::Allow))
248248
});
249249

250-
let crate_types =
251-
if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
252250
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
253251
// plays with error output here!
254252
let sessopts = config::Options {

src/librustdoc/doctest.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::hir::nested_filter;
1212
use rustc_middle::ty::TyCtxt;
1313
use rustc_parse::maybe_new_parser_from_source_str;
1414
use rustc_parse::parser::attr::InnerAttrPolicy;
15-
use rustc_session::config::{self, CrateType, ErrorOutputType};
15+
use rustc_session::config::{self, ErrorOutputType};
1616
use rustc_session::parse::ParseSess;
1717
use rustc_session::{lint, Session};
1818
use rustc_span::edition::Edition;
@@ -68,8 +68,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
6868

6969
debug!(?lint_opts);
7070

71-
let crate_types =
72-
if options.proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
71+
let crate_types = options.crate_types.clone();
7372

7473
let sessopts = config::Options {
7574
maybe_sysroot: options.maybe_sysroot.clone(),

src/librustdoc/html/render/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2957,14 +2957,22 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
29572957

29582958
// The call locations are output in sequence, so that sequence needs to be determined.
29592959
// Ideally the most "relevant" examples would be shown first, but there's no general algorithm
2960-
// for determining relevance. Instead, we prefer the smallest examples being likely the easiest to
2961-
// understand at a glance.
2960+
// for determining relevance. We instead make a proxy for relevance with the following heuristics:
2961+
// 1. Code written to be an example is better than code not written to be an example, e.g.
2962+
// a snippet from examples/foo.rs is better than src/lib.rs. We don't know the Cargo directory
2963+
// structure in Rustdoc, so we proxy this by prioriting code that comes from a --crate-type bin.
2964+
// 2. Smaller examples are better than large examples. So we prioritize snippets that have the
2965+
// smallest line span for their enclosing item.
2966+
// 3. Finally we sort by the displayed file name, which is arbitrary but prevents the ordering
2967+
// of examples from randomly changing between Rustdoc invocations.
29622968
let ordered_locations = {
2963-
let sort_criterion = |(_, call_data): &(_, &CallData)| {
2969+
fn sort_criterion<'a>(
2970+
(_, call_data): &(&PathBuf, &'a CallData),
2971+
) -> (bool, u32, &'a String) {
29642972
// Use the first location because that's what the user will see initially
29652973
let (lo, hi) = call_data.locations[0].enclosing_item.byte_span;
2966-
hi - lo
2967-
};
2974+
(!call_data.is_bin, hi - lo, &call_data.display_name)
2975+
}
29682976

29692977
let mut locs = call_locations.iter().collect::<Vec<_>>();
29702978
locs.sort_by_key(sort_criterion);

src/librustdoc/html/static/css/rustdoc.css

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,6 +1901,10 @@ in storage.js
19011901
border-radius: 50px;
19021902
}
19031903

1904+
.scraped-example {
1905+
position: relative;
1906+
}
1907+
19041908
.scraped-example .code-wrapper {
19051909
position: relative;
19061910
display: flex;
@@ -1909,16 +1913,31 @@ in storage.js
19091913
width: 100%;
19101914
}
19111915

1916+
.scraped-example-title {
1917+
position: absolute;
1918+
z-index: 1000;
1919+
background: white;
1920+
bottom: 8px;
1921+
right: 5px;
1922+
padding: 2px 4px;
1923+
box-shadow: 0 0 4px white;
1924+
}
1925+
19121926
.scraped-example:not(.expanded) .code-wrapper {
1913-
max-height: 240px;
1927+
max-height: 120px;
19141928
}
19151929

19161930
.scraped-example:not(.expanded) .code-wrapper pre {
19171931
overflow-y: hidden;
1918-
max-height: 240px;
1932+
max-height: 120px;
19191933
padding-bottom: 0;
19201934
}
19211935

1936+
.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper,
1937+
.more-scraped-examples .scraped-example:not(.expanded) .code-wrapper pre {
1938+
max-height: 240px;
1939+
}
1940+
19221941
.scraped-example .code-wrapper .next,
19231942
.scraped-example .code-wrapper .prev,
19241943
.scraped-example .code-wrapper .expand {

src/librustdoc/html/static/js/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ function loadCss(cssUrl) {
622622
const innerToggle = document.getElementById(toggleAllDocsId);
623623
removeClass(innerToggle, "will-expand");
624624
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
625-
if (!hasClass(e, "type-contents-toggle")) {
625+
if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
626626
e.open = true;
627627
}
628628
});

src/librustdoc/html/static/js/scrape-examples.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44

55
(function() {
66
// Number of lines shown when code viewer is not expanded
7-
const MAX_LINES = 10;
7+
const DEFAULT_MAX_LINES = 5;
8+
const HIDDEN_MAX_LINES = 10;
89

910
// Scroll code block to the given code location
10-
function scrollToLoc(elt, loc) {
11+
function scrollToLoc(elt, loc, isHidden) {
1112
const lines = elt.querySelector(".src-line-numbers");
1213
let scrollOffset;
1314

1415
// If the block is greater than the size of the viewer,
1516
// then scroll to the top of the block. Otherwise scroll
1617
// to the middle of the block.
17-
if (loc[1] - loc[0] > MAX_LINES) {
18+
let maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES;
19+
if (loc[1] - loc[0] > maxLines) {
1820
const line = Math.max(0, loc[0] - 1);
1921
scrollOffset = lines.children[line].offsetTop;
2022
} else {
@@ -29,7 +31,7 @@
2931
elt.querySelector(".rust").scrollTo(0, scrollOffset);
3032
}
3133

32-
function updateScrapedExample(example) {
34+
function updateScrapedExample(example, isHidden) {
3335
const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
3436
let locIndex = 0;
3537
const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight"));
@@ -40,7 +42,7 @@
4042
const onChangeLoc = changeIndex => {
4143
removeClass(highlights[locIndex], "focus");
4244
changeIndex();
43-
scrollToLoc(example, locs[locIndex][0]);
45+
scrollToLoc(example, locs[locIndex][0], isHidden);
4446
addClass(highlights[locIndex], "focus");
4547

4648
const url = locs[locIndex][1];
@@ -70,19 +72,19 @@
7072
expandButton.addEventListener("click", () => {
7173
if (hasClass(example, "expanded")) {
7274
removeClass(example, "expanded");
73-
scrollToLoc(example, locs[0][0]);
75+
scrollToLoc(example, locs[0][0], isHidden);
7476
} else {
7577
addClass(example, "expanded");
7678
}
7779
});
7880
}
7981

8082
// Start with the first example in view
81-
scrollToLoc(example, locs[0][0]);
83+
scrollToLoc(example, locs[0][0], isHidden);
8284
}
8385

8486
const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example");
85-
onEachLazy(firstExamples, updateScrapedExample);
87+
onEachLazy(firstExamples, el => updateScrapedExample(el, false));
8688
onEachLazy(document.querySelectorAll(".more-examples-toggle"), toggle => {
8789
// Allow users to click the left border of the <details> section to close it,
8890
// since the section can be large and finding the [+] button is annoying.
@@ -99,7 +101,7 @@
99101
// depends on offsetHeight, a property that requires an element to be visible to
100102
// compute correctly.
101103
setTimeout(() => {
102-
onEachLazy(moreExamples, updateScrapedExample);
104+
onEachLazy(moreExamples, el => updateScrapedExample(el, true));
103105
});
104106
}, {once: true});
105107
});

src/librustdoc/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ fn main_args(at_args: &[String]) -> MainResult {
774774
let output_format = options.output_format;
775775
let externs = options.externs.clone();
776776
let scrape_examples_options = options.scrape_examples_options.clone();
777+
let crate_types = options.crate_types.clone();
777778

778779
let config = core::create_config(options);
779780

@@ -832,7 +833,14 @@ fn main_args(at_args: &[String]) -> MainResult {
832833
info!("finished with rustc");
833834

834835
if let Some(options) = scrape_examples_options {
835-
return scrape_examples::run(krate, render_opts, cache, tcx, options);
836+
return scrape_examples::run(
837+
krate,
838+
render_opts,
839+
cache,
840+
tcx,
841+
options,
842+
crate_types,
843+
);
836844
}
837845

838846
cache.crate_version = crate_version;

src/librustdoc/scrape_examples.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_serialize::{
2020
opaque::{FileEncoder, MemDecoder},
2121
Decodable, Encodable,
2222
};
23-
use rustc_session::getopts;
23+
use rustc_session::{config::CrateType, getopts};
2424
use rustc_span::{
2525
def_id::{CrateNum, DefPathHash, LOCAL_CRATE},
2626
edition::Edition,
@@ -110,6 +110,7 @@ pub(crate) struct CallData {
110110
pub(crate) url: String,
111111
pub(crate) display_name: String,
112112
pub(crate) edition: Edition,
113+
pub(crate) is_bin: bool,
113114
}
114115

115116
pub(crate) type FnCallLocations = FxHashMap<PathBuf, CallData>;
@@ -122,6 +123,7 @@ struct FindCalls<'a, 'tcx> {
122123
cx: Context<'tcx>,
123124
target_crates: Vec<CrateNum>,
124125
calls: &'a mut AllCallLocations,
126+
crate_types: Vec<CrateType>,
125127
}
126128

127129
impl<'a, 'tcx> Visitor<'tcx> for FindCalls<'a, 'tcx>
@@ -245,7 +247,9 @@ where
245247
let mk_call_data = || {
246248
let display_name = file_path.display().to_string();
247249
let edition = call_span.edition();
248-
CallData { locations: Vec::new(), url, display_name, edition }
250+
let is_bin = self.crate_types.contains(&CrateType::Executable);
251+
252+
CallData { locations: Vec::new(), url, display_name, edition, is_bin }
249253
};
250254

251255
let fn_key = tcx.def_path_hash(*def_id);
@@ -274,6 +278,7 @@ pub(crate) fn run(
274278
cache: formats::cache::Cache,
275279
tcx: TyCtxt<'_>,
276280
options: ScrapeExamplesOptions,
281+
crate_types: Vec<CrateType>,
277282
) -> interface::Result<()> {
278283
let inner = move || -> Result<(), String> {
279284
// Generates source files for examples
@@ -300,7 +305,8 @@ pub(crate) fn run(
300305

301306
// Run call-finder on all items
302307
let mut calls = FxHashMap::default();
303-
let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
308+
let mut finder =
309+
FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates, crate_types };
304310
tcx.hir().visit_all_item_likes_in_crate(&mut finder);
305311

306312
// The visitor might have found a type error, which we need to

src/test/rustdoc-gui/scrape-examples-button-focus.goml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,12 @@ store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre
2525
assert-property: (".scraped-example-list > .scraped-example pre", {
2626
"scrollHeight": |fullOffsetHeight|
2727
})
28+
29+
assert-attribute-false: (".more-examples-toggle", {"open": ""})
30+
click: ".more-examples-toggle"
31+
assert-attribute: (".more-examples-toggle", {"open": ""})
32+
click: "#toggle-all-docs"
33+
assert-attribute-false: (".more-examples-toggle", {"open": ""})
34+
// After re-opening the docs, the additional examples should stay closed
35+
click: "#toggle-all-docs"
36+
assert-attribute-false: (".more-examples-toggle", {"open": ""})
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
fn main() {
2+
for i in 0..9 {
3+
println!("hello world!");
4+
println!("hello world!");
5+
println!("hello world!");
6+
println!("hello world!");
7+
println!("hello world!");
8+
println!("hello world!");
9+
println!("hello world!");
10+
println!("hello world!");
11+
println!("hello world!");
12+
}
13+
scrape_examples::test();
14+
for i in 0..9 {
15+
println!("hello world!");
16+
println!("hello world!");
17+
println!("hello world!");
18+
println!("hello world!");
19+
println!("hello world!");
20+
println!("hello world!");
21+
println!("hello world!");
22+
println!("hello world!");
23+
println!("hello world!");
24+
}
25+
}

0 commit comments

Comments
 (0)