Skip to content

Commit

Permalink
Auto merge of #56005 - GuillaumeGomez:speedup-doc-render, r=QuietMisd…
Browse files Browse the repository at this point in the history
…reavus

Greatly improve rustdoc rendering speed issues

Fixes #55900.

So a few improvements here:

* we're switching to `DOMTokenList` API when available providing a replacement if it isn't (should only happen on safari and IE I think...)
* hide doc sections by default to allow the whole HTML generation to happen in the background to avoid triggering DOM redraw all the times (which killed the performances)

r? @QuietMisdreavus
  • Loading branch information
bors committed Dec 15, 2018
2 parents 91857a3 + b8c1726 commit 7f04a64
Show file tree
Hide file tree
Showing 7 changed files with 688 additions and 612 deletions.
1 change: 1 addition & 0 deletions src/librustdoc/html/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub fn render<T: fmt::Display, S: fmt::Display>(
<link rel=\"stylesheet\" type=\"text/css\" href=\"{root_path}light{suffix}.css\" \
id=\"themeStyle\">\
<script src=\"{root_path}storage{suffix}.js\"></script>\
<noscript><link rel=\"stylesheet\" href=\"{root_path}noscript{suffix}.css\"></noscript>\
{css_extension}\
{favicon}\
{in_header}\
Expand Down
104 changes: 47 additions & 57 deletions src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,9 @@ fn write_shared(
write_minify(cx.dst.join(&format!("settings{}.css", cx.shared.resource_suffix)),
static_files::SETTINGS_CSS,
options.enable_minification)?;
write_minify(cx.dst.join(&format!("noscript{}.css", cx.shared.resource_suffix)),
static_files::NOSCRIPT_CSS,
options.enable_minification)?;

// To avoid "light.css" to be overwritten, we'll first run over the received themes and only
// then we'll run over the "official" styles.
Expand Down Expand Up @@ -865,9 +868,8 @@ themePicker.onblur = handleThemeButtonsBlur;
}

{
let mut data = format!("var resourcesSuffix = \"{}\";\n",
cx.shared.resource_suffix);
data.push_str(static_files::STORAGE_JS);
let mut data = static_files::STORAGE_JS.to_owned();
data.push_str(&format!("var resourcesSuffix = \"{}\";", cx.shared.resource_suffix));
write_minify(cx.dst.join(&format!("storage{}.js", cx.shared.resource_suffix)),
&data,
options.enable_minification)?;
Expand Down Expand Up @@ -3013,6 +3015,22 @@ fn item_trait(
// Trait documentation
document(w, cx, it)?;

fn write_small_section_header(
w: &mut fmt::Formatter,
id: &str,
title: &str,
extra_content: &str,
) -> fmt::Result {
write!(w, "
<h2 id='{0}' class='small-section-header'>\
{1}<a href='#{0}' class='anchor'></a>\
</h2>{2}", id, title, extra_content)
}

fn write_loading_content(w: &mut fmt::Formatter, extra_content: &str) -> fmt::Result {
write!(w, "{}<span class='loading-content'>Loading content...</span>", extra_content)
}

fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item, t: &clean::Item)
-> fmt::Result {
let name = m.name.as_ref().unwrap();
Expand All @@ -3033,74 +3051,45 @@ fn item_trait(
}

if !types.is_empty() {
write!(w, "
<h2 id='associated-types' class='small-section-header'>
Associated Types<a href='#associated-types' class='anchor'></a>
</h2>
<div class='methods'>
")?;
write_small_section_header(w, "associated-types", "Associated Types",
"<div class='methods'>")?;
for t in &types {
trait_item(w, cx, *t, it)?;
}
write!(w, "</div>")?;
write_loading_content(w, "</div>")?;
}

if !consts.is_empty() {
write!(w, "
<h2 id='associated-const' class='small-section-header'>
Associated Constants<a href='#associated-const' class='anchor'></a>
</h2>
<div class='methods'>
")?;
write_small_section_header(w, "associated-const", "Associated Constants",
"<div class='methods'>")?;
for t in &consts {
trait_item(w, cx, *t, it)?;
}
write!(w, "</div>")?;
write_loading_content(w, "</div>")?;
}

// Output the documentation for each function individually
if !required.is_empty() {
write!(w, "
<h2 id='required-methods' class='small-section-header'>
Required Methods<a href='#required-methods' class='anchor'></a>
</h2>
<div class='methods'>
")?;
write_small_section_header(w, "required-methods", "Required methods",
"<div class='methods'>")?;
for m in &required {
trait_item(w, cx, *m, it)?;
}
write!(w, "</div>")?;
write_loading_content(w, "</div>")?;
}
if !provided.is_empty() {
write!(w, "
<h2 id='provided-methods' class='small-section-header'>
Provided Methods<a href='#provided-methods' class='anchor'></a>
</h2>
<div class='methods'>
")?;
write_small_section_header(w, "provided-methods", "Provided methods",
"<div class='methods'>")?;
for m in &provided {
trait_item(w, cx, *m, it)?;
}
write!(w, "</div>")?;
write_loading_content(w, "</div>")?;
}

// If there are methods directly on this trait object, render them here.
render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?;

let cache = cache();
let impl_header = "\
<h2 id='implementors' class='small-section-header'>\
Implementors<a href='#implementors' class='anchor'></a>\
</h2>\
<div class='item-list' id='implementors-list'>\
";

let synthetic_impl_header = "\
<h2 id='synthetic-implementors' class='small-section-header'>\
Auto implementors<a href='#synthetic-implementors' class='anchor'></a>\
</h2>\
<div class='item-list' id='synthetic-implementors-list'>\
";

let mut synthetic_types = Vec::new();

Expand Down Expand Up @@ -3137,11 +3126,7 @@ fn item_trait(
concrete.sort_by(compare_impl);

if !foreign.is_empty() {
write!(w, "
<h2 id='foreign-impls' class='small-section-header'>
Implementations on Foreign Types<a href='#foreign-impls' class='anchor'></a>
</h2>
")?;
write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", "")?;

for implementor in foreign {
let assoc_link = AssocItemLink::GotoSource(
Expand All @@ -3152,33 +3137,38 @@ fn item_trait(
RenderMode::Normal, implementor.impl_item.stable_since(), false,
None)?;
}
write_loading_content(w, "")?;
}

write!(w, "{}", impl_header)?;
write_small_section_header(w, "implementors", "Implementors",
"<div class='item-list' id='implementors-list'>")?;
for implementor in concrete {
render_implementor(cx, implementor, w, &implementor_dups)?;
}
write!(w, "</div>")?;
write_loading_content(w, "</div>")?;

if t.auto {
write!(w, "{}", synthetic_impl_header)?;
write_small_section_header(w, "synthetic-implementors", "Auto implementors",
"<div class='item-list' id='synthetic-implementors-list'>")?;
for implementor in synthetic {
synthetic_types.extend(
collect_paths_for_type(implementor.inner_impl().for_.clone())
);
render_implementor(cx, implementor, w, &implementor_dups)?;
}
write!(w, "</div>")?;
write_loading_content(w, "</div>")?;
}
} else {
// even without any implementations to write in, we still want the heading and list, so the
// implementors javascript file pulled in below has somewhere to write the impls into
write!(w, "{}", impl_header)?;
write!(w, "</div>")?;
write_small_section_header(w, "implementors", "Implementors",
"<div class='item-list' id='implementors-list'>")?;
write_loading_content(w, "</div>")?;

if t.auto {
write!(w, "{}", synthetic_impl_header)?;
write!(w, "</div>")?;
write_small_section_header(w, "synthetic-implementors", "Auto implementors",
"<div class='item-list' id='synthetic-implementors-list'>")?;
write_loading_content(w, "</div>")?;
}
}
write!(w, r#"<script type="text/javascript">window.inlined_types=new Set({});</script>"#,
Expand Down
Loading

0 comments on commit 7f04a64

Please sign in to comment.