diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index ed1eb66b97c36..63cd0e04a288a 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -17,10 +17,11 @@ use super::print_item::{full_path, item_path, print_item};
use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
- collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
- LinkFromSrc, StylePath,
+ collect_spans_and_sources, scrape_examples_help,
+ sidebar::print_sidebar,
+ sidebar::{sidebar_module_like, Sidebar},
+ AllTypes, LinkFromSrc, StylePath,
};
-
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
@@ -35,6 +36,7 @@ use crate::html::url_parts_builder::UrlPartsBuilder;
use crate::html::{layout, sources, static_files};
use crate::scrape_examples::AllCallLocations;
use crate::try_err;
+use askama::Template;
/// Major driving force in all rustdoc rendering. This contains information
/// about where in the tree-like hierarchy rendering is occurring and controls
@@ -600,15 +602,18 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
};
let all = shared.all.replace(AllTypes::new());
let mut sidebar = Buffer::html();
- write!(sidebar, "
", crate_name);
-
- let mut items = Buffer::html();
- sidebar_module_like(&mut items, all.item_sections());
- if !items.is_empty() {
- sidebar.push_str("");
- }
+
+ let blocks = sidebar_module_like(all.item_sections());
+ let bar = Sidebar {
+ title_prefix: "Crate ",
+ title: crate_name.as_str(),
+ is_crate: false,
+ version: "",
+ blocks: vec![blocks],
+ path: String::new(),
+ };
+
+ bar.render_into(&mut sidebar).unwrap();
let v = layout::render(
&shared.layout,
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e7219867aac81..da1f1cf5eccfc 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -30,6 +30,7 @@ mod tests;
mod context;
mod print_item;
+mod sidebar;
mod span_map;
mod write_shared;
@@ -50,11 +51,9 @@ use askama::Template;
use rustc_ast_pretty::pprust;
use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def::CtorKind;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
use rustc_middle::middle::stability;
-use rustc_middle::ty;
use rustc_middle::ty::TyCtxt;
use rustc_span::{
symbol::{sym, Symbol},
@@ -1881,154 +1880,6 @@ pub(crate) fn render_impl_summary(
w.write_str("");
}
-fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
- if it.is_struct()
- || it.is_trait()
- || it.is_primitive()
- || it.is_union()
- || it.is_enum()
- || it.is_mod()
- || it.is_typedef()
- {
- write!(
- buffer,
- "",
- match *it.kind {
- clean::ModuleItem(..) =>
- if it.is_crate() {
- "Crate "
- } else {
- "Module "
- },
- _ => "",
- },
- it.name.as_ref().unwrap()
- );
- }
-
- buffer.write_str("");
-}
-
-fn get_next_url(used_links: &mut FxHashSet, url: String) -> String {
- if used_links.insert(url.clone()) {
- return url;
- }
- let mut add = 1;
- while !used_links.insert(format!("{}-{}", url, add)) {
- add += 1;
- }
- format!("{}-{}", url, add)
-}
-
-struct SidebarLink {
- name: Symbol,
- url: String,
-}
-
-impl fmt::Display for SidebarLink {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{} ", self.url, self.name)
- }
-}
-
-impl PartialEq for SidebarLink {
- fn eq(&self, other: &Self) -> bool {
- self.url == other.url
- }
-}
-
-impl Eq for SidebarLink {}
-
-impl PartialOrd for SidebarLink {
- fn partial_cmp(&self, other: &Self) -> Option {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for SidebarLink {
- fn cmp(&self, other: &Self) -> std::cmp::Ordering {
- self.url.cmp(&other.url)
- }
-}
-
-fn get_methods(
- i: &clean::Impl,
- for_deref: bool,
- used_links: &mut FxHashSet,
- deref_mut: bool,
- tcx: TyCtxt<'_>,
-) -> Vec {
- i.items
- .iter()
- .filter_map(|item| match item.name {
- Some(name) if !name.is_empty() && item.is_method() => {
- if !for_deref || should_render_item(item, deref_mut, tcx) {
- Some(SidebarLink {
- name,
- url: get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
- })
- } else {
- None
- }
- }
- _ => None,
- })
- .collect::>()
-}
-
-fn get_associated_constants(
- i: &clean::Impl,
- used_links: &mut FxHashSet,
-) -> Vec {
- i.items
- .iter()
- .filter_map(|item| match item.name {
- Some(name) if !name.is_empty() && item.is_associated_const() => Some(SidebarLink {
- name,
- url: get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
- }),
- _ => None,
- })
- .collect::>()
-}
-
pub(crate) fn small_url_encode(s: String) -> String {
// These characters don't need to be escaped in a URI.
// See https://url.spec.whatwg.org/#query-percent-encode-set
@@ -2094,232 +1945,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
}
}
-pub(crate) fn sidebar_render_assoc_items(
- cx: &Context<'_>,
- out: &mut Buffer,
- id_map: &mut IdMap,
- concrete: Vec<&Impl>,
- synthetic: Vec<&Impl>,
- blanket_impl: Vec<&Impl>,
-) {
- let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
- let mut links = FxHashSet::default();
-
- let mut ret = impls
- .iter()
- .filter_map(|it| {
- let trait_ = it.inner_impl().trait_.as_ref()?;
- let encoded =
- id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
- let i_display = format!("{:#}", trait_.print(cx));
- let out = Escape(&i_display);
- let prefix = match it.inner_impl().polarity {
- ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
- ty::ImplPolarity::Negative => "!",
- };
- let generated = format!("{}{} ", encoded, prefix, out);
- if links.insert(generated.clone()) { Some(generated) } else { None }
- })
- .collect::>();
- ret.sort();
- ret
- };
-
- let concrete_format = format_impls(concrete, id_map);
- let synthetic_format = format_impls(synthetic, id_map);
- let blanket_format = format_impls(blanket_impl, id_map);
-
- if !concrete_format.is_empty() {
- print_sidebar_block(
- out,
- "trait-implementations",
- "Trait Implementations",
- concrete_format.iter(),
- );
- }
-
- if !synthetic_format.is_empty() {
- print_sidebar_block(
- out,
- "synthetic-implementations",
- "Auto Trait Implementations",
- synthetic_format.iter(),
- );
- }
-
- if !blanket_format.is_empty() {
- print_sidebar_block(
- out,
- "blanket-implementations",
- "Blanket Implementations",
- blanket_format.iter(),
- );
- }
-}
-
-fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
- let did = it.item_id.expect_def_id();
- let cache = cx.cache();
-
- if let Some(v) = cache.impls.get(&did) {
- let mut used_links = FxHashSet::default();
- let mut id_map = IdMap::new();
-
- {
- let used_links_bor = &mut used_links;
- let mut assoc_consts = v
- .iter()
- .filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor))
- .collect::>();
- if !assoc_consts.is_empty() {
- // We want links' order to be reproducible so we don't use unstable sort.
- assoc_consts.sort();
-
- print_sidebar_block(
- out,
- "implementations",
- "Associated Constants",
- assoc_consts.iter(),
- );
- }
- let mut methods = v
- .iter()
- .filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx()))
- .collect::>();
- if !methods.is_empty() {
- // We want links' order to be reproducible so we don't use unstable sort.
- methods.sort();
-
- print_sidebar_block(out, "implementations", "Methods", methods.iter());
- }
- }
-
- if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
- if let Some(impl_) =
- v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
- {
- let mut derefs = DefIdSet::default();
- derefs.insert(did);
- sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
- }
-
- let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
- v.iter().partition::, _>(|i| i.inner_impl().kind.is_auto());
- let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
- concrete.into_iter().partition::, _>(|i| i.inner_impl().kind.is_blanket());
-
- sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
- }
- }
-}
-
-fn sidebar_deref_methods(
- cx: &Context<'_>,
- out: &mut Buffer,
- impl_: &Impl,
- v: &[Impl],
- derefs: &mut DefIdSet,
- used_links: &mut FxHashSet,
-) {
- let c = cx.cache();
-
- debug!("found Deref: {:?}", impl_);
- if let Some((target, real_target)) =
- impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
- clean::AssocTypeItem(box ref t, _) => Some(match *t {
- clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
- _ => (&t.type_, &t.type_),
- }),
- _ => None,
- })
- {
- debug!("found target, real_target: {:?} {:?}", target, real_target);
- if let Some(did) = target.def_id(c) &&
- let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
- // `impl Deref for S`
- (did == type_did || !derefs.insert(did))
- {
- // Avoid infinite cycles
- return;
- }
- let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
- let inner_impl = target
- .def_id(c)
- .or_else(|| {
- target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
- })
- .and_then(|did| c.impls.get(&did));
- if let Some(impls) = inner_impl {
- debug!("found inner_impl: {:?}", impls);
- let mut ret = impls
- .iter()
- .filter(|i| i.inner_impl().trait_.is_none())
- .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
- .collect::>();
- if !ret.is_empty() {
- let id = if let Some(target_def_id) = real_target.def_id(c) {
- cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id")
- } else {
- "deref-methods"
- };
- let title = format!(
- "Methods from {}<Target={}>",
- Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
- Escape(&format!("{:#}", real_target.print(cx))),
- );
- // We want links' order to be reproducible so we don't use unstable sort.
- ret.sort();
- print_sidebar_block(out, id, &title, ret.iter());
- }
- }
-
- // Recurse into any further impls that might exist for `target`
- if let Some(target_did) = target.def_id(c) &&
- let Some(target_impls) = c.impls.get(&target_did) &&
- let Some(target_deref_impl) = target_impls.iter().find(|i| {
- i.inner_impl()
- .trait_
- .as_ref()
- .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
- .unwrap_or(false)
- })
- {
- sidebar_deref_methods(
- cx,
- out,
- target_deref_impl,
- target_impls,
- derefs,
- used_links,
- );
- }
- }
-}
-
-fn sidebar_struct(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
- let mut sidebar = Buffer::new();
- let fields = get_struct_fields_name(&s.fields);
-
- if !fields.is_empty() {
- match s.ctor_kind {
- None => {
- print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
- }
- Some(CtorKind::Fn) => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
- Some(CtorKind::Const) => {}
- }
- }
-
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "", sidebar.into_inner());
- }
-}
-
fn get_id_for_impl(for_: &clean::Type, trait_: Option<&clean::Path>, cx: &Context<'_>) -> String {
match trait_ {
Some(t) => small_url_encode(format!("impl-{:#}-for-{:#}", t.print(cx), for_.print(cx))),
@@ -2340,131 +1965,6 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String
}
}
-fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
- write!(buf, "", id, title);
-}
-
-fn print_sidebar_block(
- buf: &mut Buffer,
- id: &str,
- title: &str,
- items: impl Iterator- ,
-) {
- print_sidebar_title(buf, id, title);
- buf.push_str("
");
- for item in items {
- write!(buf, "{} ", item);
- }
- buf.push_str(" ");
-}
-
-fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
- buf.write_str("");
-
- fn print_sidebar_section(
- out: &mut Buffer,
- items: &[clean::Item],
- id: &str,
- title: &str,
- filter: impl Fn(&clean::Item) -> bool,
- mapper: impl Fn(&str) -> String,
- ) {
- let mut items: Vec<&str> = items
- .iter()
- .filter_map(|m| match m.name {
- Some(ref name) if filter(m) => Some(name.as_str()),
- _ => None,
- })
- .collect::>();
-
- if !items.is_empty() {
- items.sort_unstable();
- print_sidebar_block(out, id, title, items.into_iter().map(mapper));
- }
- }
-
- print_sidebar_section(
- buf,
- &t.items,
- "required-associated-types",
- "Required Associated Types",
- |m| m.is_ty_associated_type(),
- |sym| format!("{0} ", sym, ItemType::AssocType),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "provided-associated-types",
- "Provided Associated Types",
- |m| m.is_associated_type(),
- |sym| format!("{0} ", sym, ItemType::AssocType),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "required-associated-consts",
- "Required Associated Constants",
- |m| m.is_ty_associated_const(),
- |sym| format!("{0} ", sym, ItemType::AssocConst),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "provided-associated-consts",
- "Provided Associated Constants",
- |m| m.is_associated_const(),
- |sym| format!("{0} ", sym, ItemType::AssocConst),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "required-methods",
- "Required Methods",
- |m| m.is_ty_method(),
- |sym| format!("{0} ", sym, ItemType::TyMethod),
- );
-
- print_sidebar_section(
- buf,
- &t.items,
- "provided-methods",
- "Provided Methods",
- |m| m.is_method(),
- |sym| format!("{0} ", sym, ItemType::Method),
- );
-
- if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
- let mut res = implementors
- .iter()
- .filter(|i| !i.is_on_local_type(cx))
- .filter_map(|i| extract_for_impl_name(&i.impl_item, cx))
- .collect::>();
-
- if !res.is_empty() {
- res.sort();
- print_sidebar_block(
- buf,
- "foreign-impls",
- "Implementations on Foreign Types",
- res.iter().map(|(name, id)| format!("{} ", id, Escape(name))),
- );
- }
- }
-
- sidebar_assoc_items(cx, buf, it);
-
- print_sidebar_title(buf, "implementors", "Implementors");
- if t.is_auto(cx.tcx()) {
- print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
- }
-
- buf.push_str(" ")
-}
-
/// Returns the list of implementations for the primitive reference type, filtering out any
/// implementations that are on concrete or partially generic types, only keeping implementations
/// of the form `impl Trait for &T`.
@@ -2495,89 +1995,6 @@ pub(crate) fn get_filtered_impls_for_reference<'a>(
(concrete, synthetic, blanket_impl)
}
-fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
- let mut sidebar = Buffer::new();
-
- if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
- sidebar_assoc_items(cx, &mut sidebar, it);
- } else {
- let shared = Rc::clone(&cx.shared);
- let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
-
- sidebar_render_assoc_items(
- cx,
- &mut sidebar,
- &mut IdMap::new(),
- concrete,
- synthetic,
- blanket_impl,
- );
- }
-
- if !sidebar.is_empty() {
- write!(buf, "", sidebar.into_inner());
- }
-}
-
-fn sidebar_typedef(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
- let mut sidebar = Buffer::new();
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "", sidebar.into_inner());
- }
-}
-
-fn get_struct_fields_name(fields: &[clean::Item]) -> Vec {
- let mut fields = fields
- .iter()
- .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
- .filter_map(|f| {
- f.name.map(|name| format!("{name} ", name = name))
- })
- .collect::>();
- fields.sort();
- fields
-}
-
-fn sidebar_union(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, u: &clean::Union) {
- let mut sidebar = Buffer::new();
- let fields = get_struct_fields_name(&u.fields);
-
- if !fields.is_empty() {
- print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
- }
-
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "", sidebar.into_inner());
- }
-}
-
-fn sidebar_enum(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) {
- let mut sidebar = Buffer::new();
-
- let mut variants = e
- .variants()
- .filter_map(|v| {
- v.name
- .as_ref()
- .map(|name| format!("{name} ", name = name))
- })
- .collect::>();
- if !variants.is_empty() {
- variants.sort_unstable();
- print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
- }
-
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "", sidebar.into_inner());
- }
-}
-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) enum ItemSection {
Reexports,
@@ -2731,54 +2148,6 @@ fn item_ty_to_section(ty: ItemType) -> ItemSection {
}
}
-pub(crate) fn sidebar_module_like(buf: &mut Buffer, item_sections_in_use: FxHashSet) {
- use std::fmt::Write as _;
-
- let mut sidebar = String::new();
-
- for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
- let _ = write!(sidebar, "{} ", sec.id(), sec.name());
- }
-
- if !sidebar.is_empty() {
- write!(
- buf,
- "",
- sidebar
- );
- }
-}
-
-fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
- let item_sections_in_use: FxHashSet<_> = items
- .iter()
- .filter(|it| {
- !it.is_stripped()
- && it
- .name
- .or_else(|| {
- if let clean::ImportItem(ref i) = *it.kind &&
- let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
- })
- .is_some()
- })
- .map(|it| item_ty_to_section(it.type_()))
- .collect();
-
- sidebar_module_like(buf, item_sections_in_use);
-}
-
-fn sidebar_foreign_type(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
- let mut sidebar = Buffer::new();
- sidebar_assoc_items(cx, &mut sidebar, it);
-
- if !sidebar.is_empty() {
- write!(buf, "", sidebar.into_inner());
- }
-}
-
/// Returns a list of all paths used in the type.
/// This is used to help deduplicate imported impls
/// for reexported types. If any of the contained
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
new file mode 100644
index 0000000000000..94ad4753d7cb6
--- /dev/null
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -0,0 +1,561 @@
+use std::{borrow::Cow, rc::Rc};
+
+use askama::Template;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::{def::CtorKind, def_id::DefIdSet};
+use rustc_middle::ty::{self, TyCtxt};
+
+use crate::{
+ clean,
+ formats::{item_type::ItemType, Impl},
+ html::{format::Buffer, markdown::IdMap},
+};
+
+use super::{item_ty_to_section, Context, ItemSection};
+
+#[derive(Template)]
+#[template(path = "sidebar.html")]
+pub(super) struct Sidebar<'a> {
+ pub(super) title_prefix: &'static str,
+ pub(super) title: &'a str,
+ pub(super) is_crate: bool,
+ pub(super) version: &'a str,
+ pub(super) blocks: Vec>,
+ pub(super) path: String,
+}
+
+impl<'a> Sidebar<'a> {
+ /// Only create a `` if there are any blocks
+ /// which should actually be rendered.
+ pub fn should_render_blocks(&self) -> bool {
+ self.blocks.iter().any(LinkBlock::should_render)
+ }
+}
+
+/// A sidebar section such as 'Methods'.
+pub(crate) struct LinkBlock<'a> {
+ /// The name of this section, e.g. 'Methods'
+ /// as well as the link to it, e.g. `#implementations`.
+ /// Will be rendered inside an `` tag
+ heading: Link<'a>,
+ links: Vec >,
+ /// Render the heading even if there are no links
+ force_render: bool,
+}
+
+impl<'a> LinkBlock<'a> {
+ pub fn new(heading: Link<'a>, links: Vec >) -> Self {
+ Self { heading, links, force_render: false }
+ }
+
+ pub fn forced(heading: Link<'a>) -> Self {
+ Self { heading, links: vec![], force_render: true }
+ }
+
+ pub fn should_render(&self) -> bool {
+ self.force_render || !self.links.is_empty()
+ }
+}
+
+/// A link to an item. Content should not be escaped.
+#[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
+pub(crate) struct Link<'a> {
+ /// The content for the anchor tag
+ name: Cow<'a, str>,
+ /// The id of an anchor within the page (without a `#` prefix)
+ href: Cow<'a, str>,
+}
+
+impl<'a> Link<'a> {
+ pub fn new(href: impl Into>, name: impl Into>) -> Self {
+ Self { href: href.into(), name: name.into() }
+ }
+ pub fn empty() -> Link<'static> {
+ Link::new("", "")
+ }
+}
+
+pub(super) fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
+ let blocks: Vec> = match *it.kind {
+ clean::StructItem(ref s) => sidebar_struct(cx, it, s),
+ clean::TraitItem(ref t) => sidebar_trait(cx, it, t),
+ clean::PrimitiveItem(_) => sidebar_primitive(cx, it),
+ clean::UnionItem(ref u) => sidebar_union(cx, it, u),
+ clean::EnumItem(ref e) => sidebar_enum(cx, it, e),
+ clean::TypedefItem(_) => sidebar_typedef(cx, it),
+ clean::ModuleItem(ref m) => vec![sidebar_module(&m.items)],
+ clean::ForeignTypeItem => sidebar_foreign_type(cx, it),
+ _ => vec![],
+ };
+ // The sidebar is designed to display sibling functions, modules and
+ // other miscellaneous information. since there are lots of sibling
+ // items (and that causes quadratic growth in large modules),
+ // we refactor common parts into a shared JavaScript file per module.
+ // still, we don't move everything into JS because we want to preserve
+ // as much HTML as possible in order to allow non-JS-enabled browsers
+ // to navigate the documentation (though slightly inefficiently).
+ let (title_prefix, title) = if it.is_struct()
+ || it.is_trait()
+ || it.is_primitive()
+ || it.is_union()
+ || it.is_enum()
+ || it.is_mod()
+ || it.is_typedef()
+ {
+ (
+ match *it.kind {
+ clean::ModuleItem(..) if it.is_crate() => "Crate ",
+ clean::ModuleItem(..) => "Module ",
+ _ => "",
+ },
+ it.name.as_ref().unwrap().as_str(),
+ )
+ } else {
+ ("", "")
+ };
+ let version = if it.is_crate() {
+ cx.cache().crate_version.as_ref().map(String::as_str).unwrap_or_default()
+ } else {
+ ""
+ };
+ let path: String = if !it.is_mod() {
+ cx.current.iter().map(|s| s.as_str()).intersperse("::").collect()
+ } else {
+ "".into()
+ };
+ let sidebar = Sidebar { title_prefix, title, is_crate: it.is_crate(), version, blocks, path };
+ sidebar.render_into(buffer).unwrap();
+}
+
+fn get_struct_fields_name<'a>(fields: &'a [clean::Item]) -> Vec > {
+ let mut fields = fields
+ .iter()
+ .filter(|f| matches!(*f.kind, clean::StructFieldItem(..)))
+ .filter_map(|f| {
+ f.name.as_ref().map(|name| Link::new(format!("structfield.{name}"), name.as_str()))
+ })
+ .collect::>>();
+ fields.sort();
+ fields
+}
+
+fn sidebar_struct<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ s: &'a clean::Struct,
+) -> Vec> {
+ let fields = get_struct_fields_name(&s.fields);
+ let field_name = match s.ctor_kind {
+ Some(CtorKind::Fn) => Some("Tuple Fields"),
+ None => Some("Fields"),
+ _ => None,
+ };
+ let mut items = vec![];
+ if let Some(name) = field_name {
+ items.push(LinkBlock::new(Link::new("fields", name), fields));
+ }
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+fn sidebar_trait<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ t: &'a clean::Trait,
+) -> Vec> {
+ fn filter_items<'a>(
+ items: &'a [clean::Item],
+ filt: impl Fn(&clean::Item) -> bool,
+ ty: &str,
+ ) -> Vec > {
+ let mut res = items
+ .iter()
+ .filter_map(|m: &clean::Item| match m.name {
+ Some(ref name) if filt(m) => Some(Link::new(format!("{ty}.{name}"), name.as_str())),
+ _ => None,
+ })
+ .collect::>>();
+ res.sort();
+ res
+ }
+
+ let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
+ let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
+ let req_assoc_const =
+ filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
+ let prov_assoc_const =
+ filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
+ let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
+ let prov_method = filter_items(&t.items, |m| m.is_method(), "method");
+ let mut foreign_impls = vec![];
+ if let Some(implementors) = cx.cache().implementors.get(&it.item_id.expect_def_id()) {
+ foreign_impls.extend(
+ implementors
+ .iter()
+ .filter(|i| !i.is_on_local_type(cx))
+ .filter_map(|i| super::extract_for_impl_name(&i.impl_item, cx))
+ .map(|(name, id)| Link::new(id, name)),
+ );
+ foreign_impls.sort();
+ }
+
+ let mut blocks: Vec> = [
+ ("required-associated-types", "Required Associated Types", req_assoc),
+ ("provided-associated-types", "Provided Associated Types", prov_assoc),
+ ("required-associated-consts", "Required Associated Constants", req_assoc_const),
+ ("provided-associated-consts", "Provided Associated Constants", prov_assoc_const),
+ ("required-methods", "Required Methods", req_method),
+ ("provided-methods", "Provided Methods", prov_method),
+ ("foreign-impls", "Implementations on Foreign Types", foreign_impls),
+ ]
+ .into_iter()
+ .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items))
+ .collect();
+ sidebar_assoc_items(cx, it, &mut blocks);
+ blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors")));
+ if t.is_auto(cx.tcx()) {
+ blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors")));
+ }
+ blocks
+}
+
+fn sidebar_primitive<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> {
+ if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+ let mut items = vec![];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+ } else {
+ let shared = Rc::clone(&cx.shared);
+ let (concrete, synthetic, blanket_impl) =
+ super::get_filtered_impls_for_reference(&shared, it);
+
+ sidebar_render_assoc_items(cx, &mut IdMap::new(), concrete, synthetic, blanket_impl).into()
+ }
+}
+
+fn sidebar_typedef<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> {
+ let mut items = vec![];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+fn sidebar_union<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ u: &'a clean::Union,
+) -> Vec> {
+ let fields = get_struct_fields_name(&u.fields);
+ let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+/// Adds trait implementations into the blocks of links
+fn sidebar_assoc_items<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ links: &mut Vec>,
+) {
+ let did = it.item_id.expect_def_id();
+ let cache = cx.cache();
+
+ let mut assoc_consts = Vec::new();
+ let mut methods = Vec::new();
+ if let Some(v) = cache.impls.get(&did) {
+ let mut used_links = FxHashSet::default();
+ let mut id_map = IdMap::new();
+
+ {
+ let used_links_bor = &mut used_links;
+ assoc_consts.extend(
+ v.iter()
+ .filter(|i| i.inner_impl().trait_.is_none())
+ .flat_map(|i| get_associated_constants(i.inner_impl(), used_links_bor)),
+ );
+ // We want links' order to be reproducible so we don't use unstable sort.
+ assoc_consts.sort();
+
+ #[rustfmt::skip] // rustfmt makes the pipeline less readable
+ methods.extend(
+ v.iter()
+ .filter(|i| i.inner_impl().trait_.is_none())
+ .flat_map(|i| get_methods(i.inner_impl(), false, used_links_bor, false, cx.tcx())),
+ );
+
+ // We want links' order to be reproducible so we don't use unstable sort.
+ methods.sort();
+ }
+
+ let mut deref_methods = Vec::new();
+ let [concrete, synthetic, blanket] = if v.iter().any(|i| i.inner_impl().trait_.is_some()) {
+ if let Some(impl_) =
+ v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
+ {
+ let mut derefs = DefIdSet::default();
+ derefs.insert(did);
+ sidebar_deref_methods(
+ cx,
+ &mut deref_methods,
+ impl_,
+ v,
+ &mut derefs,
+ &mut used_links,
+ );
+ }
+
+ let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ v.iter().partition::, _>(|i| i.inner_impl().kind.is_auto());
+ let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
+ concrete.into_iter().partition::, _>(|i| i.inner_impl().kind.is_blanket());
+
+ sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl)
+ } else {
+ std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![]))
+ };
+
+ let mut blocks = vec![
+ LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts),
+ LinkBlock::new(Link::new("implementations", "Methods"), methods),
+ ];
+ blocks.append(&mut deref_methods);
+ blocks.extend([concrete, synthetic, blanket]);
+ links.append(&mut blocks);
+ }
+}
+
+fn sidebar_deref_methods<'a>(
+ cx: &'a Context<'_>,
+ out: &mut Vec>,
+ impl_: &Impl,
+ v: &[Impl],
+ derefs: &mut DefIdSet,
+ used_links: &mut FxHashSet,
+) {
+ let c = cx.cache();
+
+ debug!("found Deref: {:?}", impl_);
+ if let Some((target, real_target)) =
+ impl_.inner_impl().items.iter().find_map(|item| match *item.kind {
+ clean::AssocTypeItem(box ref t, _) => Some(match *t {
+ clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
+ _ => (&t.type_, &t.type_),
+ }),
+ _ => None,
+ })
+ {
+ debug!("found target, real_target: {:?} {:?}", target, real_target);
+ if let Some(did) = target.def_id(c) &&
+ let Some(type_did) = impl_.inner_impl().for_.def_id(c) &&
+ // `impl Deref for S`
+ (did == type_did || !derefs.insert(did))
+ {
+ // Avoid infinite cycles
+ return;
+ }
+ let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+ let inner_impl = target
+ .def_id(c)
+ .or_else(|| {
+ target.primitive_type().and_then(|prim| c.primitive_locations.get(&prim).cloned())
+ })
+ .and_then(|did| c.impls.get(&did));
+ if let Some(impls) = inner_impl {
+ debug!("found inner_impl: {:?}", impls);
+ let mut ret = impls
+ .iter()
+ .filter(|i| i.inner_impl().trait_.is_none())
+ .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
+ .collect::>();
+ if !ret.is_empty() {
+ let id = if let Some(target_def_id) = real_target.def_id(c) {
+ Cow::Borrowed(
+ cx.deref_id_map
+ .get(&target_def_id)
+ .expect("Deref section without derived id")
+ .as_str(),
+ )
+ } else {
+ Cow::Borrowed("deref-methods")
+ };
+ let title = format!(
+ "Methods from {:#}",
+ impl_.inner_impl().trait_.as_ref().unwrap().print(cx),
+ real_target.print(cx),
+ );
+ // We want links' order to be reproducible so we don't use unstable sort.
+ ret.sort();
+ out.push(LinkBlock::new(Link::new(id, title), ret));
+ }
+ }
+
+ // Recurse into any further impls that might exist for `target`
+ if let Some(target_did) = target.def_id(c) &&
+ let Some(target_impls) = c.impls.get(&target_did) &&
+ let Some(target_deref_impl) = target_impls.iter().find(|i| {
+ i.inner_impl()
+ .trait_
+ .as_ref()
+ .map(|t| Some(t.def_id()) == cx.tcx().lang_items().deref_trait())
+ .unwrap_or(false)
+ })
+ {
+ sidebar_deref_methods(
+ cx,
+ out,
+ target_deref_impl,
+ target_impls,
+ derefs,
+ used_links,
+ );
+ }
+ }
+}
+
+fn sidebar_enum<'a>(
+ cx: &'a Context<'_>,
+ it: &'a clean::Item,
+ e: &'a clean::Enum,
+) -> Vec> {
+ let mut variants = e
+ .variants()
+ .filter_map(|v| v.name)
+ .map(|name| Link::new(format!("variant.{name}"), name.to_string()))
+ .collect::>();
+ variants.sort_unstable();
+
+ let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+pub(crate) fn sidebar_module_like(
+ item_sections_in_use: FxHashSet,
+) -> LinkBlock<'static> {
+ let item_sections = ItemSection::ALL
+ .iter()
+ .copied()
+ .filter(|sec| item_sections_in_use.contains(sec))
+ .map(|sec| Link::new(sec.id(), sec.name()))
+ .collect();
+ LinkBlock::new(Link::empty(), item_sections)
+}
+
+fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> {
+ let item_sections_in_use: FxHashSet<_> = items
+ .iter()
+ .filter(|it| {
+ !it.is_stripped()
+ && it
+ .name
+ .or_else(|| {
+ if let clean::ImportItem(ref i) = *it.kind &&
+ let clean::ImportKind::Simple(s) = i.kind { Some(s) } else { None }
+ })
+ .is_some()
+ })
+ .map(|it| item_ty_to_section(it.type_()))
+ .collect();
+
+ sidebar_module_like(item_sections_in_use)
+}
+
+fn sidebar_foreign_type<'a>(cx: &'a Context<'_>, it: &'a clean::Item) -> Vec> {
+ let mut items = vec![];
+ sidebar_assoc_items(cx, it, &mut items);
+ items
+}
+
+/// Renders the trait implementations for this type
+fn sidebar_render_assoc_items(
+ cx: &Context<'_>,
+ id_map: &mut IdMap,
+ concrete: Vec<&Impl>,
+ synthetic: Vec<&Impl>,
+ blanket_impl: Vec<&Impl>,
+) -> [LinkBlock<'static>; 3] {
+ let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+ let mut links = FxHashSet::default();
+
+ let mut ret = impls
+ .iter()
+ .filter_map(|it| {
+ let trait_ = it.inner_impl().trait_.as_ref()?;
+ let encoded =
+ id_map.derive(super::get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+ let prefix = match it.inner_impl().polarity {
+ ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+ ty::ImplPolarity::Negative => "!",
+ };
+ let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx)));
+ if links.insert(generated.clone()) { Some(generated) } else { None }
+ })
+ .collect::>>();
+ ret.sort();
+ ret
+ };
+
+ let concrete = format_impls(concrete, id_map);
+ let synthetic = format_impls(synthetic, id_map);
+ let blanket = format_impls(blanket_impl, id_map);
+ [
+ LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete),
+ LinkBlock::new(
+ Link::new("synthetic-implementations", "Auto Trait Implementations"),
+ synthetic,
+ ),
+ LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket),
+ ]
+}
+
+fn get_next_url(used_links: &mut FxHashSet, url: String) -> String {
+ if used_links.insert(url.clone()) {
+ return url;
+ }
+ let mut add = 1;
+ while !used_links.insert(format!("{}-{}", url, add)) {
+ add += 1;
+ }
+ format!("{}-{}", url, add)
+}
+
+fn get_methods<'a>(
+ i: &'a clean::Impl,
+ for_deref: bool,
+ used_links: &mut FxHashSet,
+ deref_mut: bool,
+ tcx: TyCtxt<'_>,
+) -> Vec > {
+ i.items
+ .iter()
+ .filter_map(|item| match item.name {
+ Some(ref name) if !name.is_empty() && item.is_method() => {
+ if !for_deref || super::should_render_item(item, deref_mut, tcx) {
+ Some(Link::new(
+ get_next_url(used_links, format!("{}.{}", ItemType::Method, name)),
+ name.as_str(),
+ ))
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })
+ .collect::>()
+}
+
+fn get_associated_constants<'a>(
+ i: &'a clean::Impl,
+ used_links: &mut FxHashSet,
+) -> Vec > {
+ i.items
+ .iter()
+ .filter_map(|item| match item.name {
+ Some(ref name) if !name.is_empty() && item.is_associated_const() => Some(Link::new(
+ get_next_url(used_links, format!("{}.{}", ItemType::AssocConst, name)),
+ name.as_str(),
+ )),
+ _ => None,
+ })
+ .collect::>()
+}
diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html
new file mode 100644
index 0000000000000..01d476ad29f2f
--- /dev/null
+++ b/src/librustdoc/html/templates/sidebar.html
@@ -0,0 +1,37 @@
+{% if !title.is_empty() %}
+
+{% endif %}
+