From 4b806878549990d2ad2aa3c265751d3d89947cdf Mon Sep 17 00:00:00 2001 From: bors Date: Sat, 30 Jan 2021 01:02:18 +0000 Subject: rustdoc tweaking * Reuse memory * simplify `next_def_id`, avoid multiple hashing and unnecessary lookups * remove `all_fake_def_ids`, use the global map instead (probably not a good step toward parallelization, though...) * convert `add_deref_target` to iterative implementation * use `ArrayVec` where we know the max number of elements * minor touchups here and there * avoid building temporary vectors that get appended to other vectors At most places I may or may not be doing the compiler's job is this PR. --- src/librustdoc/html/render/mod.rs | 699 ++++++++++++++++++++------------------ 1 file changed, 361 insertions(+), 338 deletions(-) (limited to 'src/librustdoc/html/render') diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index df3701487d7..daaa2c719b0 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -89,7 +89,7 @@ crate type NameDoc = (String, Option); crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ { crate::html::format::display_fn(move |f| { - if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { write!(f, "{}", v) } + if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) } }) } @@ -359,28 +359,23 @@ crate struct StylePath { thread_local!(crate static CURRENT_DEPTH: Cell = Cell::new(0)); -crate fn initial_ids() -> Vec { - [ - "main", - "search", - "help", - "TOC", - "render-detail", - "associated-types", - "associated-const", - "required-methods", - "provided-methods", - "implementors", - "synthetic-implementors", - "implementors-list", - "synthetic-implementors-list", - "methods", - "implementations", - ] - .iter() - .map(|id| (String::from(*id))) - .collect() -} +crate const INITIAL_IDS: [&'static str; 15] = [ + "main", + "search", + "help", + "TOC", + "render-detail", + "associated-types", + "associated-const", + "required-methods", + "provided-methods", + "implementors", + "synthetic-implementors", + "implementors-list", + "synthetic-implementors-list", + "methods", + "implementations", +]; /// Generates the documentation for `crate` into the directory `dst` impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { @@ -908,12 +903,13 @@ themePicker.onblur = handleThemeButtonsBlur; let mut krates = Vec::new(); if path.exists() { + let prefix = format!(r#"{}["{}"]"#, key, krate); for line in BufReader::new(File::open(path)?).lines() { let line = line?; if !line.starts_with(key) { continue; } - if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) { + if line.starts_with(&prefix) { continue; } ret.push(line.to_string()); @@ -934,12 +930,13 @@ themePicker.onblur = handleThemeButtonsBlur; let mut krates = Vec::new(); if path.exists() { + let prefix = format!("\"{}\"", krate); for line in BufReader::new(File::open(path)?).lines() { let line = line?; if !line.starts_with('"') { continue; } - if line.starts_with(&format!("\"{}\"", krate)) { + if line.starts_with(&prefix) { continue; } if line.ends_with(",\\") { @@ -1020,8 +1017,7 @@ themePicker.onblur = handleThemeButtonsBlur; break; } else { let e = cur_elem.clone(); - h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); - h = h.children.get_mut(&cur_elem).expect("not found child"); + h = h.children.entry(cur_elem.clone()).or_insert_with(|| Hierarchy::new(e)); } } } @@ -1214,11 +1210,7 @@ fn write_minify( fn write_srclink(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { if let Some(l) = cx.src_href(item) { - write!( - buf, - "[src]", - l, "goto source code" - ) + write!(buf, "[src]", l) } } @@ -1325,25 +1317,23 @@ impl AllTypes { } } -fn print_entries(f: &mut Buffer, e: &FxHashSet, title: &str, class: &str) { - if !e.is_empty() { - let mut e: Vec<&ItemEntry> = e.iter().collect(); - e.sort(); - write!( - f, - "

{}

    {}
