]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #56005 - GuillaumeGomez:speedup-doc-render, r=QuietMisdreavus
authorbors <bors@rust-lang.org>
Sat, 15 Dec 2018 06:42:27 +0000 (06:42 +0000)
committerbors <bors@rust-lang.org>
Sat, 15 Dec 2018 06:42:27 +0000 (06:42 +0000)
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

1  2 
src/librustdoc/html/layout.rs
src/librustdoc/html/render.rs
src/librustdoc/html/static/rustdoc.css

index 6ce02c313ee050365ac36fa0703c681046287566,3e69db3f899af832fbc0679aafee52da86b23760..37ff693bdf16794e4d16c71cd49a98171f848b5d
@@@ -54,6 -54,7 +54,7 @@@ pub fn render<T: fmt::Display, S: fmt::
      <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}\
              <div class=\"infos\">\
                  <h2>Search Tricks</h2>\
                  <p>\
 -                    Prefix searches with a type followed by a colon (e.g. \
 +                    Prefix searches with a type followed by a colon (e.g., \
                      <code>fn:</code>) to restrict the search to a given type.\
                  </p>\
                  <p>\
                      and <code>const</code>.\
                  </p>\
                  <p>\
 -                    Search functions by type signature (e.g. \
 +                    Search functions by type signature (e.g., \
                      <code>vec -> usize</code> or <code>* -> vec</code>)\
                  </p>\
                  <p>\
 -                    Search multiple things at once by splitting your query with comma (e.g. \
 +                    Search multiple things at once by splitting your query with comma (e.g., \
                      <code>str,u8</code> or <code>String,struct:Vec,test</code>)\
                  </p>\
              </div>\
index 4b10c23fd3f3f219bbed58a7e673b10486adb808,b84fa7d581f489762b904844f970c94bf6692998..c9f3aa011a1d4987dec7b0602b0c7310f6dbf246
@@@ -698,7 -698,7 +698,7 @@@ fn build_index(krate: &clean::Crate, ca
                  ty: item.type_(),
                  name: item.name.clone().unwrap(),
                  path: fqp[..fqp.len() - 1].join("::"),
 -                desc: plain_summary_line(item.doc_value()),
 +                desc: plain_summary_line_short(item.doc_value()),
                  parent: Some(did),
                  parent_idx: None,
                  search_type: get_index_search_type(&item),
      }
  
      let crate_doc = krate.module.as_ref().map(|module| {
 -        plain_summary_line(module.doc_value())
 +        plain_summary_line_short(module.doc_value())
      }).unwrap_or(String::new());
  
      let mut crate_data = BTreeMap::new();
@@@ -772,13 -772,19 +772,16 @@@ 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.
      let mut themes: FxHashSet<String> = FxHashSet::default();
  
      for entry in &cx.shared.themes {
 -        let mut content = Vec::with_capacity(100000);
 -
 -        let mut f = try_err!(File::open(&entry), &entry);
 -        try_err!(f.read_to_end(&mut content), &entry);
 +        let content = try_err!(fs::read(&entry), &entry);
          let theme = try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry);
          let extension = try_none!(try_none!(entry.extension(), &entry).to_str(), &entry);
          write(cx.dst.join(format!("{}{}.{}", theme, cx.shared.resource_suffix, extension)),
@@@ -865,9 -871,8 +868,8 @@@ themePicker.onblur = handleThemeButtons
      }
  
      {
-         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)?;
          if !options.enable_minification {
              try_err!(fs::copy(css, out), css);
          } else {
 -            let mut f = try_err!(File::open(css), css);
 -            let mut buffer = String::with_capacity(1000);
 -
 -            try_err!(f.read_to_string(&mut buffer), css);
 +            let buffer = try_err!(fs::read_to_string(css), css);
              write_minify(out, &buffer, options.enable_minification)?;
          }
      }
