1
Fork 0

Move some expansion logic into generation-time, fix section header links, remove ID from line numbers, fix horizontal scrolling on non-expanded elements

This commit is contained in:
Will Crichton 2021-10-07 09:46:18 -07:00
parent 5584c79597
commit bb383edb69
4 changed files with 54 additions and 40 deletions

View file

@ -2461,6 +2461,7 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
} }
const MAX_FULL_EXAMPLES: usize = 5; const MAX_FULL_EXAMPLES: usize = 5;
const NUM_VISIBLE_LINES: usize = 10;
/// Generates the HTML for example call locations generated via the --scrape-examples flag. /// Generates the HTML for example call locations generated via the --scrape-examples flag.
fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item: &clean::Item) { fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item: &clean::Item) {
@ -2480,10 +2481,10 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item:
w, w,
"<div class=\"docblock scraped-example-list\">\ "<div class=\"docblock scraped-example-list\">\
<span></span> <span></span>
<h5 id=\"scraped-examples\" class=\"section-header\">\ <h5 id=\"{id}\" class=\"section-header\">\
<a href=\"#{}\">Examples found in repository</a>\ <a href=\"#{id}\">Examples found in repository</a>\
</h5>", </h5>",
id id = id
); );
// Generate the HTML for a single example, being the title and code block // Generate the HTML for a single example, being the title and code block
@ -2503,54 +2504,58 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item:
assert!(!call_data.locations.is_empty()); assert!(!call_data.locations.is_empty());
let min_loc = let min_loc =
call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap(); call_data.locations.iter().min_by_key(|loc| loc.enclosing_item.byte_span.0).unwrap();
let (byte_offset, _) = min_loc.enclosing_item.byte_span; let byte_min = min_loc.enclosing_item.byte_span.0;
let (line_offset, _) = min_loc.enclosing_item.line_span; let line_min = min_loc.enclosing_item.line_span.0;
let byte_ceiling = let max_loc =
call_data.locations.iter().map(|loc| loc.enclosing_item.byte_span.1).max().unwrap(); call_data.locations.iter().max_by_key(|loc| loc.enclosing_item.byte_span.1).unwrap();
let byte_max = max_loc.enclosing_item.byte_span.1;
let line_max = max_loc.enclosing_item.line_span.1;
// The output code is limited to that byte range. // The output code is limited to that byte range.
let contents_subset = &contents[(byte_offset as usize)..(byte_ceiling as usize)]; let contents_subset = &contents[(byte_min as usize)..(byte_max as usize)];
// The call locations need to be updated to reflect that the size of the program has changed. // 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_offset` since that's the new zero point. // Specifically, the ranges are all subtracted by `byte_min` since that's the new zero point.
let (byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data let (byte_ranges, line_ranges): (Vec<_>, Vec<_>) = call_data
.locations .locations
.iter() .iter()
.map(|loc| { .map(|loc| {
let (byte_lo, byte_hi) = loc.call_expr.byte_span; let (byte_lo, byte_hi) = loc.call_expr.byte_span;
let (line_lo, line_hi) = loc.call_expr.line_span; let (line_lo, line_hi) = loc.call_expr.line_span;
( ((byte_lo - byte_min, byte_hi - byte_min), (line_lo - line_min, line_hi - line_min))
(byte_lo - byte_offset, byte_hi - byte_offset),
(line_lo - line_offset, line_hi - line_offset),
)
}) })
.unzip(); .unzip();
let (init_min, init_max) = line_ranges[0]; let (init_min, init_max) = line_ranges[0];
let line_range = if init_min == init_max { let line_range = if init_min == init_max {
format!("line {}", init_min + line_offset + 1) format!("line {}", init_min + line_min + 1)
} else { } else {
format!("lines {}-{}", init_min + line_offset + 1, init_max + line_offset + 1) format!("lines {}-{}", init_min + line_min + 1, init_max + line_min + 1)
}; };
let needs_expansion = line_max - line_min > NUM_VISIBLE_LINES;
write!( write!(
w, w,
"<div class=\"scraped-example\" data-locs=\"{locations}\" data-offset=\"{offset}\">\ "<div class=\"scraped-example {expanded_cls}\" data-locs=\"{locations}\">\
<div class=\"scraped-example-title\">\ <div class=\"scraped-example-title\">\
{name} (<a href=\"{root}{url}\" target=\"_blank\">{line_range}</a>)\ {name} (<a href=\"{root}{url}\">{line_range}</a>)\
</div>\ </div>\
<div class=\"code-wrapper\">", <div class=\"code-wrapper\">",
root = cx.root_path(), root = cx.root_path(),
url = call_data.url, url = call_data.url,
name = call_data.display_name, name = call_data.display_name,
line_range = line_range, line_range = line_range,
offset = line_offset, expanded_cls = if needs_expansion { "" } else { "expanded" },
// The locations are encoded as a data attribute, so they can be read // The locations are encoded as a data attribute, so they can be read
// later by the JS for interactions. // later by the JS for interactions.
locations = serde_json::to_string(&line_ranges).unwrap(), locations = serde_json::to_string(&line_ranges).unwrap(),
); );
write!(w, r#"<span class="prev">&pr;</span> <span class="next">&sc;</span>"#); write!(w, r#"<span class="prev">&pr;</span> <span class="next">&sc;</span>"#);
if needs_expansion {
write!(w, r#"<span class="expand">&varr;</span>"#); write!(w, r#"<span class="expand">&varr;</span>"#);
}
// Look for the example file in the source map if it exists, otherwise return a dummy span // Look for the example file in the source map if it exists, otherwise return a dummy span
let file_span = (|| { let file_span = (|| {
@ -2565,8 +2570,8 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item:
_ => false, _ => false,
})?; })?;
Some(rustc_span::Span::with_root_ctxt( Some(rustc_span::Span::with_root_ctxt(
file.start_pos + BytePos(byte_offset), file.start_pos + BytePos(byte_min),
file.start_pos + BytePos(byte_ceiling), file.start_pos + BytePos(byte_max),
)) ))
})() })()
.unwrap_or(rustc_span::DUMMY_SP); .unwrap_or(rustc_span::DUMMY_SP);
@ -2584,8 +2589,8 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item:
file_span, file_span,
cx, cx,
&root_path, &root_path,
Some(line_offset),
Some(highlight::DecorationInfo(decoration_info)), Some(highlight::DecorationInfo(decoration_info)),
sources::SourceContext::Embedded { offset: line_min },
); );
write!(w, "</div></div>"); write!(w, "</div></div>");
@ -2648,7 +2653,7 @@ fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, def_id: DefId, item:
it.for_each(|(_, call_data)| { it.for_each(|(_, call_data)| {
write!( write!(
w, w,
r#"<li><a href="{root}{url}" target="_blank">{name}</a></li>"#, r#"<li><a href="{root}{url}">{name}</a></li>"#,
root = cx.root_path(), root = cx.root_path(),
url = call_data.url, url = call_data.url,
name = call_data.display_name name = call_data.display_name

View file

@ -212,7 +212,7 @@ impl SourceCollector<'_, 'tcx> {
&self.cx, &self.cx,
&root_path, &root_path,
None, None,
None, SourceContext::Standalone,
) )
}, },
&self.cx.shared.style_files, &self.cx.shared.style_files,
@ -250,6 +250,11 @@ where
} }
} }
crate enum SourceContext {
Standalone,
Embedded { offset: usize },
}
/// Wrapper struct to render the source code of a file. This will do things like /// Wrapper struct to render the source code of a file. This will do things like
/// adding line numbers to the left-hand side. /// adding line numbers to the left-hand side.
crate fn print_src( crate fn print_src(
@ -259,8 +264,8 @@ crate fn print_src(
file_span: rustc_span::Span, file_span: rustc_span::Span,
context: &Context<'_>, context: &Context<'_>,
root_path: &str, root_path: &str,
offset: Option<usize>,
decoration_info: Option<highlight::DecorationInfo>, decoration_info: Option<highlight::DecorationInfo>,
source_context: SourceContext,
) { ) {
let lines = s.lines().count(); let lines = s.lines().count();
let mut line_numbers = Buffer::empty_from(buf); let mut line_numbers = Buffer::empty_from(buf);
@ -271,9 +276,15 @@ crate fn print_src(
tmp /= 10; tmp /= 10;
} }
line_numbers.write_str("<pre class=\"line-numbers\">"); line_numbers.write_str("<pre class=\"line-numbers\">");
let offset = offset.unwrap_or(0);
for i in 1..=lines { for i in 1..=lines {
writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i + offset, cols); match source_context {
SourceContext::Standalone => {
writeln!(line_numbers, "<span id=\"{0}\">{0:1$}</span>", i, cols)
}
SourceContext::Embedded { offset } => {
writeln!(line_numbers, "<span>{0:1$}</span>", i + offset, cols)
}
}
} }
line_numbers.write_str("</pre>"); line_numbers.write_str("</pre>");
highlight::render_with_highlighting( highlight::render_with_highlighting(

View file

@ -1978,12 +1978,16 @@ details.undocumented[open] > summary::before {
font-family: 'Fira Sans'; font-family: 'Fira Sans';
} }
.scraped-example:not(.expanded) .code-wrapper pre.line-numbers, .scraped-example:not(.expanded) .code-wrapper pre.line-numbers {
.scraped-example:not(.expanded) .code-wrapper .example-wrap pre.rust {
overflow: hidden; overflow: hidden;
max-height: 240px; max-height: 240px;
} }
.scraped-example:not(.expanded) .code-wrapper .example-wrap pre.rust {
overflow-y: hidden;
max-height: 240px;
}
.scraped-example .code-wrapper .prev { .scraped-example .code-wrapper .prev {
position: absolute; position: absolute;
top: 0.25em; top: 0.25em;
@ -2019,7 +2023,7 @@ details.undocumented[open] > summary::before {
.scraped-example:not(.expanded) .code-wrapper:before { .scraped-example:not(.expanded) .code-wrapper:before {
content: " "; content: " ";
width: 100%; width: 100%;
height: 10px; height: 5px;
position: absolute; position: absolute;
z-index: 100; z-index: 100;
top: 0; top: 0;
@ -2029,7 +2033,7 @@ details.undocumented[open] > summary::before {
.scraped-example:not(.expanded) .code-wrapper:after { .scraped-example:not(.expanded) .code-wrapper:after {
content: " "; content: " ";
width: 100%; width: 100%;
height: 10px; height: 5px;
position: absolute; position: absolute;
z-index: 100; z-index: 100;
bottom: 0; bottom: 0;

View file

@ -15,7 +15,8 @@
function updateScrapedExample(example) { function updateScrapedExample(example) {
var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent); var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
var offset = parseInt(example.attributes.getNamedItem("data-offset").textContent); var first_line_no = example.querySelector('.line-numbers > span:first-child');
var offset = parseInt(first_line_no.innerHTML) - 1;
var locIndex = 0; var locIndex = 0;
var highlights = example.querySelectorAll('.highlight'); var highlights = example.querySelectorAll('.highlight');
@ -68,11 +69,8 @@
example.querySelector('.next').remove(); example.querySelector('.next').remove();
} }
var codeEl = example.querySelector('.rust');
var codeOverflows = codeEl.scrollHeight > codeEl.clientHeight;
var expandButton = example.querySelector('.expand'); var expandButton = example.querySelector('.expand');
if (codeOverflows) { if (expandButton) {
// If file is larger than default height, give option to expand the viewer
expandButton.addEventListener('click', function () { expandButton.addEventListener('click', function () {
if (hasClass(example, "expanded")) { if (hasClass(example, "expanded")) {
removeClass(example, "expanded"); removeClass(example, "expanded");
@ -81,10 +79,6 @@
addClass(example, "expanded"); addClass(example, "expanded");
} }
}); });
} else {
// Otherwise remove expansion buttons
addClass(example, 'expanded');
expandButton.remove();
} }
// Start with the first example in view // Start with the first example in view