", - title, - Escape(title), - class, - e.iter().map(|s| format!("
  • {}
  • ", s.print())).collect::() - ); - } -} - impl AllTypes { fn print(self, f: &mut Buffer) { - write!( - f, + fn print_entries(f: &mut Buffer, e: &FxHashSet, title: &str, class: &str) { + if !e.is_empty() { + let mut e: Vec<&ItemEntry> = e.iter().collect(); + e.sort(); + write!(f, "

    {}

      ", title, title, class); + + for s in e.iter() { + write!(f, "
    • {}
    • ", s.print()); + } + + f.write_str("
    "); + } + } + + f.write_str( "

    \ List of all items\ \ @@ -1354,8 +1344,11 @@ impl AllTypes { \ -

    " + List of all items\ + ", ); + // Note: print_entries does not escape the title, because we know the current set of titles + // don't require escaping. print_entries(f, &self.structs, "Structs", "structs"); print_entries(f, &self.enums, "Enums", "enums"); print_entries(f, &self.unions, "Unions", "unions"); @@ -1581,7 +1574,7 @@ impl Context<'_> { { self.id_map.borrow_mut().reset(); - self.id_map.borrow_mut().populate(initial_ids()); + self.id_map.borrow_mut().populate(&INITIAL_IDS); } if !self.render_redirect_pages { @@ -1703,15 +1696,15 @@ fn wrap_into_docblock(w: &mut Buffer, f: F) where F: FnOnce(&mut Buffer), { - write!(w, "
    "); + w.write_str("
    "); f(w); - write!(w, "
    ") + w.write_str("
    ") } fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { debug_assert!(!item.is_stripped()); // Write the breadcrumb trail header for the top - write!(buf, "

    "); + buf.write_str("

    "); let name = match *item.kind { clean::ModuleItem(ref m) => { if m.is_crate { @@ -1759,8 +1752,8 @@ fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { } write!(buf, "{}", item.type_(), item.name.as_ref().unwrap()); - write!(buf, ""); // in-band - write!(buf, ""); + buf.write_str(""); // in-band + buf.write_str(""); render_stability_since_raw( buf, item.stable_since(cx.tcx()).as_deref(), @@ -1768,14 +1761,13 @@ fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { None, None, ); - write!( - buf, + buf.write_str( "\ \ []\ \ - " + ", ); // Write `src` tag @@ -1788,7 +1780,7 @@ fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer) { write_srclink(cx, item, buf); } - write!(buf, "

    "); // out-of-band + buf.write_str("

    "); // out-of-band match *item.kind { clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), @@ -1923,12 +1915,13 @@ fn document_full( debug!("Doc block: =====\n{}\n=====", s); render_markdown(w, cx, &*s, item.links(&cx.cache), prefix, is_hidden); } else if !prefix.is_empty() { - write!( - w, - "
    {}
    ", - if is_hidden { " hidden" } else { "" }, - prefix - ); + if is_hidden { + w.write_str("
    "); + } else { + w.write_str("
    "); + } + w.write_str(prefix); + w.write_str("
    "); } } @@ -1946,11 +1939,15 @@ fn document_item_info( ) { let item_infos = short_item_info(item, cx, parent); if !item_infos.is_empty() { - write!(w, "
    ", if is_hidden { " hidden" } else { "" }); + if is_hidden { + w.write_str("
    "); + } else { + w.write_str("
    "); + } for info in item_infos { - write!(w, "{}", info); + w.write_str(&info); } - write!(w, "
    "); + w.write_str("
    "); } } @@ -1973,36 +1970,32 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { }); if item.is_struct() { - write!( - w, + w.write_str( "Non-exhaustive structs could have additional fields added in future. \ Therefore, non-exhaustive structs cannot be constructed in external crates \ using the traditional Struct {{ .. }} syntax; cannot be \ matched against without a wildcard ..; and \ - struct update syntax will not work." + struct update syntax will not work.", ); } else if item.is_enum() { - write!( - w, + w.write_str( "Non-exhaustive enums could have additional variants added in future. \ Therefore, when matching against variants of non-exhaustive enums, an \ - extra wildcard arm must be added to account for any future variants." + extra wildcard arm must be added to account for any future variants.", ); } else if item.is_variant() { - write!( - w, + w.write_str( "Non-exhaustive enum variants could have additional fields added in future. \ Therefore, non-exhaustive enum variants cannot be constructed in external \ - crates and cannot be matched against." + crates and cannot be matched against.", ); } else { - write!( - w, - "This type will require a wildcard arm in any match statements or constructors." + w.write_str( + "This type will require a wildcard arm in any match statements or constructors.", ); } - write!(w, "
    "); + w.write_str("
    "); } } @@ -2139,7 +2132,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl curty = myty; } else if myty != curty { if curty.is_some() { - write!(w, ""); + w.write_str(""); } curty = myty; let (short, name) = item_ty_to_strs(&myty.unwrap()); @@ -2171,7 +2164,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl anchor(myitem.def_id, &*name.as_str(), cx.cache()) ), } - write!(w, ""); + w.write_str(""); } clean::ImportItem(ref import) => { @@ -2227,7 +2220,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl } if curty.is_some() { - write!(w, ""); + w.write_str(""); } } @@ -2389,7 +2382,7 @@ fn short_item_info( } fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { - write!(w, "
    ");
    +    w.write_str("
    ");
         render_attributes(w, it, false);
     
         write!(
    @@ -2403,7 +2396,7 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::
         if c.value.is_some() || c.is_literal {
             write!(w, " = {expr};", expr = Escape(&c.expr));
         } else {
    -        write!(w, ";");
    +        w.write_str(";");
         }
     
         if let Some(value) = &c.value {
    @@ -2419,12 +2412,12 @@ fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::
             }
         }
     
    -    write!(w, "
    "); + w.write_str("
    "); document(w, cx, it, None) } fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { - write!(w, "
    ");
    +    w.write_str("
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -2449,7 +2442,7 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::
             f.generics.print(cx.cache())
         )
         .len();
    -    write!(w, "
    ");
    +    w.write_str("
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -2573,7 +2566,7 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
     
         // Output the trait definition
         wrap_into_docblock(w, |w| {
    -        write!(w, "
    ");
    +        w.write_str("
    ");
             render_attributes(w, it, true);
             write!(
                 w,
    @@ -2590,34 +2583,34 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
                 let where_ = WhereClause { gens: &t.generics, indent: 0, end_newline: true };
                 write!(w, "{}", where_.print(cx.cache()));
             } else {
    -            write!(w, " ");
    +            w.write_str(" ");
             }
     
             if t.items.is_empty() {
    -            write!(w, "{{ }}");
    +            w.write_str("{ }");
             } else {
                 // FIXME: we should be using a derived_id for the Anchors here
    -            write!(w, "{{\n");
    +            w.write_str("{\n");
                 for t in &types {
                     render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
    -                write!(w, ";\n");
    +                w.write_str(";\n");
                 }
                 if !types.is_empty() && !consts.is_empty() {
                     w.write_str("\n");
                 }
                 for t in &consts {
                     render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait, cx);
    -                write!(w, ";\n");
    +                w.write_str(";\n");
                 }
                 if !consts.is_empty() && !required.is_empty() {
                     w.write_str("\n");
                 }
                 for (pos, m) in required.iter().enumerate() {
                     render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait, cx);
    -                write!(w, ";\n");
    +                w.write_str(";\n");
     
                     if pos < required.len() - 1 {
    -                    write!(w, "
    "); + w.write_str("
    "); } } if !required.is_empty() && !provided.is_empty() { @@ -2629,19 +2622,19 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra clean::MethodItem(ref inner, _) if !inner.generics.where_predicates.is_empty() => { - write!(w, ",\n {{ ... }}\n"); + w.write_str(",\n { ... }\n"); } _ => { - write!(w, " {{ ... }}\n"); + w.write_str(" { ... }\n"); } } if pos < provided.len() - 1 { - write!(w, "
    "); + w.write_str("
    "); } } - write!(w, "}}"); + w.write_str("}"); } - write!(w, "
    ") + w.write_str("
    ") }); // Trait documentation @@ -2668,10 +2661,10 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra let id = cx.derive_id(format!("{}.{}", item_type, name)); write!(w, "

    ", id = id,); render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx); - write!(w, ""); + w.write_str(""); render_stability_since(w, m, t, cx.tcx()); write_srclink(cx, m, w); - write!(w, "

    "); + w.write_str(""); document(w, cx, m, Some(t)); } @@ -3072,10 +3065,10 @@ fn render_assoc_item( fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { wrap_into_docblock(w, |w| { - write!(w, "
    ");
    +        w.write_str("
    ");
             render_attributes(w, it, true);
             render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true, cx);
    -        write!(w, "
    ") + w.write_str("
    ") }); document(w, cx, it, None); @@ -3122,10 +3115,10 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_into_docblock(w, |w| { - write!(w, "
    ");
    +        w.write_str("
    ");
             render_attributes(w, it, true);
             render_union(w, it, Some(&s.generics), &s.fields, "", true, cx);
    -        write!(w, "
    ") + w.write_str("
    ") }); document(w, cx, it, None); @@ -3168,7 +3161,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { wrap_into_docblock(w, |w| { - write!(w, "
    ");
    +        w.write_str("
    ");
             render_attributes(w, it, true);
             write!(
                 w,
    @@ -3179,11 +3172,11 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                 WhereClause { gens: &e.generics, indent: 0, end_newline: true }.print(cx.cache())
             );
             if e.variants.is_empty() && !e.variants_stripped {
    -            write!(w, " {{}}");
    +            w.write_str(" {}");
             } else {
    -            write!(w, " {{\n");
    +            w.write_str(" {\n");
                 for v in &e.variants {
    -                write!(w, "    ");
    +                w.write_str("    ");
                     let name = v.name.as_ref().unwrap();
                     match *v.kind {
                         clean::VariantItem(ref var) => match var {
    @@ -3192,11 +3185,11 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                                 write!(w, "{}(", name);
                                 for (i, ty) in tys.iter().enumerate() {
                                     if i > 0 {
    -                                    write!(w, ", ")
    +                                    w.write_str(", ")
                                     }
                                     write!(w, "{}", ty.print(cx.cache()));
                                 }
    -                            write!(w, ")");
    +                            w.write_str(")");
                             }
                             clean::Variant::Struct(ref s) => {
                                 render_struct(w, v, None, s.struct_type, &s.fields, "    ", false, cx);
    @@ -3204,15 +3197,15 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
                         },
                         _ => unreachable!(),
                     }
    -                write!(w, ",\n");
    +                w.write_str(",\n");
                 }
     
                 if e.variants_stripped {
    -                write!(w, "    // some variants omitted\n");
    +                w.write_str("    // some variants omitted\n");
                 }
    -            write!(w, "}}");
    +            w.write_str("}");
             }
    -        write!(w, "
    ") + w.write_str("
    ") }); document(w, cx, it, None); @@ -3236,16 +3229,16 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum name = variant.name.as_ref().unwrap() ); if let clean::VariantItem(clean::Variant::Tuple(ref tys)) = *variant.kind { - write!(w, "("); + w.write_str("("); for (i, ty) in tys.iter().enumerate() { if i > 0 { - write!(w, ", "); + w.write_str(", "); } write!(w, "{}", ty.print(cx.cache())); } - write!(w, ")"); + w.write_str(")"); } - write!(w, ""); + w.write_str(""); document(w, cx, variant, Some(it)); document_non_exhaustive(w, variant); @@ -3283,7 +3276,7 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum document(w, cx, field, Some(variant)); } } - write!(w, ""); + w.write_str(""); } render_stability_since(w, variant, it, cx.tcx()); } @@ -3363,7 +3356,7 @@ fn render_struct( ) } let mut has_visible_fields = false; - write!(w, " {{"); + w.write_str(" {"); for field in fields { if let clean::StructFieldItem(ref ty) = *field.kind { write!( @@ -3388,13 +3381,13 @@ fn render_struct( // `{ /* fields omitted */ }` to save space. write!(w, " /* fields omitted */ "); } - write!(w, "}}"); + w.write_str("}"); } CtorKind::Fn => { - write!(w, "("); + w.write_str("("); for (i, field) in fields.iter().enumerate() { if i > 0 { - write!(w, ", "); + w.write_str(", "); } match *field.kind { clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), @@ -3409,7 +3402,7 @@ fn render_struct( _ => unreachable!(), } } - write!(w, ")"); + w.write_str(")"); if let Some(g) = g { write!( w, @@ -3417,7 +3410,7 @@ fn render_struct( WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache()) ) } - write!(w, ";"); + w.write_str(";"); } CtorKind::Const => { // Needed for PhantomData. @@ -3428,7 +3421,7 @@ fn render_struct( WhereClause { gens: g, indent: 0, end_newline: false }.print(cx.cache()) ) } - write!(w, ";"); + w.write_str(";"); } } } @@ -3471,7 +3464,7 @@ fn render_union( if it.has_stripped_fields().unwrap() { write!(w, " // some fields omitted\n{}", tab); } - write!(w, "}}"); + w.write_str("}"); } #[derive(Copy, Clone)] @@ -3505,16 +3498,15 @@ fn render_assoc_items( if !non_trait.is_empty() { let render_mode = match what { AssocItemRender::All => { - write!( - w, + w.write_str( "

    \ Implementations\ -

    " + ", ); RenderMode::Normal } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - let id = cx.derive_id(small_url_encode(&format!( + let id = cx.derive_id(small_url_encode(format!( "deref-methods-{:#}", type_.print(cx.cache()) ))); @@ -3589,29 +3581,27 @@ fn render_assoc_items( } if !synthetic.is_empty() { - write!( - w, + w.write_str( "

    \ Auto Trait Implementations\ \

    \ -
    " +
    ", ); render_impls(cx, w, &synthetic, containing_item); - write!(w, "
    "); + w.write_str("
    "); } if !blanket_impl.is_empty() { - write!( - w, + w.write_str( "

    \ Blanket Implementations\ \

    \ -
    " +
    ", ); render_impls(cx, w, &blanket_impl, containing_item); - write!(w, "
    "); + w.write_str("
    "); } } } @@ -3693,19 +3683,21 @@ fn spotlight_decl(decl: &clean::FnDecl, cache: &Cache) -> String { if impl_.trait_.def_id_full(cache).map_or(false, |d| cache.traits[&d].is_spotlight) { if out.is_empty() { - out.push_str(&format!( + write!( + &mut out, "

    Notable traits for {}

    \ ", impl_.for_.print(cache) - )); + ); trait_.push_str(&impl_.for_.print(cache).to_string()); } //use the "where" class here to make it small - out.push_str(&format!( + write!( + &mut out, "{}", impl_.print(cache) - )); + ); let t_did = impl_.trait_.def_id_full(cache).unwrap(); for it in &impl_.items { if let clean::TypedefItem(ref tydef, _) = *it.kind { @@ -3765,7 +3757,7 @@ fn render_impl( if is_on_foreign_type { get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t, cx.cache()) } else { - format!("impl-{}", small_url_encode(&format!("{:#}", t.print(cx.cache())))) + format!("impl-{}", small_url_encode(format!("{:#}", t.print(cx.cache())))) } } None => "impl".to_string(), @@ -3781,7 +3773,7 @@ fn render_impl( if show_def_docs { for it in &i.inner_impl().items { if let clean::TypedefItem(ref tydef, _) = *it.kind { - write!(w, " "); + w.write_str(" "); assoc_type( w, it, @@ -3791,11 +3783,11 @@ fn render_impl( "", cx.cache(), ); - write!(w, ";"); + w.write_str(";"); } } } - write!(w, ""); + w.write_str(""); } else { write!( w, @@ -3814,7 +3806,7 @@ fn render_impl( outer_const_version, ); write_srclink(cx, &i.impl_item, w); - write!(w, ""); + w.write_str(""); if trait_.is_some() { if let Some(portability) = portability(&i.impl_item, Some(parent)) { @@ -3877,9 +3869,9 @@ fn render_impl( if render_method_item { let id = cx.derive_id(format!("{}.{}", item_type, name)); write!(w, "

    ", id, item_type, extra_class); - write!(w, ""); + w.write_str(""); render_assoc_item(w, item, link.anchor(&id), ItemType::Impl, cx); - write!(w, ""); + w.write_str(""); render_stability_since_raw( w, item.stable_since(cx.tcx()).as_deref(), @@ -3888,7 +3880,7 @@ fn render_impl( outer_const_version, ); write_srclink(cx, item, w); - write!(w, "

    "); + w.write_str(""); } } clean::TypedefItem(ref tydef, _) => { @@ -3903,13 +3895,13 @@ fn render_impl( "", cx.cache(), ); - write!(w, ""); + w.write_str(""); } clean::AssocConstItem(ref ty, ref default) => { let id = cx.derive_id(format!("{}.{}", item_type, name)); write!(w, "

    ", id, item_type, extra_class); assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), "", cx); - write!(w, ""); + w.write_str(""); render_stability_since_raw( w, item.stable_since(cx.tcx()).as_deref(), @@ -3918,13 +3910,13 @@ fn render_impl( outer_const_version, ); write_srclink(cx, item, w); - write!(w, "

    "); + w.write_str(""); } clean::AssocTypeItem(ref bounds, ref default) => { let id = cx.derive_id(format!("{}.{}", item_type, name)); write!(w, "

    ", id, item_type, extra_class); assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id), "", cx.cache()); - write!(w, "

    "); + w.write_str(""); } clean::StrippedItem(..) => return, _ => panic!("can't make docs for trait item with name {:?}", item.name), @@ -3968,7 +3960,7 @@ fn render_impl( } } - write!(w, "
    "); + w.write_str("
    "); for trait_item in &i.inner_impl().items { doc_impl_item( w, @@ -4039,11 +4031,11 @@ fn render_impl( ); } } - write!(w, "
    "); + w.write_str("
    "); } fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { - write!(w, "
    ");
    +    w.write_str("
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -4065,7 +4057,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean:
     }
     
     fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
    -    write!(w, "
    ");
    +    w.write_str("
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -4086,7 +4078,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clea
     }
     
     fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
    -    write!(w, "
    ");
    +    w.write_str("
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -4108,7 +4100,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::T
     }
     
     fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) {
    -    writeln!(w, "
    extern {{");
    +    w.write_str("
    extern {\n");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -4168,7 +4160,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
             }
         }
     
    -    write!(buffer, "
    "); + buffer.write_str("
    "); if it.is_crate() { write!( buffer, @@ -4196,10 +4188,10 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { // as much HTML as possible in order to allow non-JS-enabled browsers // to navigate the documentation (though slightly inefficiently). - write!(buffer, "

    "); + buffer.write_str("

    "); for (i, name) in cx.current.iter().take(parentlen).enumerate() { if i > 0 { - write!(buffer, "::"); + buffer.write_str("::"); } write!( buffer, @@ -4208,7 +4200,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { *name ); } - write!(buffer, "

    "); + buffer.write_str("

    "); // Sidebar refers to the enclosing module, not this module. let relpath = if it.is_mod() { "../" } else { "" }; @@ -4227,7 +4219,7 @@ fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) { write!(buffer, "", path = relpath); } // Closes sidebar-elems div. - write!(buffer, "
    "); + buffer.write_str("
    "); } fn get_next_url(used_links: &mut FxHashSet, url: String) -> String { @@ -4268,23 +4260,42 @@ fn get_methods( } // The point is to url encode any potential character from a type with genericity. -fn small_url_encode(s: &str) -> String { - s.replace("<", "%3C") - .replace(">", "%3E") - .replace(" ", "%20") - .replace("?", "%3F") - .replace("'", "%27") - .replace("&", "%26") - .replace(",", "%2C") - .replace(":", "%3A") - .replace(";", "%3B") - .replace("[", "%5B") - .replace("]", "%5D") - .replace("\"", "%22") +fn small_url_encode(s: String) -> String { + let mut st = String::new(); + let mut last_match = 0; + for (idx, c) in s.char_indices() { + let escaped = match c { + '<' => "%3C", + '>' => "%3E", + ' ' => "%20", + '?' => "%3F", + '\'' => "%27", + '&' => "%26", + ',' => "%2C", + ':' => "%3A", + ';' => "%3B", + '[' => "%5B", + ']' => "%5D", + '"' => "%22", + _ => continue, + }; + + st += &s[last_match..idx]; + st += escaped; + // NOTE: we only expect single byte characters here - which is fine as long as we + // only match single byte characters + last_match = idx + 1; + } + + if last_match != 0 { + st += &s[last_match..]; + st + } else { + s + } } -fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { - let mut out = String::new(); +fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) { if let Some(v) = cx.cache.impls.get(&it.def_id) { let mut used_links = FxHashSet::default(); @@ -4300,11 +4311,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { if !ret.is_empty() { // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); - out.push_str(&format!( + + out.push_str( "Methods\ -
    {}
    ", - ret.join("") - )); +
    ", + ); + for line in ret { + out.push_str(&line); + } + out.push_str("
    "); } } @@ -4314,7 +4329,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { .filter(|i| i.inner_impl().trait_.is_some()) .find(|i| i.inner_impl().trait_.def_id_full(cx.cache()) == cx.cache.deref_trait_did) { - out.push_str(&sidebar_deref_methods(cx, impl_, v)); + sidebar_deref_methods(cx, out, impl_, v); } let format_impls = |impls: Vec<&Impl>| { let mut links = FxHashSet::default(); @@ -4325,7 +4340,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { if let Some(ref i) = it.inner_impl().trait_ { let i_display = format!("{:#}", i.print(cx.cache())); let out = Escape(&i_display); - let encoded = small_url_encode(&format!("{:#}", i.print(cx.cache()))); + let encoded = small_url_encode(format!("{:#}", i.print(cx.cache()))); let generated = format!( "{}{}", encoded, @@ -4339,7 +4354,15 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { }) .collect::>(); ret.sort(); - ret.join("") + ret + }; + + let write_sidebar_links = |out: &mut Buffer, links: Vec| { + out.push_str("
    "); + for link in links { + out.push_str(&link); + } + out.push_str("
    "); }; let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = @@ -4357,7 +4380,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { "\ Trait Implementations", ); - out.push_str(&format!("
    {}
    ", concrete_format)); + write_sidebar_links(out, concrete_format); } if !synthetic_format.is_empty() { @@ -4365,7 +4388,7 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { "\ Auto Trait Implementations", ); - out.push_str(&format!("
    {}
    ", synthetic_format)); + write_sidebar_links(out, synthetic_format); } if !blanket_format.is_empty() { @@ -4373,16 +4396,13 @@ fn sidebar_assoc_items(cx: &Context<'_>, it: &clean::Item) -> String { "\ Blanket Implementations", ); - out.push_str(&format!("
    {}
    ", blanket_format)); + write_sidebar_links(out, blanket_format); } } } - - out } -fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec) -> String { - let mut out = String::new(); +fn sidebar_deref_methods(cx: &Context<'_>, out: &mut Buffer, impl_: &Impl, v: &Vec) { let c = cx.cache(); debug!("found Deref: {:?}", impl_); @@ -4419,15 +4439,20 @@ fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec) -> Strin let id = deref_id_map .get(&real_target.def_id_full(cx.cache()).unwrap()) .expect("Deref section without derived id"); - out.push_str(&format!( + write!( + out, "Methods from {}<Target={}>", id, Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(c))), Escape(&format!("{:#}", real_target.print(c))), - )); + ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); - out.push_str(&format!("
    {}
    ", ret.join(""))); + out.push_str("
    "); + for link in ret { + out.push_str(&link); + } + out.push_str("
    "); } } @@ -4443,36 +4468,39 @@ fn sidebar_deref_methods(cx: &Context<'_>, impl_: &Impl, v: &Vec) -> Strin // `impl Deref for S` if target_did == type_did { // Avoid infinite cycles - return out; + return; } } - out.push_str(&sidebar_deref_methods(cx, target_deref_impl, target_impls)); + sidebar_deref_methods(cx, out, target_deref_impl, target_impls); } } } } - - out } fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { - let mut sidebar = String::new(); + let mut sidebar = Buffer::new(); let fields = get_struct_fields_name(&s.fields); if !fields.is_empty() { if let CtorKind::Fictive = s.struct_type { - sidebar.push_str(&format!( + sidebar.push_str( "Fields\ -
    {}
    ", - fields - )); +
    ", + ); + + for field in fields { + sidebar.push_str(&field); + } + + sidebar.push_str("
    "); } } - sidebar.push_str(&sidebar_assoc_items(cx, it)); + sidebar_assoc_items(cx, &mut sidebar, it); if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); + write!(buf, "
    {}
    ", sidebar.into_inner()); } } @@ -4481,7 +4509,7 @@ fn get_id_for_impl_on_foreign_type( trait_: &clean::Type, cache: &Cache, ) -> String { - small_url_encode(&format!("impl-{:#}-for-{:#}", trait_.print(cache), for_.print(cache))) + small_url_encode(format!("impl-{:#}-for-{:#}", trait_.print(cache), for_.print(cache))) } fn extract_for_impl_name(item: &clean::Item, cache: &Cache) -> Option<(String, String)> { @@ -4501,82 +4529,74 @@ fn extract_for_impl_name(item: &clean::Item, cache: &Cache) -> Option<(String, S } fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { - let mut sidebar = String::new(); + buf.write_str("
    "); + + fn print_sidebar_section( + out: &mut Buffer, + items: &[clean::Item], + before: &str, + filter: impl Fn(&clean::Item) -> bool, + write: impl Fn(&mut Buffer, &Symbol), + after: &str, + ) { + let mut items = items + .iter() + .filter_map(|m| match m.name { + Some(ref name) if filter(m) => Some(name), + _ => None, + }) + .collect::>(); - let mut types = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_associated_type() => { - Some(format!("{name}", name = name)) - } - _ => None, - }) - .collect::>(); - let mut consts = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_associated_const() => { - Some(format!("{name}", name = name)) - } - _ => None, - }) - .collect::>(); - let mut required = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_ty_method() => { - Some(format!("{name}", name = name)) + if !items.is_empty() { + items.sort(); + out.push_str(before); + for item in items.into_iter() { + write(out, item); } - _ => None, - }) - .collect::>(); - let mut provided = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_method() => { - Some(format!("{0}", name)) - } - _ => None, - }) - .collect::>(); - - if !types.is_empty() { - types.sort(); - sidebar.push_str(&format!( - "\ - Associated Types
    {}
    ", - types.join("") - )); - } - if !consts.is_empty() { - consts.sort(); - sidebar.push_str(&format!( - "\ - Associated Constants
    {}
    ", - consts.join("") - )); - } - if !required.is_empty() { - required.sort(); - sidebar.push_str(&format!( - "\ - Required Methods
    {}
    ", - required.join("") - )); - } - if !provided.is_empty() { - provided.sort(); - sidebar.push_str(&format!( - "\ - Provided Methods
    {}
    ", - provided.join("") - )); + out.push_str(after); + } } + print_sidebar_section( + buf, + &t.items, + "\ + Associated Types
    ", + |m| m.is_associated_type(), + |out, sym| write!(out, "{0}", sym), + "
    ", + ); + + print_sidebar_section( + buf, + &t.items, + "\ + Associated Constants
    ", + |m| m.is_associated_const(), + |out, sym| write!(out, "{0}", sym), + "
    ", + ); + + print_sidebar_section( + buf, + &t.items, + "\ + Required Methods
    ", + |m| m.is_ty_method(), + |out, sym| write!(out, "{0}", sym), + "
    ", + ); + + print_sidebar_section( + buf, + &t.items, + "\ + Provided Methods
    ", + |m| m.is_method(), + |out, sym| write!(out, "{0}", sym), + "
    ", + ); + if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { let mut res = implementors .iter() @@ -4591,83 +4611,87 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean if !res.is_empty() { res.sort(); - sidebar.push_str(&format!( + buf.push_str( "\ Implementations on Foreign Types\ -
    {}
    ", - res.into_iter() - .map(|(name, id)| format!("{}", id, Escape(&name))) - .collect::>() - .join("") - )); +
    ", + ); + for (name, id) in res.into_iter() { + write!(buf, "{}", id, Escape(&name)); + } + buf.push_str("
    "); } } - sidebar.push_str(&sidebar_assoc_items(cx, it)); + sidebar_assoc_items(cx, buf, it); - sidebar.push_str("Implementors"); + buf.push_str("Implementors"); if t.is_auto { - sidebar.push_str( + buf.push_str( "Auto Implementors", ); } - write!(buf, "
    {}
    ", sidebar) + buf.push_str("
    ") } fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(cx, it); + let mut sidebar = Buffer::new(); + sidebar_assoc_items(cx, &mut sidebar, it); if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); + write!(buf, "
    {}
    ", sidebar.into_inner()); } } fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(cx, it); + let mut sidebar = Buffer::new(); + sidebar_assoc_items(cx, &mut sidebar, it); if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); + write!(buf, "
    {}
    ", sidebar.into_inner()); } } -fn get_struct_fields_name(fields: &[clean::Item]) -> String { +fn get_struct_fields_name(fields: &[clean::Item]) -> Vec { let mut fields = fields .iter() .filter(|f| matches!(*f.kind, clean::StructFieldItem(..))) - .filter_map(|f| match f.name { - Some(ref name) => { - Some(format!("{name}", name = name)) - } - _ => None, + .filter_map(|f| { + f.name.map(|name| format!("{name}", name = name)) }) .collect::>(); fields.sort(); - fields.join("") + fields } fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { - let mut sidebar = String::new(); + let mut sidebar = Buffer::new(); let fields = get_struct_fields_name(&u.fields); if !fields.is_empty() { - sidebar.push_str(&format!( + sidebar.push_str( "Fields\ -
    {}
    ", - fields - )); +
    ", + ); + + for field in fields { + sidebar.push_str(&field); + } + + sidebar.push_str("
    "); } - sidebar.push_str(&sidebar_assoc_items(cx, it)); + sidebar_assoc_items(cx, &mut sidebar, it); if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); + write!(buf, "
    {}
    ", sidebar.into_inner()); } } fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { - let mut sidebar = String::new(); + let mut sidebar = Buffer::new(); let mut variants = e .variants @@ -4686,10 +4710,10 @@ fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean: )); } - sidebar.push_str(&sidebar_assoc_items(cx, it)); + sidebar_assoc_items(cx, &mut sidebar, it); if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); + write!(buf, "
    {}
    ", sidebar.into_inner()); } } @@ -4729,11 +4753,7 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { if items.iter().any(|it| { it.type_() == ItemType::ExternCrate || (it.type_() == ItemType::Import && !it.is_stripped()) }) { - sidebar.push_str(&format!( - "
  • {name}
  • ", - id = "reexports", - name = "Re-exports" - )); + sidebar.push_str("
  • Re-exports
  • "); } // ordering taken from item_module, reorder, where it prioritized elements in a certain order @@ -4776,21 +4796,24 @@ fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { } fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(cx, it); + let mut sidebar = Buffer::new(); + sidebar_assoc_items(cx, &mut sidebar, it); + if !sidebar.is_empty() { - write!(buf, "
    {}
    ", sidebar); + write!(buf, "
    {}
    ", sidebar.into_inner()); } } fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) { wrap_into_docblock(w, |w| { - w.write_str(&highlight::render_with_highlighting( - t.source.clone(), + highlight::render_with_highlighting( + &t.source, + w, Some("macro"), None, None, it.source.span().edition(), - )) + ); }); document(w, cx, it, None) } @@ -4799,27 +4822,27 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean let name = it.name.as_ref().expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(w, "
    ");
    +            w.push_str("
    ");
                 write!(w, "{}!() {{ /* proc-macro */ }}", name);
    -            write!(w, "
    "); + w.push_str("
    "); } MacroKind::Attr => { - write!(w, "
    ");
    +            w.push_str("
    ");
                 write!(w, "#[{}]", name);
    -            write!(w, "
    "); + w.push_str("
    "); } MacroKind::Derive => { - write!(w, "
    ");
    +            w.push_str("
    ");
                 write!(w, "#[derive({})]", name);
                 if !m.helpers.is_empty() {
    -                writeln!(w, "\n{{");
    -                writeln!(w, "    // Attributes available to this derive:");
    +                w.push_str("\n{\n");
    +                w.push_str("    // Attributes available to this derive:\n");
                     for attr in &m.helpers {
                         writeln!(w, "    #[{}]", attr);
                     }
    -                write!(w, "}}");
    +                w.push_str("}\n");
                 }
    -            write!(w, "
    "); + w.push_str("
    "); } } document(w, cx, it, None) -- cgit 1.4.1-3-g733a5