@@@ -1481,7 -1489,7 +1483,7 @@@ impl DocFolder for Cache 
                              ty: item.type_(),
                              name: s.to_string(),
                              path: path.join("::"),
 -                            desc: plain_summary_line(item.doc_value()),
 +                            desc: plain_summary_line_short(item.doc_value()),
                              parent,
                              parent_idx: None,
                              search_type: get_index_search_type(&item),
              clean::FunctionItem(..) | clean::ModuleItem(..) |
              clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
              clean::ConstantItem(..) | clean::StaticItem(..) |
 -            clean::UnionItem(..) | clean::ForeignTypeItem | clean::MacroItem(..)
 +            clean::UnionItem(..) | clean::ForeignTypeItem |
 +            clean::MacroItem(..) | clean::ProcMacroItem(..)
              if !self.stripped_mod => {
                  // Re-exported items mean that the same id can show up twice
                  // in the rustdoc ast that we're looking at. We know,
@@@ -1674,7 -1681,7 +1676,7 @@@ impl<'a> Cache 
                                  ty: item.type_(),
                                  name: item_name.to_string(),
                                  path: path.clone(),
 -                                desc: plain_summary_line(item.doc_value()),
 +                                desc: plain_summary_line_short(item.doc_value()),
                                  parent: None,
                                  parent_idx: None,
                                  search_type: get_index_search_type(&item),
@@@ -2072,7 -2079,7 +2074,7 @@@ impl Context 
      fn item<F>(&mut self, item: clean::Item, all: &mut AllTypes, mut f: F) -> Result<(), Error>
          where F: FnMut(&mut Context, clean::Item),
      {
 -        // Stripped modules survive the rustdoc passes (i.e. `strip-private`)
 +        // Stripped modules survive the rustdoc passes (i.e., `strip-private`)
          // if they contain impls for public types. These modules can also
          // contain items such as publicly re-exported structures.
          //
                  if !buf.is_empty() {
                      try_err!(this.shared.ensure_dir(&this.dst), &this.dst);
                      let joint_dst = this.dst.join("index.html");
 -                    let mut dst = try_err!(File::create(&joint_dst), &joint_dst);
 -                    try_err!(dst.write_all(&buf), &joint_dst);
 +                    try_err!(fs::write(&joint_dst, buf), &joint_dst);
                  }
  
                  let m = match item.inner {
                  let file_name = &item_path(item_type, name);
                  try_err!(self.shared.ensure_dir(&self.dst), &self.dst);
                  let joint_dst = self.dst.join(file_name);
 -                let mut dst = try_err!(File::create(&joint_dst), &joint_dst);
 -                try_err!(dst.write_all(&buf), &joint_dst);
 +                try_err!(fs::write(&joint_dst, buf), &joint_dst);
  
                  if !self.render_redirect_pages {
                      all.append(full_path(self, &item), &item_type);
@@@ -2389,13 -2398,7 +2391,13 @@@ fn shorter<'a>(s: Option<&'a str>) -> S
  #[inline]
  fn plain_summary_line(s: Option<&str>) -> String {
      let line = shorter(s).replace("\n", " ");
 -    markdown::plain_summary_line(&line[..])
 +    markdown::plain_summary_line_full(&line[..], false)
 +}
 +
 +#[inline]
 +fn plain_summary_line_short(s: Option<&str>) -> String {
 +    let line = shorter(s).replace("\n", " ");
 +    markdown::plain_summary_line_full(&line[..], true)
  }
  
  fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result {
@@@ -3013,6 -3016,22 +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();
      }
  
      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();
  
          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(
                              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>"#,
@@@ -4797,7 -4788,7 +4787,7 @@@ impl<'a> fmt::Display for Source<'a> 
              tmp /= 10;
          }
          write!(fmt, "<pre class=\"line-numbers\">")?;
 -        for i in 1..lines + 1 {
 +        for i in 1..=lines {
              write!(fmt, "<span id=\"{0}\">{0:1$}</span>\n", i, cols)?;
          }
          write!(fmt, "</pre>")?;
index cd5a8a739d16dfac78d3288c9dccb18f28e15bf9,3e2e6d6eb4caa9e1ed189603b05903bf0e880204..d1336b1e8eb0b7aa510bd30df42795ef893dcd1d
@@@ -368,6 -368,10 +368,10 @@@ body:not(.source) .example-wrap > pre 
  #main > .docblock h2 { font-size: 1.15em; }
  #main > .docblock h3, #main > .docblock h4, #main > .docblock h5 { font-size: 1em; }
  
+ #main > h2 + div, #main > h2 + h3, #main > h3 + div {
+       display: none;
+ }
  .docblock h1 { font-size: 1em; }
  .docblock h2 { font-size: 0.95em; }
  .docblock h3, .docblock h4, .docblock h5 { font-size: 0.9em; }
@@@ -641,7 -645,7 +645,7 @@@ a 
        text-overflow: "";
        background-repeat: no-repeat;
        background-color: transparent;
 -      background-size: 16%;
 +      background-size: 20px;
        background-position: calc(100% - 1px) 56%;
  }
  .search-container > .top-button {