-//! Rustdoc's HTML Rendering module
+//! Rustdoc's HTML rendering module.
//!
//! This modules contains the bulk of the logic necessary for rendering a
//! rustdoc `clean::Crate` instance to a set of static HTML pages. This
use html::escape::Escape;
use html::format::{AsyncSpace, ConstnessSpace};
use html::format::{GenericBounds, WhereClause, href, AbiSpace};
-use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
+use html::format::{VisSpace, Function, UnsafetySpace, MutableSpace};
use html::format::fmt_impl_for_trait_page;
use html::item_type::ItemType;
use html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine, ErrorCodes, IdMap};
/// If false, the `select` element to have search filtering by crates on rendered docs
/// won't be generated.
pub generate_search_filter: bool,
+ /// Option disabled by default to generate files used by RLS and some other tools.
+ pub generate_redirect_pages: bool,
}
impl SharedContext {
}
impl SharedContext {
- /// Returns whether the `collapse-docs` pass was run on this crate.
+ /// Returns `true` if the `collapse-docs` pass was run on this crate.
pub fn was_collapsed(&self) -> bool {
self.passes.contains("collapse-docs")
}
#[derive(Default)]
pub struct Cache {
/// Mapping of typaram ids to the name of the type parameter. This is used
- /// when pretty-printing a type (so pretty printing doesn't have to
+ /// when pretty-printing a type (so pretty-printing doesn't have to
/// painfully maintain a context like this)
pub typarams: FxHashMap<DefId, String>,
- /// Maps a type id to all known implementations for that type. This is only
+ /// Maps a type ID to all known implementations for that type. This is only
/// recognized for intra-crate `ResolvedPath` types, and is used to print
/// out extra documentation on the page of an enum/struct.
///
/// found on that implementation.
pub impls: FxHashMap<DefId, Vec<Impl>>,
- /// Maintains a mapping of local crate node ids to the fully qualified name
+ /// Maintains a mapping of local crate `NodeId`s to the fully qualified name
/// and "short type description" of that node. This is used when generating
/// URLs when a type is being linked to. External paths are not located in
/// this map because the `External` type itself has all the information
/// generating explicit hyperlinks to other crates.
pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
- /// Maps local def ids of exported types to fully qualified paths.
+ /// Maps local `DefId`s of exported types to fully qualified paths.
/// Unlike 'paths', this mapping ignores any renames that occur
/// due to 'use' statements.
///
resource_suffix,
static_root_path,
generate_search_filter,
+ generate_redirect_pages,
..
} = options;
resource_suffix,
static_root_path,
generate_search_filter,
+ generate_redirect_pages,
};
// If user passed in `--playground-url` arg, we fill in crate name here
cx.krate(krate)
}
-/// Build the search index from the collected metadata
+/// Builds the search index from the collected metadata
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
let mut nodeid_to_pathid = FxHashMap::default();
let mut crate_items = Vec::with_capacity(cache.search_index.len());
for param in &generics.params {
match param.kind {
clean::GenericParamDefKind::Lifetime => {}
- clean::GenericParamDefKind::Type { did, .. } => {
+ clean::GenericParamDefKind::Type { did, .. } |
+ clean::GenericParamDefKind::Const { did, .. } => {
self.typarams.insert(did, param.name.clone());
}
}
keywords: FxHashSet<ItemEntry>,
attributes: FxHashSet<ItemEntry>,
derives: FxHashSet<ItemEntry>,
+ trait_aliases: FxHashSet<ItemEntry>,
}
impl AllTypes {
keywords: new_set(100),
attributes: new_set(100),
derives: new_set(100),
+ trait_aliases: new_set(100),
}
}
ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)),
ItemType::ProcAttribute => self.attributes.insert(ItemEntry::new(new_url, name)),
ItemType::ProcDerive => self.derives.insert(ItemEntry::new(new_url, name)),
+ ItemType::TraitAlias => self.trait_aliases.insert(ItemEntry::new(new_url, name)),
_ => true,
};
}
print_entries(f, &self.derives, "Derive Macros", "derives")?;
print_entries(f, &self.functions, "Functions", "functions")?;
print_entries(f, &self.typedefs, "Typedefs", "typedefs")?;
+ print_entries(f, &self.trait_aliases, "Trait Aliases", "trait-aliases")?;
print_entries(f, &self.existentials, "Existentials", "existentials")?;
print_entries(f, &self.statics, "Statics", "statics")?;
print_entries(f, &self.constants, "Constants", "constants")
if !self.render_redirect_pages {
all.append(full_path(self, &item), &item_type);
}
- // Redirect from a sane URL using the namespace to Rustdoc's
- // URL for the page.
- let redir_name = format!("{}.{}.html", name, item_type.name_space());
- let redir_dst = self.dst.join(redir_name);
- if let Ok(redirect_out) = OpenOptions::new().create_new(true)
- .write(true)
- .open(&redir_dst) {
- let mut redirect_out = BufWriter::new(redirect_out);
- try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst);
+ if self.shared.generate_redirect_pages {
+ // Redirect from a sane URL using the namespace to Rustdoc's
+ // URL for the page.
+ let redir_name = format!("{}.{}.html", name, item_type.name_space());
+ let redir_dst = self.dst.join(redir_name);
+ if let Ok(redirect_out) = OpenOptions::new().create_new(true)
+ .write(true)
+ .open(&redir_dst) {
+ let mut redirect_out = BufWriter::new(redirect_out);
+ try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst);
+ }
}
-
// If the item is a macro, redirect from the old macro URL (with !)
// to the new one (without).
if item_type == ItemType::Macro {
}
impl<'a> Item<'a> {
- /// Generate a url appropriate for an `href` attribute back to the source of
+ /// Generates a url appropriate for an `href` attribute back to the source of
/// this item.
///
/// The url generated, when clicked, will redirect the browser back to the
clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?,
clean::KeywordItem(..) => write!(fmt, "Keyword ")?,
clean::ExistentialItem(..) => write!(fmt, "Existential Type ")?,
+ clean::TraitAliasItem(..) => write!(fmt, "Trait Alias ")?,
_ => {
// We don't generate pages for any other type.
unreachable!();
clean::ForeignTypeItem => item_foreign_type(fmt, self.cx, self.item),
clean::KeywordItem(ref k) => item_keyword(fmt, self.cx, self.item, k),
clean::ExistentialItem(ref e, _) => item_existential(fmt, self.cx, self.item, e),
+ clean::TraitAliasItem(ref ta) => item_trait_alias(fmt, self.cx, self.item, ta),
_ => {
// We don't generate pages for any other type.
unreachable!();
// The trailing space after each tag is to space it properly against the rest of the docs.
if item.deprecation().is_some() {
- tags += &tag_html("deprecated", "Deprecated");
+ let mut message = "Deprecated";
+ if let Some(ref stab) = item.stability {
+ if let Some(ref depr) = stab.deprecation {
+ if let Some(ref since) = depr.since {
+ if !stability::deprecation_in_effect(&since) {
+ message = "Deprecation planned";
+ }
+ }
+ }
+ }
+ tags += &tag_html("deprecated", message);
}
if let Some(stab) = item
let mut stability = vec![];
let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
- if let Some(Deprecation { since, note }) = &item.deprecation() {
+ if let Some(Deprecation { note, since }) = &item.deprecation() {
+ // We display deprecation messages for #[deprecated] and #[rustc_deprecated]
+ // but only display the future-deprecation messages for #[rustc_deprecated].
let mut message = if let Some(since) = since {
- if stability::deprecation_in_effect(since) {
- format!("Deprecated since {}", Escape(since))
- } else {
- format!("Deprecating in {}", Escape(since))
- }
+ format!("Deprecated since {}", Escape(since))
} else {
String::from("Deprecated")
};
+ if let Some(ref stab) = item.stability {
+ if let Some(ref depr) = stab.deprecation {
+ if let Some(ref since) = depr.since {
+ if !stability::deprecation_in_effect(&since) {
+ message = format!("Deprecating in {}", Escape(&since));
+ }
+ }
+ }
+ }
if let Some(note) = note {
let mut ids = cx.id_map.borrow_mut();
name = it.name.as_ref().unwrap(),
generics = f.generics,
where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true },
- decl = Method {
+ decl = Function {
decl: &f.decl,
name_len,
indent: 0,
+ asyncness: f.header.asyncness,
})?;
document(w, cx, it)
}
Ok(())
}
-fn bounds(t_bounds: &[clean::GenericBound]) -> String {
+fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String {
let mut bounds = String::new();
- let mut bounds_plain = String::new();
if !t_bounds.is_empty() {
- if !bounds.is_empty() {
- bounds.push(' ');
- bounds_plain.push(' ');
+ if !trait_alias {
+ bounds.push_str(": ");
}
- bounds.push_str(": ");
- bounds_plain.push_str(": ");
for (i, p) in t_bounds.iter().enumerate() {
if i > 0 {
bounds.push_str(" + ");
- bounds_plain.push_str(" + ");
}
bounds.push_str(&(*p).to_string());
- bounds_plain.push_str(&format!("{:#}", *p));
}
}
bounds
it: &clean::Item,
t: &clean::Trait,
) -> fmt::Result {
- let bounds = bounds(&t.bounds);
+ let bounds = bounds(&t.bounds, false);
let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
href = href,
name = name,
generics = *g,
- decl = Method {
+ decl = Function {
decl: d,
name_len: head_len,
indent,
+ asyncness: header.asyncness,
},
where_clause = WhereClause {
gens: g,
it.name.as_ref().unwrap(),
t.generics,
where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true },
- bounds = bounds(&t.bounds))?;
+ bounds = bounds(&t.bounds, false))?;
+
+ document(w, cx, it)?;
+
+ // Render any items associated directly to this alias, as otherwise they
+ // won't be visible anywhere in the docs. It would be nice to also show
+ // associated items from the aliased type (see discussion in #32077), but
+ // we need #14072 to make sense of the generics.
+ render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
+}
+
+fn item_trait_alias(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
+ t: &clean::TraitAlias) -> fmt::Result {
+ write!(w, "<pre class='rust trait-alias'>")?;
+ render_attributes(w, it)?;
+ write!(w, "trait {}{}{} = {};</pre>",
+ it.name.as_ref().unwrap(),
+ t.generics,
+ WhereClause { gens: &t.generics, indent: 0, end_newline: true },
+ bounds(&t.bounds, true))?;
document(w, cx, it)?;
ItemType::Existential => ("existentials", "Existentials"),
ItemType::ProcAttribute => ("attributes", "Attribute Macros"),
ItemType::ProcDerive => ("derives", "Derive Macros"),
+ ItemType::TraitAlias => ("trait-aliases", "Trait aliases"),
}
}