From 7e81b0a3173470cbc77273b2f984913dd3c02ef8 Mon Sep 17 00:00:00 2001 From: Will Crichton Date: Sat, 22 Jan 2022 15:32:19 -0800 Subject: [PATCH] Improve Rustdoc UI for scraped examples with multiline arguments, fix overflow in line numbers --- src/librustdoc/html/render/mod.rs | 32 +++++++++++++++++-- src/librustdoc/html/static/css/rustdoc.css | 11 +++---- .../html/static/js/scrape-examples.js | 26 +++++++++++---- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 3666767a9d9c..23ce3b131bbd 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -2717,6 +2717,30 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) { // The output code is limited to that byte range. let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)]; + // Given a call-site range, return the set of sub-ranges that exclude leading whitespace + // when the range spans multiple lines. + let strip_leading_whitespace = |(lo, hi): (u32, u32)| -> Vec<(u32, u32)> { + let contents_range = &contents_subset[(lo as usize)..(hi as usize)]; + let mut ignoring_whitespace = false; + let mut ranges = Vec::new(); + let mut cur_lo = 0; + for (idx, chr) in contents_range.char_indices() { + let idx = idx as u32; + if ignoring_whitespace { + if !chr.is_whitespace() { + ignoring_whitespace = false; + cur_lo = idx; + } + } else if chr == '\n' { + ranges.push((lo + cur_lo, lo + idx)); + cur_lo = idx; + ignoring_whitespace = true; + } + } + ranges.push((lo + cur_lo, hi)); + ranges + }; + // The call locations need to be updated to reflect that the size of the program has changed. // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point. let (mut byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data @@ -2726,10 +2750,12 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) { let (byte_lo, byte_hi) = loc.call_expr.byte_span; let (line_lo, line_hi) = loc.call_expr.line_span; let byte_range = (byte_lo - byte_min, byte_hi - byte_min); + let byte_ranges = strip_leading_whitespace(byte_range); + let line_range = (line_lo - line_min, line_hi - line_min); let (line_url, line_title) = link_to_loc(call_data, loc); - (byte_range, (line_range, line_url, line_title)) + (byte_ranges, (line_range, line_url, line_title)) }) .unzip(); @@ -2784,8 +2810,8 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) { let root_path = vec!["../"; cx.current.len() - 1].join(""); let mut decoration_info = FxHashMap::default(); - decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]); - decoration_info.insert("highlight", byte_ranges); + decoration_info.insert("highlight focus", byte_ranges.remove(0)); + decoration_info.insert("highlight", byte_ranges.into_iter().flatten().collect()); sources::print_src( w, diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index ee265b8c4b54..42b66c70c4cb 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -2038,17 +2038,16 @@ details.rustdoc-toggle[open] > summary.hideme::after { font-family: 'Fira Sans'; } -.scraped-example:not(.expanded) .code-wrapper pre.line-numbers { - overflow: hidden; - 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; diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 664b187e33e9..383ae001bc21 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -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); }