Skip to content

Commit

Permalink
Improve several aspects of the Rustdoc scrape-examples UI.
Browse files Browse the repository at this point in the history
* Examples take up less screen height.
* Snippets from binary crates are prioritized.
* toggle-all-docs does not expand "More examples" sections.
  • Loading branch information
willcrichton committed Dec 7, 2022
1 parent 01fbc5a commit 6ccd14a
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 33 deletions.
9 changes: 4 additions & 5 deletions src/librustdoc/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ pub(crate) struct Options {
pub(crate) input: PathBuf,
/// The name of the crate being documented.
pub(crate) crate_name: Option<String>,
/// Whether or not this is a proc-macro crate
pub(crate) proc_macro_crate: bool,
/// The types of the crate being documented.
pub(crate) crate_types: Vec<CrateType>,
/// How to format errors and warnings.
pub(crate) error_format: ErrorOutputType,
/// Width of output buffer to truncate errors appropriately.
Expand Down Expand Up @@ -176,7 +176,7 @@ impl fmt::Debug for Options {
f.debug_struct("Options")
.field("input", &self.input)
.field("crate_name", &self.crate_name)
.field("proc_macro_crate", &self.proc_macro_crate)
.field("crate_types", &self.crate_types)
.field("error_format", &self.error_format)
.field("libs", &self.libs)
.field("externs", &FmtExterns(&self.externs))
Expand Down Expand Up @@ -667,7 +667,6 @@ impl Options {
None => OutputFormat::default(),
};
let crate_name = matches.opt_str("crate-name");
let proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
let playground_url = matches.opt_str("playground-url");
let maybe_sysroot = matches.opt_str("sysroot").map(PathBuf::from);
let module_sorting = if matches.opt_present("sort-modules-by-appearance") {
Expand Down Expand Up @@ -718,7 +717,7 @@ impl Options {
rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
let options = Options {
input,
proc_macro_crate,
crate_types,
error_format,
diagnostic_width,
libs,
Expand Down
6 changes: 2 additions & 4 deletions src/librustdoc/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rustc_interface::interface;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_resolve as resolve;
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::config::{self, ErrorOutputType};
use rustc_session::lint;
use rustc_session::Session;
use rustc_span::symbol::sym;
Expand Down Expand Up @@ -203,7 +203,7 @@ pub(crate) fn create_config(
RustdocOptions {
input,
crate_name,
proc_macro_crate,
crate_types,
error_format,
diagnostic_width,
libs,
Expand Down Expand Up @@ -247,8 +247,6 @@ pub(crate) fn create_config(
Some((lint.name_lower(), lint::Allow))
});

let crate_types =
if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
// plays with error output here!
let sessopts = config::Options {
Expand Down
5 changes: 2 additions & 3 deletions src/librustdoc/doctest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_parse::parser::attr::InnerAttrPolicy;
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::config::{self, ErrorOutputType};
use rustc_session::parse::ParseSess;
use rustc_session::{lint, Session};
use rustc_span::edition::Edition;
Expand Down Expand Up @@ -68,8 +68,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {

debug!(?lint_opts);

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

let sessopts = config::Options {
maybe_sysroot: options.maybe_sysroot.clone(),
Expand Down
18 changes: 13 additions & 5 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2957,14 +2957,22 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite

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

let mut locs = call_locations.iter().collect::<Vec<_>>();
locs.sort_by_key(sort_criterion);
Expand Down
23 changes: 21 additions & 2 deletions src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -1901,6 +1901,10 @@ in storage.js
border-radius: 50px;
}

.scraped-example {
position: relative;
}

.scraped-example .code-wrapper {
position: relative;
display: flex;
Expand All @@ -1909,16 +1913,31 @@ in storage.js
width: 100%;
}

.scraped-example-title {
position: absolute;
z-index: 1000;
background: white;
bottom: 8px;
right: 5px;
padding: 2px 4px;
box-shadow: 0 0 4px white;
}

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

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

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

.scraped-example .code-wrapper .next,
.scraped-example .code-wrapper .prev,
.scraped-example .code-wrapper .expand {
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/html/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ function loadCss(cssUrl) {
const innerToggle = document.getElementById(toggleAllDocsId);
removeClass(innerToggle, "will-expand");
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
if (!hasClass(e, "type-contents-toggle")) {
if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
e.open = true;
}
});
Expand Down
20 changes: 11 additions & 9 deletions src/librustdoc/html/static/js/scrape-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@

(function() {
// Number of lines shown when code viewer is not expanded
const MAX_LINES = 10;
const DEFAULT_MAX_LINES = 5;
const HIDDEN_MAX_LINES = 10;

// Scroll code block to the given code location
function scrollToLoc(elt, loc) {
function scrollToLoc(elt, loc, isHidden) {
const lines = elt.querySelector(".src-line-numbers");
let 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) {
let maxLines = isHidden ? HIDDEN_MAX_LINES : DEFAULT_MAX_LINES;
if (loc[1] - loc[0] > maxLines) {
const line = Math.max(0, loc[0] - 1);
scrollOffset = lines.children[line].offsetTop;
} else {
Expand All @@ -29,7 +31,7 @@
elt.querySelector(".rust").scrollTo(0, scrollOffset);
}

function updateScrapedExample(example) {
function updateScrapedExample(example, isHidden) {
const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
let locIndex = 0;
const highlights = Array.prototype.slice.call(example.querySelectorAll(".highlight"));
Expand All @@ -40,7 +42,7 @@
const onChangeLoc = changeIndex => {
removeClass(highlights[locIndex], "focus");
changeIndex();
scrollToLoc(example, locs[locIndex][0]);
scrollToLoc(example, locs[locIndex][0], isHidden);
addClass(highlights[locIndex], "focus");

const url = locs[locIndex][1];
Expand Down Expand Up @@ -70,19 +72,19 @@
expandButton.addEventListener("click", () => {
if (hasClass(example, "expanded")) {
removeClass(example, "expanded");
scrollToLoc(example, locs[0][0]);
scrollToLoc(example, locs[0][0], isHidden);
} else {
addClass(example, "expanded");
}
});
}

// Start with the first example in view
scrollToLoc(example, locs[0][0]);
scrollToLoc(example, locs[0][0], isHidden);
}

const firstExamples = document.querySelectorAll(".scraped-example-list > .scraped-example");
onEachLazy(firstExamples, updateScrapedExample);
onEachLazy(firstExamples, el => updateScrapedExample(el, false));
onEachLazy(document.querySelectorAll(".more-examples-toggle"), 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.
Expand All @@ -99,7 +101,7 @@
// depends on offsetHeight, a property that requires an element to be visible to
// compute correctly.
setTimeout(() => {
onEachLazy(moreExamples, updateScrapedExample);
onEachLazy(moreExamples, el => updateScrapedExample(el, true));
});
}, {once: true});
});
Expand Down
10 changes: 9 additions & 1 deletion src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@ fn main_args(at_args: &[String]) -> MainResult {
let output_format = options.output_format;
let externs = options.externs.clone();
let scrape_examples_options = options.scrape_examples_options.clone();
let crate_types = options.crate_types.clone();

let config = core::create_config(options);

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

if let Some(options) = scrape_examples_options {
return scrape_examples::run(krate, render_opts, cache, tcx, options);
return scrape_examples::run(
krate,
render_opts,
cache,
tcx,
options,
crate_types,
);
}

cache.crate_version = crate_version;
Expand Down
12 changes: 9 additions & 3 deletions src/librustdoc/scrape_examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use rustc_serialize::{
opaque::{FileEncoder, MemDecoder},
Decodable, Encodable,
};
use rustc_session::getopts;
use rustc_session::{config::CrateType, getopts};
use rustc_span::{
def_id::{CrateNum, DefPathHash, LOCAL_CRATE},
edition::Edition,
Expand Down Expand Up @@ -110,6 +110,7 @@ pub(crate) struct CallData {
pub(crate) url: String,
pub(crate) display_name: String,
pub(crate) edition: Edition,
pub(crate) is_bin: bool,
}

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

impl<'a, 'tcx> Visitor<'tcx> for FindCalls<'a, 'tcx>
Expand Down Expand Up @@ -245,7 +247,9 @@ where
let mk_call_data = || {
let display_name = file_path.display().to_string();
let edition = call_span.edition();
CallData { locations: Vec::new(), url, display_name, edition }
let is_bin = self.crate_types.contains(&CrateType::Executable);

CallData { locations: Vec::new(), url, display_name, edition, is_bin }
};

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

// Run call-finder on all items
let mut calls = FxHashMap::default();
let mut finder = FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates };
let mut finder =
FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates, crate_types };
tcx.hir().visit_all_item_likes_in_crate(&mut finder);

// The visitor might have found a type error, which we need to
Expand Down
9 changes: 9 additions & 0 deletions src/test/rustdoc-gui/scrape-examples-button-focus.goml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ store-property: (fullOffsetHeight, ".scraped-example-list > .scraped-example pre
assert-property: (".scraped-example-list > .scraped-example pre", {
"scrollHeight": |fullOffsetHeight|
})

assert-attribute-false: (".more-examples-toggle", {"open": ""})
click: ".more-examples-toggle"
assert-attribute: (".more-examples-toggle", {"open": ""})
click: "#toggle-all-docs"
assert-attribute-false: (".more-examples-toggle", {"open": ""})
// After re-opening the docs, the additional examples should stay closed
click: "#toggle-all-docs"
assert-attribute-false: (".more-examples-toggle", {"open": ""})
25 changes: 25 additions & 0 deletions src/test/rustdoc-gui/src/scrape_examples/examples/check2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
fn main() {
for i in 0..9 {
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
}
scrape_examples::test();
for i in 0..9 {
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
println!("hello world!");
}
}

0 comments on commit 6ccd14a

Please sign in to comment.