diff options
| author | Joseph Ryan <josephryan3.14@gmail.com> | 2020-06-24 08:16:21 -0500 |
|---|---|---|
| committer | Joseph Ryan <josephryan3.14@gmail.com> | 2020-07-27 16:00:38 -0500 |
| commit | 6a4396b98c6fcb405429a9798a9ab6554f015b7e (patch) | |
| tree | 8e900f16fd378deb9ad937878b7cd90e1d541af6 /src/librustdoc/html/render.rs | |
| parent | 5bc97946ca35a789b690668bb6b27ca41bfeb5b2 (diff) | |
| download | rust-6a4396b98c6fcb405429a9798a9ab6554f015b7e.tar.gz rust-6a4396b98c6fcb405429a9798a9ab6554f015b7e.zip | |
Extract `Cache` and other types from `html` module
Diffstat (limited to 'src/librustdoc/html/render.rs')
| -rw-r--r-- | src/librustdoc/html/render.rs | 4599 |
1 files changed, 0 insertions, 4599 deletions
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs deleted file mode 100644 index fd32ba66d91..00000000000 --- a/src/librustdoc/html/render.rs +++ /dev/null @@ -1,4599 +0,0 @@ -// ignore-tidy-filelength - -//! 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 -//! rendering process is largely driven by the `format!` syntax extension to -//! perform all I/O into files and streams. -//! -//! The rendering process is largely driven by the `Context` and `Cache` -//! structures. The cache is pre-populated by crawling the crate in question, -//! and then it is shared among the various rendering threads. The cache is meant -//! to be a fairly large structure not implementing `Clone` (because it's shared -//! among threads). The context, however, should be a lightweight structure. This -//! is cloned per-thread and contains information about what is currently being -//! rendered. -//! -//! In order to speed up rendering (mostly because of markdown rendering), the -//! rendering process has been parallelized. This parallelization is only -//! exposed through the `crate` method on the context, and then also from the -//! fact that the shared cache is stored in TLS (and must be accessed as such). -//! -//! In addition to rendering the crate itself, this module is also responsible -//! for creating the corresponding search index and source file renderings. -//! These threads are not parallelized (they haven't been a bottleneck yet), and -//! both occur before the crate is rendered. - -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; -use std::cmp::Ordering; -use std::collections::{BTreeMap, VecDeque}; -use std::default::Default; -use std::ffi::OsStr; -use std::fmt::{self, Write}; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::path::{Component, Path, PathBuf}; -use std::rc::Rc; -use std::str; -use std::string::ToString; -use std::sync::Arc; - -use itertools::Itertools; -use rustc_ast_pretty::pprust; -use rustc_data_structures::flock; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_feature::UnstableFeatures; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_hir::Mutability; -use rustc_middle::middle::stability; -use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::FileName; -use rustc_span::symbol::{sym, Symbol}; -use serde::ser::SerializeSeq; -use serde::{Serialize, Serializer}; - -use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, TypeKind}; -use crate::config::RenderInfo; -use crate::config::RenderOptions; -use crate::docfs::{DocFS, ErrorStorage, PathError}; -use crate::doctree; -use crate::error::Error; -use crate::formats::{FormatRenderer, Renderer}; -use crate::html::escape::Escape; -use crate::html::format::fmt_impl_for_trait_page; -use crate::html::format::Function; -use crate::html::format::{href, print_default_space, print_generic_bounds, WhereClause}; -use crate::html::format::{print_abi_with_space, Buffer, PrintWithSpace}; -use crate::html::item_type::ItemType; -use crate::html::markdown::{self, ErrorCodes, IdMap, Markdown, MarkdownHtml, MarkdownSummaryLine}; -use crate::html::sources; -use crate::html::{highlight, layout, static_files}; - -#[cfg(test)] -mod tests; - -mod cache; - -use cache::Cache; -crate use cache::ExternalLocation::{self, *}; - -/// A pair of name and its optional document. -pub type NameDoc = (String, Option<String>); - -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) } - }) -} - -/// Major driving force in all rustdoc rendering. This contains information -/// about where in the tree-like hierarchy rendering is occurring and controls -/// how the current page is being rendered. -/// -/// It is intended that this context is a lightweight object which can be fairly -/// easily cloned because it is cloned per work-job (about once per item in the -/// rustdoc tree). -#[derive(Clone)] -crate struct Context { - /// Current hierarchy of components leading down to what's currently being - /// rendered - pub current: Vec<String>, - /// The current destination folder of where HTML artifacts should be placed. - /// This changes as the context descends into the module hierarchy. - pub dst: PathBuf, - /// A flag, which when `true`, will render pages which redirect to the - /// real location of an item. This is used to allow external links to - /// publicly reused items to redirect to the right location. - pub render_redirect_pages: bool, - /// The map used to ensure all generated 'id=' attributes are unique. - id_map: Rc<RefCell<IdMap>>, - pub shared: Arc<SharedContext>, - pub cache: Arc<Cache>, - pub parent: Rc<RefCell<Renderer>>, - all: Rc<RefCell<AllTypes>>, - pub errors: Arc<ErrorStorage>, -} - -crate struct SharedContext { - /// The path to the crate root source minus the file name. - /// Used for simplifying paths to the highlighted source code files. - pub src_root: PathBuf, - /// This describes the layout of each page, and is not modified after - /// creation of the context (contains info like the favicon and added html). - pub layout: layout::Layout, - /// This flag indicates whether `[src]` links should be generated or not. If - /// the source files are present in the html rendering, then this will be - /// `true`. - pub include_sources: bool, - /// The local file sources we've emitted and their respective url-paths. - pub local_sources: FxHashMap<PathBuf, String>, - /// Whether the collapsed pass ran - pub collapsed: bool, - /// The base-URL of the issue tracker for when an item has been tagged with - /// an issue number. - pub issue_tracker_base_url: Option<String>, - /// The directories that have already been created in this doc run. Used to reduce the number - /// of spurious `create_dir_all` calls. - pub created_dirs: RefCell<FxHashSet<PathBuf>>, - /// This flag indicates whether listings of modules (in the side bar and documentation itself) - /// should be ordered alphabetically or in order of appearance (in the source code). - pub sort_modules_alphabetically: bool, - /// Additional CSS files to be added to the generated docs. - pub style_files: Vec<StylePath>, - /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes - /// "light-v2.css"). - pub resource_suffix: String, - /// Optional path string to be used to load static files on output pages. If not set, uses - /// combinations of `../` to reach the documentation root. - pub static_root_path: Option<String>, - /// The fs handle we are working with. - pub fs: DocFS, - /// The default edition used to parse doctests. - pub edition: Edition, - pub codes: ErrorCodes, - playground: Option<markdown::Playground>, -} - -impl Context { - fn path(&self, filename: &str) -> PathBuf { - // We use splitn vs Path::extension here because we might get a filename - // like `style.min.css` and we want to process that into - // `style-suffix.min.css`. Path::extension would just return `css` - // which would result in `style.min-suffix.css` which isn't what we - // want. - let mut iter = filename.splitn(2, '.'); - let base = iter.next().unwrap(); - let ext = iter.next().unwrap(); - let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext,); - self.dst.join(&filename) - } -} - -impl SharedContext { - crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> { - let mut dirs = self.created_dirs.borrow_mut(); - if !dirs.contains(dst) { - try_err!(self.fs.create_dir_all(dst), dst); - dirs.insert(dst.to_path_buf()); - } - - Ok(()) - } - - /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the - /// `collapsed_doc_value` of the given item. - pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<Cow<'a, str>> { - if self.collapsed { - item.collapsed_doc_value().map(|s| s.into()) - } else { - item.doc_value().map(|s| s.into()) - } - } -} - -/// Metadata about implementations for a type or trait. -#[derive(Clone, Debug)] -pub struct Impl { - pub impl_item: clean::Item, -} - -impl Impl { - fn inner_impl(&self) -> &clean::Impl { - match self.impl_item.inner { - clean::ImplItem(ref impl_) => impl_, - _ => panic!("non-impl item found in impl"), - } - } - - fn trait_did(&self) -> Option<DefId> { - self.inner_impl().trait_.def_id() - } -} - -// Helper structs for rendering items/sidebars and carrying along contextual -// information - -/// Struct representing one entry in the JS search index. These are all emitted -/// by hand to a large JS file at the end of cache-creation. -#[derive(Debug)] -struct IndexItem { - ty: ItemType, - name: String, - path: String, - desc: String, - parent: Option<DefId>, - parent_idx: Option<usize>, - search_type: Option<IndexItemFunctionType>, -} - -impl Serialize for IndexItem { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - assert_eq!( - self.parent.is_some(), - self.parent_idx.is_some(), - "`{}` is missing idx", - self.name - ); - - (self.ty, &self.name, &self.path, &self.desc, self.parent_idx, &self.search_type) - .serialize(serializer) - } -} - -/// A type used for the search index. -#[derive(Debug)] -struct RenderType { - ty: Option<DefId>, - idx: Option<usize>, - name: Option<String>, - generics: Option<Vec<Generic>>, -} - -impl Serialize for RenderType { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - if let Some(name) = &self.name { - let mut seq = serializer.serialize_seq(None)?; - if let Some(id) = self.idx { - seq.serialize_element(&id)?; - } else { - seq.serialize_element(&name)?; - } - if let Some(generics) = &self.generics { - seq.serialize_element(&generics)?; - } - seq.end() - } else { - serializer.serialize_none() - } - } -} - -/// A type used for the search index. -#[derive(Debug)] -struct Generic { - name: String, - defid: Option<DefId>, - idx: Option<usize>, -} - -impl Serialize for Generic { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - if let Some(id) = self.idx { - serializer.serialize_some(&id) - } else { - serializer.serialize_some(&self.name) - } - } -} - -/// Full type of functions/methods in the search index. -#[derive(Debug)] -struct IndexItemFunctionType { - inputs: Vec<TypeWithKind>, - output: Option<Vec<TypeWithKind>>, -} - -impl Serialize for IndexItemFunctionType { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - // If we couldn't figure out a type, just write `null`. - let mut iter = self.inputs.iter(); - if match self.output { - Some(ref output) => iter.chain(output.iter()).any(|ref i| i.ty.name.is_none()), - None => iter.any(|ref i| i.ty.name.is_none()), - } { - serializer.serialize_none() - } else { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&self.inputs)?; - if let Some(output) = &self.output { - if output.len() > 1 { - seq.serialize_element(&output)?; - } else { - seq.serialize_element(&output[0])?; - } - } - seq.end() - } - } -} - -#[derive(Debug)] -pub struct TypeWithKind { - ty: RenderType, - kind: TypeKind, -} - -impl From<(RenderType, TypeKind)> for TypeWithKind { - fn from(x: (RenderType, TypeKind)) -> TypeWithKind { - TypeWithKind { ty: x.0, kind: x.1 } - } -} - -impl Serialize for TypeWithKind { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let mut seq = serializer.serialize_seq(None)?; - seq.serialize_element(&self.ty.name)?; - let x: ItemType = self.kind.into(); - seq.serialize_element(&x)?; - seq.end() - } -} - -#[derive(Debug, Clone)] -pub struct StylePath { - /// The path to the theme - pub path: PathBuf, - /// What the `disabled` attribute should be set to in the HTML tag - pub disabled: bool, -} - -thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default()); -thread_local!(pub static CURRENT_DEPTH: Cell<usize> = Cell::new(0)); - -pub fn initial_ids() -> Vec<String> { - [ - "main", - "search", - "help", - "TOC", - "render-detail", - "associated-types", - "associated-const", - "required-methods", - "provided-methods", - "implementors", - "synthetic-implementors", - "implementors-list", - "synthetic-implementors-list", - "methods", - "deref-methods", - "implementations", - ] - .iter() - .map(|id| (String::from(*id))) - .collect() -} - -impl FormatRenderer for Context { - type Output = Self; - - /// Generates the documentation for `crate` into the directory `dst` - fn init( - mut krate: clean::Crate, - options: RenderOptions, - renderinfo: RenderInfo, - _diag: &rustc_errors::Handler, - edition: Edition, - parent: Rc<RefCell<Renderer>>, - ) -> Result<(Context, clean::Crate), Error> { - // need to save a copy of the options for rendering the index page - let md_opts = options.clone(); - let RenderOptions { - output, - external_html, - id_map, - playground_url, - sort_modules_alphabetically, - themes: style_files, - extension_css, - extern_html_root_urls, - resource_suffix, - static_root_path, - generate_search_filter, - document_private, - .. - } = options; - - let src_root = match krate.src { - FileName::Real(ref p) => match p.local_path().parent() { - Some(p) => p.to_path_buf(), - None => PathBuf::new(), - }, - _ => PathBuf::new(), - }; - let errors = Arc::new(ErrorStorage::new()); - // If user passed in `--playground-url` arg, we fill in crate name here - let mut playground = None; - if let Some(url) = playground_url { - playground = Some(markdown::Playground { crate_name: Some(krate.name.clone()), url }); - } - let mut layout = layout::Layout { - logo: String::new(), - favicon: String::new(), - external_html, - krate: krate.name.clone(), - css_file_extension: extension_css, - generate_search_filter, - }; - let mut issue_tracker_base_url = None; - let mut include_sources = true; - - // Crawl the crate attributes looking for attributes which control how we're - // going to emit HTML - if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { - for attr in attrs.lists(sym::doc) { - match (attr.name_or_empty(), attr.value_str()) { - (sym::html_favicon_url, Some(s)) => { - layout.favicon = s.to_string(); - } - (sym::html_logo_url, Some(s)) => { - layout.logo = s.to_string(); - } - (sym::html_playground_url, Some(s)) => { - playground = Some(markdown::Playground { - crate_name: Some(krate.name.clone()), - url: s.to_string(), - }); - } - (sym::issue_tracker_base_url, Some(s)) => { - issue_tracker_base_url = Some(s.to_string()); - } - (sym::html_no_source, None) if attr.is_word() => { - include_sources = false; - } - _ => {} - } - } - } - let mut scx = SharedContext { - collapsed: krate.collapsed, - src_root, - include_sources, - local_sources: Default::default(), - issue_tracker_base_url, - layout, - created_dirs: Default::default(), - sort_modules_alphabetically, - style_files, - resource_suffix, - static_root_path, - fs: DocFS::new(&errors), - edition, - codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()), - playground, - }; - - // Add the default themes to the `Vec` of stylepaths - // - // Note that these must be added before `sources::render` is called - // so that the resulting source pages are styled - // - // `light.css` is not disabled because it is the stylesheet that stays loaded - // by the browser as the theme stylesheet. The theme system (hackily) works by - // changing the href to this stylesheet. All other themes are disabled to - // prevent rule conflicts - scx.style_files.push(StylePath { path: PathBuf::from("light.css"), disabled: false }); - scx.style_files.push(StylePath { path: PathBuf::from("dark.css"), disabled: true }); - scx.style_files.push(StylePath { path: PathBuf::from("ayu.css"), disabled: true }); - - let dst = output; - scx.ensure_dir(&dst)?; - krate = sources::render(&dst, &mut scx, krate)?; - let (new_crate, index, cache) = - Cache::from_krate(renderinfo, document_private, &extern_html_root_urls, &dst, krate); - krate = new_crate; - let cache = Arc::new(cache); - let mut cx = Context { - current: Vec::new(), - dst, - render_redirect_pages: false, - id_map: Rc::new(RefCell::new(id_map)), - shared: Arc::new(scx), - cache: cache.clone(), - parent, - all: Rc::new(RefCell::new(AllTypes::new())), - errors, - }; - - // Freeze the cache now that the index has been built. Put an Arc into TLS - // for future parallelization opportunities - CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); - CURRENT_DEPTH.with(|s| s.set(0)); - - // Write shared runs within a flock; disable thread dispatching of IO temporarily. - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); - write_shared(&cx, &krate, index, &md_opts)?; - Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); - Ok((cx, krate)) - } - - fn after_run(&mut self, diag: &rustc_errors::Handler) -> Result<(), Error> { - let nb_errors = - Arc::get_mut(&mut self.errors).map_or_else(|| 0, |errors| errors.write_errors(diag)); - if nb_errors > 0 { - Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), "")) - } else { - Ok(()) - } - } - - fn after_krate(&mut self, krate: &clean::Crate) -> Result<(), Error> { - let final_file = self.dst.join(&krate.name).join("all.html"); - let settings_file = self.dst.join("settings.html"); - let crate_name = krate.name.clone(); - - let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); - if !root_path.ends_with('/') { - root_path.push('/'); - } - let mut page = layout::Page { - title: "List of all items in this crate", - css_class: "mod", - root_path: "../", - static_root_path: self.shared.static_root_path.as_deref(), - description: "List of all items in this crate", - keywords: BASIC_KEYWORDS, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - let sidebar = if let Some(ref version) = self.cache.crate_version { - format!( - "<p class='location'>Crate {}</p>\ - <div class='block version'>\ - <p>Version {}</p>\ - </div>\ - <a id='all-types' href='index.html'><p>Back to index</p></a>", - crate_name, - Escape(version), - ) - } else { - String::new() - }; - let all = self.all.replace(AllTypes::new()); - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - |buf: &mut Buffer| all.print(buf), - &self.shared.style_files, - ); - self.shared.fs.write(&final_file, v.as_bytes())?; - - // Generating settings page. - page.title = "Rustdoc settings"; - page.description = "Settings of Rustdoc"; - page.root_path = "./"; - - let mut style_files = self.shared.style_files.clone(); - let sidebar = "<p class='location'>Settings</p><div class='sidebar-elems'></div>"; - style_files.push(StylePath { path: PathBuf::from("settings.css"), disabled: false }); - let v = layout::render( - &self.shared.layout, - &page, - sidebar, - settings( - self.shared.static_root_path.as_deref().unwrap_or("./"), - &self.shared.resource_suffix, - ), - &style_files, - ); - self.shared.fs.write(&settings_file, v.as_bytes())?; - Ok(()) - } - - fn mod_item_in( - &mut self, - item: &clean::Item, - item_name: &str, - module: &clean::Module, - ) -> Result<(), Error> { - // 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. - // - // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally - // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); - } - let scx = &self.shared; - self.dst.push(item_name); - self.current.push(item_name.to_owned()); - - info!("Recursing into {}", self.dst.display()); - - let buf = self.render_item(item, false); - // buf will be empty if the module is stripped and there is no redirect for it - if !buf.is_empty() { - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join("index.html"); - scx.fs.write(&joint_dst, buf.as_bytes())?; - } - - // Render sidebar-items.js used throughout this module. - if !self.render_redirect_pages { - let items = self.build_sidebar_items(module); - let js_dst = self.dst.join("sidebar-items.js"); - let v = format!("initSidebarItems({});", serde_json::to_string(&items).unwrap()); - scx.fs.write(&js_dst, &v)?; - } - Ok(()) - } - - fn mod_item_out(&mut self) -> Result<(), Error> { - info!("Recursed; leaving {}", self.dst.display()); - - // Go back to where we were at - self.dst.pop(); - self.current.pop(); - Ok(()) - } - - fn item(&mut self, item: clean::Item) -> Result<(), Error> { - // 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. - // - // External crates will provide links to these structures, so - // these modules are recursed into, but not rendered normally - // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); - } - - let buf = self.render_item(&item, true); - // buf will be empty if the item is stripped and there is no redirect for it - if !buf.is_empty() { - let name = item.name.as_ref().unwrap(); - let item_type = item.type_(); - let file_name = &item_path(item_type, name); - self.shared.ensure_dir(&self.dst)?; - let joint_dst = self.dst.join(file_name); - self.shared.fs.write(&joint_dst, buf.as_bytes())?; - - if !self.render_redirect_pages { - self.all.borrow_mut().append(full_path(self, &item), &item_type); - } - // If the item is a macro, redirect from the old macro URL (with !) - // to the new one (without). - if item_type == ItemType::Macro { - let redir_name = format!("{}.{}!.html", item_type, name); - let redir_dst = self.dst.join(redir_name); - let v = layout::redirect(file_name); - self.shared.fs.write(&redir_dst, v.as_bytes())?; - } - } - Ok(()) - } -} - -fn write_shared( - cx: &Context, - krate: &clean::Crate, - search_index: String, - options: &RenderOptions, -) -> Result<(), Error> { - // Write out the shared files. Note that these are shared among all rustdoc - // docs placed in the output directory, so this needs to be a synchronized - // operation with respect to all other rustdocs running around. - let lock_file = cx.dst.join(".lock"); - let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); - - // Add all the static files. These may already exist, but we just - // overwrite them anyway to make sure that they're fresh and up-to-date. - - write_minify( - &cx.shared.fs, - cx.path("rustdoc.css"), - static_files::RUSTDOC_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.css"), - static_files::SETTINGS_CSS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("noscript.css"), - 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.style_files { - let theme = try_none!(try_none!(entry.path.file_stem(), &entry.path).to_str(), &entry.path); - let extension = - try_none!(try_none!(entry.path.extension(), &entry.path).to_str(), &entry.path); - - // Handle the official themes - match theme { - "light" => write_minify( - &cx.shared.fs, - cx.path("light.css"), - static_files::themes::LIGHT, - options.enable_minification, - )?, - "dark" => write_minify( - &cx.shared.fs, - cx.path("dark.css"), - static_files::themes::DARK, - options.enable_minification, - )?, - "ayu" => write_minify( - &cx.shared.fs, - cx.path("ayu.css"), - static_files::themes::AYU, - options.enable_minification, - )?, - _ => { - // Handle added third-party themes - let content = try_err!(fs::read(&entry.path), &entry.path); - cx.shared - .fs - .write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?; - } - }; - - themes.insert(theme.to_owned()); - } - - let write = |p, c| cx.shared.fs.write(p, c); - if (*cx.shared).layout.logo.is_empty() { - write(cx.path("rust-logo.png"), static_files::RUST_LOGO)?; - } - if (*cx.shared).layout.favicon.is_empty() { - write(cx.path("favicon.ico"), static_files::RUST_FAVICON)?; - } - write(cx.path("brush.svg"), static_files::BRUSH_SVG)?; - write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?; - write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?; - - let mut themes: Vec<&String> = themes.iter().collect(); - themes.sort(); - // To avoid theme switch latencies as much as possible, we put everything theme related - // at the beginning of the html files into another js file. - let theme_js = format!( - r#"var themes = document.getElementById("theme-choices"); -var themePicker = document.getElementById("theme-picker"); - -function showThemeButtonState() {{ - themes.style.display = "block"; - themePicker.style.borderBottomRightRadius = "0"; - themePicker.style.borderBottomLeftRadius = "0"; -}} - -function hideThemeButtonState() {{ - themes.style.display = "none"; - themePicker.style.borderBottomRightRadius = "3px"; - themePicker.style.borderBottomLeftRadius = "3px"; -}} - -function switchThemeButtonState() {{ - if (themes.style.display === "block") {{ - hideThemeButtonState(); - }} else {{ - showThemeButtonState(); - }} -}}; - -function handleThemeButtonsBlur(e) {{ - var active = document.activeElement; - var related = e.relatedTarget; - - if (active.id !== "themePicker" && - (!active.parentNode || active.parentNode.id !== "theme-choices") && - (!related || - (related.id !== "themePicker" && - (!related.parentNode || related.parentNode.id !== "theme-choices")))) {{ - hideThemeButtonState(); - }} -}} - -themePicker.onclick = switchThemeButtonState; -themePicker.onblur = handleThemeButtonsBlur; -{}.forEach(function(item) {{ - var but = document.createElement('button'); - but.textContent = item; - but.onclick = function(el) {{ - switchTheme(currentTheme, mainTheme, item, true); - }}; - but.onblur = handleThemeButtonsBlur; - themes.appendChild(but); -}});"#, - serde_json::to_string(&themes).unwrap() - ); - - write_minify(&cx.shared.fs, cx.path("theme.js"), &theme_js, options.enable_minification)?; - write_minify( - &cx.shared.fs, - cx.path("main.js"), - static_files::MAIN_JS, - options.enable_minification, - )?; - write_minify( - &cx.shared.fs, - cx.path("settings.js"), - static_files::SETTINGS_JS, - options.enable_minification, - )?; - if cx.shared.include_sources { - write_minify( - &cx.shared.fs, - cx.path("source-script.js"), - static_files::sidebar::SOURCE_SCRIPT, - options.enable_minification, - )?; - } - - { - write_minify( - &cx.shared.fs, - cx.path("storage.js"), - &format!( - "var resourcesSuffix = \"{}\";{}", - cx.shared.resource_suffix, - static_files::STORAGE_JS - ), - options.enable_minification, - )?; - } - - if let Some(ref css) = cx.shared.layout.css_file_extension { - let out = cx.path("theme.css"); - let buffer = try_err!(fs::read_to_string(css), css); - if !options.enable_minification { - cx.shared.fs.write(&out, &buffer)?; - } else { - write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?; - } - } - write_minify( - &cx.shared.fs, - cx.path("normalize.css"), - static_files::NORMALIZE_CSS, - options.enable_minification, - )?; - write(cx.dst.join("FiraSans-Regular.woff"), static_files::fira_sans::REGULAR)?; - write(cx.dst.join("FiraSans-Medium.woff"), static_files::fira_sans::MEDIUM)?; - write(cx.dst.join("FiraSans-LICENSE.txt"), static_files::fira_sans::LICENSE)?; - write(cx.dst.join("SourceSerifPro-Regular.ttf.woff"), static_files::source_serif_pro::REGULAR)?; - write(cx.dst.join("SourceSerifPro-Bold.ttf.woff"), static_files::source_serif_pro::BOLD)?; - write(cx.dst.join("SourceSerifPro-It.ttf.woff"), static_files::source_serif_pro::ITALIC)?; - write(cx.dst.join("SourceSerifPro-LICENSE.md"), static_files::source_serif_pro::LICENSE)?; - write(cx.dst.join("SourceCodePro-Regular.woff"), static_files::source_code_pro::REGULAR)?; - write(cx.dst.join("SourceCodePro-Semibold.woff"), static_files::source_code_pro::SEMIBOLD)?; - write(cx.dst.join("SourceCodePro-LICENSE.txt"), static_files::source_code_pro::LICENSE)?; - write(cx.dst.join("LICENSE-MIT.txt"), static_files::LICENSE_MIT)?; - write(cx.dst.join("LICENSE-APACHE.txt"), static_files::LICENSE_APACHE)?; - write(cx.dst.join("COPYRIGHT.txt"), static_files::COPYRIGHT)?; - - fn collect(path: &Path, krate: &str, key: &str) -> io::Result<(Vec<String>, Vec<String>)> { - let mut ret = Vec::new(); - let mut krates = Vec::new(); - - if path.exists() { - 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)) { - continue; - } - ret.push(line.to_string()); - krates.push( - line[key.len() + 2..] - .split('"') - .next() - .map(|s| s.to_owned()) - .unwrap_or_else(String::new), - ); - } - } - Ok((ret, krates)) - } - - fn collect_json(path: &Path, krate: &str) -> io::Result<(Vec<String>, Vec<String>)> { - let mut ret = Vec::new(); - let mut krates = Vec::new(); - - if path.exists() { - for line in BufReader::new(File::open(path)?).lines() { - let line = line?; - if !line.starts_with('"') { - continue; - } - if line.starts_with(&format!("\"{}\"", krate)) { - continue; - } - if line.ends_with(",\\") { - ret.push(line[..line.len() - 2].to_string()); - } else { - // Ends with "\\" (it's the case for the last added crate line) - ret.push(line[..line.len() - 1].to_string()); - } - krates.push( - line.split('"') - .find(|s| !s.is_empty()) - .map(|s| s.to_owned()) - .unwrap_or_else(String::new), - ); - } - } - Ok((ret, krates)) - } - - use std::ffi::OsString; - - #[derive(Debug)] - struct Hierarchy { - elem: OsString, - children: FxHashMap<OsString, Hierarchy>, - elems: FxHashSet<OsString>, - } - - impl Hierarchy { - fn new(elem: OsString) -> Hierarchy { - Hierarchy { elem, children: FxHashMap::default(), elems: FxHashSet::default() } - } - - fn to_json_string(&self) -> String { - let mut subs: Vec<&Hierarchy> = self.children.values().collect(); - subs.sort_unstable_by(|a, b| a.elem.cmp(&b.elem)); - let mut files = self - .elems - .iter() - .map(|s| format!("\"{}\"", s.to_str().expect("invalid osstring conversion"))) - .collect::<Vec<_>>(); - files.sort_unstable_by(|a, b| a.cmp(b)); - let subs = subs.iter().map(|s| s.to_json_string()).collect::<Vec<_>>().join(","); - let dirs = - if subs.is_empty() { String::new() } else { format!(",\"dirs\":[{}]", subs) }; - let files = files.join(","); - let files = - if files.is_empty() { String::new() } else { format!(",\"files\":[{}]", files) }; - format!( - "{{\"name\":\"{name}\"{dirs}{files}}}", - name = self.elem.to_str().expect("invalid osstring conversion"), - dirs = dirs, - files = files - ) - } - } - - if cx.shared.include_sources { - let mut hierarchy = Hierarchy::new(OsString::new()); - for source in cx - .shared - .local_sources - .iter() - .filter_map(|p| p.0.strip_prefix(&cx.shared.src_root).ok()) - { - let mut h = &mut hierarchy; - let mut elems = source - .components() - .filter_map(|s| match s { - Component::Normal(s) => Some(s.to_owned()), - _ => None, - }) - .peekable(); - loop { - let cur_elem = elems.next().expect("empty file path"); - if elems.peek().is_none() { - h.elems.insert(cur_elem); - 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"); - } - } - } - - let dst = cx.dst.join(&format!("source-files{}.js", cx.shared.resource_suffix)); - let (mut all_sources, _krates) = try_err!(collect(&dst, &krate.name, "sourcesIndex"), &dst); - all_sources.push(format!( - "sourcesIndex[\"{}\"] = {};", - &krate.name, - hierarchy.to_json_string() - )); - all_sources.sort(); - let v = format!( - "var N = null;var sourcesIndex = {{}};\n{}\ncreateSourceSidebar();\n", - all_sources.join("\n") - ); - cx.shared.fs.write(&dst, v.as_bytes())?; - } - - // Update the search index - let dst = cx.dst.join(&format!("search-index{}.js", cx.shared.resource_suffix)); - let (mut all_indexes, mut krates) = try_err!(collect_json(&dst, &krate.name), &dst); - all_indexes.push(search_index); - - // Sort the indexes by crate so the file will be generated identically even - // with rustdoc running in parallel. - all_indexes.sort(); - { - let mut v = String::from("var searchIndex = JSON.parse('{\\\n"); - v.push_str(&all_indexes.join(",\\\n")); - // "addSearchOptions" has to be called first so the crate filtering can be set before the - // search might start (if it's set into the URL for example). - v.push_str("\\\n}');\naddSearchOptions(searchIndex);initSearch(searchIndex);"); - cx.shared.fs.write(&dst, &v)?; - } - if options.enable_index_page { - if let Some(index_page) = options.index_page.clone() { - let mut md_opts = options.clone(); - md_opts.output = cx.dst.clone(); - md_opts.external_html = (*cx.shared).layout.external_html.clone(); - - crate::markdown::render(&index_page, md_opts, cx.shared.edition) - .map_err(|e| Error::new(e, &index_page))?; - } else { - let dst = cx.dst.join("index.html"); - let page = layout::Page { - title: "Index of crates", - css_class: "mod", - root_path: "./", - static_root_path: cx.shared.static_root_path.as_deref(), - description: "List of crates", - keywords: BASIC_KEYWORDS, - resource_suffix: &cx.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - krates.push(krate.name.clone()); - krates.sort(); - krates.dedup(); - - let content = format!( - "<h1 class='fqn'>\ - <span class='in-band'>List of all crates</span>\ -</h1><ul class='mod'>{}</ul>", - krates - .iter() - .map(|s| { - format!("<li><a href=\"{}index.html\">{}</li>", ensure_trailing_slash(s), s) - }) - .collect::<String>() - ); - let v = layout::render(&cx.shared.layout, &page, "", content, &cx.shared.style_files); - cx.shared.fs.write(&dst, v.as_bytes())?; - } - } - - // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); - for (&did, imps) in &cx.cache.implementors { - // Private modules can leak through to this phase of rustdoc, which - // could contain implementations for otherwise private types. In some - // rare cases we could find an implementation for an item which wasn't - // indexed, so we just skip this step in that case. - // - // FIXME: this is a vague explanation for why this can't be a `get`, in - // theory it should be... - let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) { - Some(p) => p, - None => match cx.cache.external_paths.get(&did) { - Some(p) => p, - None => continue, - }, - }; - - #[derive(Serialize)] - struct Implementor { - text: String, - synthetic: bool, - types: Vec<String>, - } - - let implementors = imps - .iter() - .filter_map(|imp| { - // If the trait and implementation are in the same crate, then - // there's no need to emit information about it (there's inlining - // going on). If they're in different crates then the crate defining - // the trait will be interested in our implementation. - // - // If the implementation is from another crate then that crate - // should add it. - if imp.impl_item.def_id.krate == did.krate || !imp.impl_item.def_id.is_local() { - None - } else { - Some(Implementor { - text: imp.inner_impl().print().to_string(), - synthetic: imp.inner_impl().synthetic, - types: collect_paths_for_type(imp.inner_impl().for_.clone()), - }) - } - }) - .collect::<Vec<_>>(); - - // Only create a js file if we have impls to add to it. If the trait is - // documented locally though we always create the file to avoid dead - // links. - if implementors.is_empty() && !cx.cache.paths.contains_key(&did) { - continue; - } - - let implementors = format!( - r#"implementors["{}"] = {};"#, - krate.name, - serde_json::to_string(&implementors).unwrap() - ); - - let mut mydst = dst.clone(); - for part in &remote_path[..remote_path.len() - 1] { - mydst.push(part); - } - cx.shared.ensure_dir(&mydst)?; - mydst.push(&format!("{}.{}.js", remote_item_type, remote_path[remote_path.len() - 1])); - - let (mut all_implementors, _) = - try_err!(collect(&mydst, &krate.name, "implementors"), &mydst); - all_implementors.push(implementors); - // Sort the implementors by crate so the file will be generated - // identically even with rustdoc running in parallel. - all_implementors.sort(); - - let mut v = String::from("(function() {var implementors = {};\n"); - for implementor in &all_implementors { - writeln!(v, "{}", *implementor).unwrap(); - } - v.push_str( - "if (window.register_implementors) {\ - window.register_implementors(implementors);\ - } else {\ - window.pending_implementors = implementors;\ - }", - ); - v.push_str("})()"); - cx.shared.fs.write(&mydst, &v)?; - } - Ok(()) -} - -fn write_minify( - fs: &DocFS, - dst: PathBuf, - contents: &str, - enable_minification: bool, -) -> Result<(), Error> { - if enable_minification { - if dst.extension() == Some(&OsStr::new("css")) { - let res = try_none!(minifier::css::minify(contents).ok(), &dst); - fs.write(dst, res.as_bytes()) - } else { - fs.write(dst, minifier::js::minify(contents).as_bytes()) - } - } else { - fs.write(dst, contents.as_bytes()) - } -} - -#[derive(Debug, Eq, PartialEq, Hash)] -struct ItemEntry { - url: String, - name: String, -} - -impl ItemEntry { - fn new(mut url: String, name: String) -> ItemEntry { - while url.starts_with('/') { - url.remove(0); - } - ItemEntry { url, name } - } -} - -impl ItemEntry { - crate fn print(&self) -> impl fmt::Display + '_ { - crate::html::format::display_fn(move |f| { - write!(f, "<a href='{}'>{}</a>", self.url, Escape(&self.name)) - }) - } -} - -impl PartialOrd for ItemEntry { - fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for ItemEntry { - fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering { - self.name.cmp(&other.name) - } -} - -#[derive(Debug)] -struct AllTypes { - structs: FxHashSet<ItemEntry>, - enums: FxHashSet<ItemEntry>, - unions: FxHashSet<ItemEntry>, - primitives: FxHashSet<ItemEntry>, - traits: FxHashSet<ItemEntry>, - macros: FxHashSet<ItemEntry>, - functions: FxHashSet<ItemEntry>, - typedefs: FxHashSet<ItemEntry>, - opaque_tys: FxHashSet<ItemEntry>, - statics: FxHashSet<ItemEntry>, - constants: FxHashSet<ItemEntry>, - keywords: FxHashSet<ItemEntry>, - attributes: FxHashSet<ItemEntry>, - derives: FxHashSet<ItemEntry>, - trait_aliases: FxHashSet<ItemEntry>, -} - -impl AllTypes { - fn new() -> AllTypes { - let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default()); - AllTypes { - structs: new_set(100), - enums: new_set(100), - unions: new_set(100), - primitives: new_set(26), - traits: new_set(100), - macros: new_set(100), - functions: new_set(100), - typedefs: new_set(100), - opaque_tys: new_set(100), - statics: new_set(100), - constants: new_set(100), - keywords: new_set(100), - attributes: new_set(100), - derives: new_set(100), - trait_aliases: new_set(100), - } - } - - fn append(&mut self, item_name: String, item_type: &ItemType) { - let mut url: Vec<_> = item_name.split("::").skip(1).collect(); - if let Some(name) = url.pop() { - let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name); - url.push(name); - let name = url.join("::"); - match *item_type { - ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)), - ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)), - ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)), - ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)), - ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)), - ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), - ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), - ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)), - ItemType::OpaqueTy => self.opaque_tys.insert(ItemEntry::new(new_url, name)), - ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), - 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, - }; - } - } -} - -fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &str) { - if !e.is_empty() { - let mut e: Vec<&ItemEntry> = e.iter().collect(); - e.sort(); - write!( - f, - "<h3 id='{}'>{}</h3><ul class='{} docblock'>{}</ul>", - title, - Escape(title), - class, - e.iter().map(|s| format!("<li>{}</li>", s.print())).collect::<String>() - ); - } -} - -impl AllTypes { - fn print(self, f: &mut Buffer) { - write!( - f, - "<h1 class='fqn'>\ - <span class='out-of-band'>\ - <span id='render-detail'>\ - <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" title=\"collapse all docs\">\ - [<span class='inner'>−</span>]\ - </a>\ - </span> - </span> - <span class='in-band'>List of all items</span>\ - </h1>" - ); - print_entries(f, &self.structs, "Structs", "structs"); - print_entries(f, &self.enums, "Enums", "enums"); - print_entries(f, &self.unions, "Unions", "unions"); - print_entries(f, &self.primitives, "Primitives", "primitives"); - print_entries(f, &self.traits, "Traits", "traits"); - print_entries(f, &self.macros, "Macros", "macros"); - print_entries(f, &self.attributes, "Attribute Macros", "attributes"); - 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.opaque_tys, "Opaque Types", "opaque-types"); - print_entries(f, &self.statics, "Statics", "statics"); - print_entries(f, &self.constants, "Constants", "constants") - } -} - -#[derive(Debug)] -enum Setting { - Section { description: &'static str, sub_settings: Vec<Setting> }, - Entry { js_data_name: &'static str, description: &'static str, default_value: bool }, -} - -impl Setting { - fn display(&self) -> String { - match *self { - Setting::Section { ref description, ref sub_settings } => format!( - "<div class='setting-line'>\ - <div class='title'>{}</div>\ - <div class='sub-settings'>{}</div> - </div>", - description, - sub_settings.iter().map(|s| s.display()).collect::<String>() - ), - Setting::Entry { ref js_data_name, ref description, ref default_value } => format!( - "<div class='setting-line'>\ - <label class='toggle'>\ - <input type='checkbox' id='{}' {}>\ - <span class='slider'></span>\ - </label>\ - <div>{}</div>\ - </div>", - js_data_name, - if *default_value { " checked" } else { "" }, - description, - ), - } - } -} - -impl From<(&'static str, &'static str, bool)> for Setting { - fn from(values: (&'static str, &'static str, bool)) -> Setting { - Setting::Entry { js_data_name: values.0, description: values.1, default_value: values.2 } - } -} - -impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting { - fn from(values: (&'static str, Vec<T>)) -> Setting { - Setting::Section { - description: values.0, - sub_settings: values.1.into_iter().map(|v| v.into()).collect::<Vec<_>>(), - } - } -} - -fn settings(root_path: &str, suffix: &str) -> String { - // (id, explanation, default value) - let settings: &[Setting] = &[ - ( - "Auto-hide item declarations", - vec![ - ("auto-hide-struct", "Auto-hide structs declaration", true), - ("auto-hide-enum", "Auto-hide enums declaration", false), - ("auto-hide-union", "Auto-hide unions declaration", true), - ("auto-hide-trait", "Auto-hide traits declaration", true), - ("auto-hide-macro", "Auto-hide macros declaration", false), - ], - ) - .into(), - ("auto-hide-attributes", "Auto-hide item attributes.", true).into(), - ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(), - ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true) - .into(), - ("auto-collapse-implementors", "Auto-hide implementors of a trait", true).into(), - ("go-to-only-result", "Directly go to item in search if there is only one result", false) - .into(), - ("line-numbers", "Show line numbers on code examples", false).into(), - ("disable-shortcuts", "Disable keyboard shortcuts", false).into(), - ]; - format!( - "<h1 class='fqn'>\ - <span class='in-band'>Rustdoc settings</span>\ -</h1>\ -<div class='settings'>{}</div>\ -<script src='{}settings{}.js'></script>", - settings.iter().map(|s| s.display()).collect::<String>(), - root_path, - suffix - ) -} - -impl Context { - fn derive_id(&self, id: String) -> String { - let mut map = self.id_map.borrow_mut(); - map.derive(id) - } - - /// String representation of how to get back to the root path of the 'doc/' - /// folder in terms of a relative URL. - fn root_path(&self) -> String { - "../".repeat(self.current.len()) - } - - fn render_item(&self, it: &clean::Item, pushname: bool) -> String { - // A little unfortunate that this is done like this, but it sure - // does make formatting *a lot* nicer. - CURRENT_DEPTH.with(|slot| { - slot.set(self.current.len()); - }); - - let mut title = if it.is_primitive() || it.is_keyword() { - // No need to include the namespace for primitive types and keywords - String::new() - } else { - self.current.join("::") - }; - if pushname { - if !title.is_empty() { - title.push_str("::"); - } - title.push_str(it.name.as_ref().unwrap()); - } - title.push_str(" - Rust"); - let tyname = it.type_(); - let desc = if it.is_crate() { - format!("API documentation for the Rust `{}` crate.", self.shared.layout.krate) - } else { - format!( - "API documentation for the Rust `{}` {} in crate `{}`.", - it.name.as_ref().unwrap(), - tyname, - self.shared.layout.krate - ) - }; - let keywords = make_item_keywords(it); - let page = layout::Page { - css_class: tyname.as_str(), - root_path: &self.root_path(), - static_root_path: self.shared.static_root_path.as_deref(), - title: &title, - description: &desc, - keywords: &keywords, - resource_suffix: &self.shared.resource_suffix, - extra_scripts: &[], - static_extra_scripts: &[], - }; - - { - self.id_map.borrow_mut().reset(); - self.id_map.borrow_mut().populate(initial_ids()); - } - - if !self.render_redirect_pages { - layout::render( - &self.shared.layout, - &page, - |buf: &mut _| print_sidebar(self, it, buf), - |buf: &mut _| print_item(self, it, buf), - &self.shared.style_files, - ) - } else { - let mut url = self.root_path(); - if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) { - for name in &names[..names.len() - 1] { - url.push_str(name); - url.push_str("/"); - } - url.push_str(&item_path(ty, names.last().unwrap())); - layout::redirect(&url) - } else { - String::new() - } - } - } - - fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> { - // BTreeMap instead of HashMap to get a sorted output - let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new(); - for item in &m.items { - if item.is_stripped() { - continue; - } - - let short = item.type_(); - let myname = match item.name { - None => continue, - Some(ref s) => s.to_string(), - }; - let short = short.to_string(); - map.entry(short) - .or_default() - .push((myname, Some(plain_summary_line(item.doc_value())))); - } - - if self.shared.sort_modules_alphabetically { - for items in map.values_mut() { - items.sort(); - } - } - map - } -} - -impl Context { - /// 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 - /// original source code. - /// - /// If `None` is returned, then a source link couldn't be generated. This - /// may happen, for example, with externally inlined items where the source - /// of their crate documentation isn't known. - fn src_href(&self, item: &clean::Item) -> Option<String> { - let mut root = self.root_path(); - - let mut path = String::new(); - - // We can safely ignore synthetic `SourceFile`s. - let file = match item.source.filename { - FileName::Real(ref path) => path.local_path().to_path_buf(), - _ => return None, - }; - let file = &file; - - let (krate, path) = if item.source.cnum == LOCAL_CRATE { - if let Some(path) = self.shared.local_sources.get(file) { - (&self.shared.layout.krate, path) - } else { - return None; - } - } else { - let (krate, src_root) = match *self.cache.extern_locations.get(&item.source.cnum)? { - (ref name, ref src, Local) => (name, src), - (ref name, ref src, Remote(ref s)) => { - root = s.to_string(); - (name, src) - } - (_, _, Unknown) => return None, - }; - - sources::clean_path(&src_root, file, false, |component| { - path.push_str(&component.to_string_lossy()); - path.push('/'); - }); - let mut fname = file.file_name().expect("source has no filename").to_os_string(); - fname.push(".html"); - path.push_str(&fname.to_string_lossy()); - (krate, &path) - }; - - let lines = if item.source.loline == item.source.hiline { - item.source.loline.to_string() - } else { - format!("{}-{}", item.source.loline, item.source.hiline) - }; - Some(format!( - "{root}src/{krate}/{path}#{lines}", - root = Escape(&root), - krate = krate, - path = path, - lines = lines - )) - } -} - -fn wrap_into_docblock<F>(w: &mut Buffer, f: F) -where - F: FnOnce(&mut Buffer), -{ - write!(w, "<div class=\"docblock type-decl hidden-by-usual-hider\">"); - f(w); - write!(w, "</div>") -} - -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, "<h1 class='fqn'><span class='out-of-band'>"); - if let Some(version) = item.stable_since() { - write!( - buf, - "<span class='since' title='Stable since Rust version {0}'>{0}</span>", - version - ); - } - write!( - buf, - "<span id='render-detail'>\ - <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \ - title=\"collapse all docs\">\ - [<span class='inner'>−</span>]\ - </a>\ - </span>" - ); - - // Write `src` tag - // - // When this item is part of a `pub use` in a downstream crate, the - // [src] link in the downstream documentation will actually come back to - // this page, and this link will be auto-clicked. The `id` attribute is - // used to find the link to auto-click. - if cx.shared.include_sources && !item.is_primitive() { - if let Some(l) = cx.src_href(item) { - write!(buf, "<a class='srclink' href='{}' title='{}'>[src]</a>", l, "goto source code"); - } - } - - write!(buf, "</span>"); // out-of-band - write!(buf, "<span class='in-band'>"); - let name = match item.inner { - clean::ModuleItem(ref m) => { - if m.is_crate { - "Crate " - } else { - "Module " - } - } - clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => "Function ", - clean::TraitItem(..) => "Trait ", - clean::StructItem(..) => "Struct ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::MacroItem(..) => "Macro ", - clean::ProcMacroItem(ref mac) => match mac.kind { - MacroKind::Bang => "Macro ", - MacroKind::Attr => "Attribute Macro ", - MacroKind::Derive => "Derive Macro ", - }, - clean::PrimitiveItem(..) => "Primitive Type ", - clean::StaticItem(..) | clean::ForeignStaticItem(..) => "Static ", - clean::ConstantItem(..) => "Constant ", - clean::ForeignTypeItem => "Foreign Type ", - clean::KeywordItem(..) => "Keyword ", - clean::OpaqueTyItem(..) => "Opaque Type ", - clean::TraitAliasItem(..) => "Trait Alias ", - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - }; - buf.write_str(name); - if !item.is_primitive() && !item.is_keyword() { - let cur = &cx.current; - let amt = if item.is_mod() { cur.len() - 1 } else { cur.len() }; - for (i, component) in cur.iter().enumerate().take(amt) { - write!( - buf, - "<a href='{}index.html'>{}</a>::<wbr>", - "../".repeat(cur.len() - i - 1), - component - ); - } - } - write!(buf, "<a class=\"{}\" href=''>{}</a>", item.type_(), item.name.as_ref().unwrap()); - - write!(buf, "</span></h1>"); // in-band - - match item.inner { - clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items), - clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => { - item_function(buf, cx, item, f) - } - clean::TraitItem(ref t) => item_trait(buf, cx, item, t), - clean::StructItem(ref s) => item_struct(buf, cx, item, s), - clean::UnionItem(ref s) => item_union(buf, cx, item, s), - clean::EnumItem(ref e) => item_enum(buf, cx, item, e), - clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t), - clean::MacroItem(ref m) => item_macro(buf, cx, item, m), - clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m), - clean::PrimitiveItem(_) => item_primitive(buf, cx, item), - clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => item_static(buf, cx, item, i), - clean::ConstantItem(ref c) => item_constant(buf, cx, item, c), - clean::ForeignTypeItem => item_foreign_type(buf, cx, item), - clean::KeywordItem(_) => item_keyword(buf, cx, item), - clean::OpaqueTyItem(ref e, _) => item_opaque_ty(buf, cx, item, e), - clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta), - _ => { - // We don't generate pages for any other type. - unreachable!(); - } - } -} - -fn item_path(ty: ItemType, name: &str) -> String { - match ty { - ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)), - _ => format!("{}.{}.html", ty, name), - } -} - -fn full_path(cx: &Context, item: &clean::Item) -> String { - let mut s = cx.current.join("::"); - s.push_str("::"); - s.push_str(item.name.as_ref().unwrap()); - s -} - -#[inline] -fn plain_summary_line(s: Option<&str>) -> String { - let s = s.unwrap_or(""); - // This essentially gets the first paragraph of text in one line. - let mut line = s - .lines() - .skip_while(|line| line.chars().all(|c| c.is_whitespace())) - .take_while(|line| line.chars().any(|c| !c.is_whitespace())) - .fold(String::new(), |mut acc, line| { - acc.push_str(line); - acc.push(' '); - acc - }); - // remove final whitespace - line.pop(); - markdown::plain_summary_line(&line[..]) -} - -fn shorten(s: String) -> String { - if s.chars().count() > 60 { - let mut len = 0; - let mut ret = s - .split_whitespace() - .take_while(|p| { - // + 1 for the added character after the word. - len += p.chars().count() + 1; - len < 60 - }) - .collect::<Vec<_>>() - .join(" "); - ret.push('…'); - ret - } else { - s - } -} - -fn document(w: &mut Buffer, cx: &Context, item: &clean::Item) { - if let Some(ref name) = item.name { - info!("Documenting {}", name); - } - document_stability(w, cx, item, false); - document_full(w, item, cx, "", false); -} - -/// Render md_text as markdown. -fn render_markdown( - w: &mut Buffer, - cx: &Context, - md_text: &str, - links: Vec<(String, String)>, - prefix: &str, - is_hidden: bool, -) { - let mut ids = cx.id_map.borrow_mut(); - write!( - w, - "<div class='docblock{}'>{}{}</div>", - if is_hidden { " hidden" } else { "" }, - prefix, - Markdown( - md_text, - &links, - &mut ids, - cx.shared.codes, - cx.shared.edition, - &cx.shared.playground - ) - .into_string() - ) -} - -fn document_short( - w: &mut Buffer, - cx: &Context, - item: &clean::Item, - link: AssocItemLink<'_>, - prefix: &str, - is_hidden: bool, -) { - if let Some(s) = item.doc_value() { - let markdown = if s.contains('\n') { - format!( - "{} [Read more]({})", - &plain_summary_line(Some(s)), - naive_assoc_href(item, link) - ) - } else { - plain_summary_line(Some(s)) - }; - render_markdown(w, cx, &markdown, item.links(), prefix, is_hidden); - } else if !prefix.is_empty() { - write!( - w, - "<div class='docblock{}'>{}</div>", - if is_hidden { " hidden" } else { "" }, - prefix - ); - } -} - -fn document_full(w: &mut Buffer, item: &clean::Item, cx: &Context, prefix: &str, is_hidden: bool) { - if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { - debug!("Doc block: =====\n{}\n=====", s); - render_markdown(w, cx, &*s, item.links(), prefix, is_hidden); - } else if !prefix.is_empty() { - write!( - w, - "<div class='docblock{}'>{}</div>", - if is_hidden { " hidden" } else { "" }, - prefix - ); - } -} - -fn document_stability(w: &mut Buffer, cx: &Context, item: &clean::Item, is_hidden: bool) { - let stabilities = short_stability(item, cx); - if !stabilities.is_empty() { - write!(w, "<div class='stability{}'>", if is_hidden { " hidden" } else { "" }); - for stability in stabilities { - write!(w, "{}", stability); - } - write!(w, "</div>"); - } -} - -fn document_non_exhaustive_header(item: &clean::Item) -> &str { - if item.is_non_exhaustive() { " (Non-exhaustive)" } else { "" } -} - -fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) { - if item.is_non_exhaustive() { - write!(w, "<div class='docblock non-exhaustive non-exhaustive-{}'>", { - if item.is_struct() { - "struct" - } else if item.is_enum() { - "enum" - } else if item.is_variant() { - "variant" - } else { - "type" - } - }); - - if item.is_struct() { - write!( - w, - "Non-exhaustive structs could have additional fields added in future. \ - Therefore, non-exhaustive structs cannot be constructed in external crates \ - using the traditional <code>Struct {{ .. }}</code> syntax; cannot be \ - matched against without a wildcard <code>..</code>; and \ - struct update syntax will not work." - ); - } else if item.is_enum() { - write!( - w, - "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." - ); - } else if item.is_variant() { - write!( - w, - "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." - ); - } else { - write!( - w, - "This type will require a wildcard arm in any match statements or \ - constructors." - ); - } - - write!(w, "</div>"); - } -} - -fn name_key(name: &str) -> (&str, u64, usize) { - let end = name.bytes().rposition(|b| b.is_ascii_digit()).map_or(name.len(), |i| i + 1); - - // find number at end - let split = name[0..end].bytes().rposition(|b| !b.is_ascii_digit()).map_or(0, |i| i + 1); - - // count leading zeroes - let after_zeroes = - name[split..end].bytes().position(|b| b != b'0').map_or(name.len(), |extra| split + extra); - - // sort leading zeroes last - let num_zeroes = after_zeroes - split; - - match name[split..end].parse() { - Ok(n) => (&name[..split], n, num_zeroes), - Err(_) => (name, 0, num_zeroes), - } -} - -fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean::Item]) { - document(w, cx, item); - - let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>(); - - // the order of item types in the listing - fn reorder(ty: ItemType) -> u8 { - match ty { - ItemType::ExternCrate => 0, - ItemType::Import => 1, - ItemType::Primitive => 2, - ItemType::Module => 3, - ItemType::Macro => 4, - ItemType::Struct => 5, - ItemType::Enum => 6, - ItemType::Constant => 7, - ItemType::Static => 8, - ItemType::Trait => 9, - ItemType::Function => 10, - ItemType::Typedef => 12, - ItemType::Union => 13, - _ => 14 + ty as u8, - } - } - - fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering { - let ty1 = i1.type_(); - let ty2 = i2.type_(); - if ty1 != ty2 { - return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)); - } - let s1 = i1.stability.as_ref().map(|s| s.level); - let s2 = i2.stability.as_ref().map(|s| s.level); - match (s1, s2) { - (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater, - (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less, - _ => {} - } - let lhs = i1.name.as_ref().map_or("", |s| &**s); - let rhs = i2.name.as_ref().map_or("", |s| &**s); - name_key(lhs).cmp(&name_key(rhs)) - } - - if cx.shared.sort_modules_alphabetically { - indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); - } - // This call is to remove re-export duplicates in cases such as: - // - // ``` - // pub mod foo { - // pub mod bar { - // pub trait Double { fn foo(); } - // } - // } - // - // pub use foo::bar::*; - // pub use foo::*; - // ``` - // - // `Double` will appear twice in the generated docs. - // - // FIXME: This code is quite ugly and could be improved. Small issue: DefId - // can be identical even if the elements are different (mostly in imports). - // So in case this is an import, we keep everything by adding a "unique id" - // (which is the position in the vector). - indices.dedup_by_key(|i| { - ( - items[*i].def_id, - if items[*i].name.as_ref().is_some() { Some(full_path(cx, &items[*i])) } else { None }, - items[*i].type_(), - if items[*i].is_import() { *i } else { 0 }, - ) - }); - - debug!("{:?}", indices); - let mut curty = None; - for &idx in &indices { - let myitem = &items[idx]; - if myitem.is_stripped() { - continue; - } - - let myty = Some(myitem.type_()); - if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { - // Put `extern crate` and `use` re-exports in the same section. - curty = myty; - } else if myty != curty { - if curty.is_some() { - write!(w, "</table>"); - } - curty = myty; - let (short, name) = item_ty_to_strs(&myty.unwrap()); - write!( - w, - "<h2 id='{id}' class='section-header'>\ - <a href=\"#{id}\">{name}</a></h2>\n<table>", - id = cx.derive_id(short.to_owned()), - name = name - ); - } - - match myitem.inner { - clean::ExternCrateItem(ref name, ref src) => { - use crate::html::format::anchor; - - match *src { - Some(ref src) => write!( - w, - "<tr><td><code>{}extern crate {} as {};", - myitem.visibility.print_with_space(), - anchor(myitem.def_id, src), - name - ), - None => write!( - w, - "<tr><td><code>{}extern crate {};", - myitem.visibility.print_with_space(), - anchor(myitem.def_id, name) - ), - } - write!(w, "</code></td></tr>"); - } - - clean::ImportItem(ref import) => { - write!( - w, - "<tr><td><code>{}{}</code></td></tr>", - myitem.visibility.print_with_space(), - import.print() - ); - } - - _ => { - if myitem.name.is_none() { - continue; - } - - let unsafety_flag = match myitem.inner { - clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) - if func.header.unsafety == hir::Unsafety::Unsafe => - { - "<a title='unsafe function' href='#'><sup>âš </sup></a>" - } - _ => "", - }; - - let stab = myitem.stability_class(); - let add = if stab.is_some() { " " } else { "" }; - - let doc_value = myitem.doc_value().unwrap_or(""); - write!( - w, - "\ - <tr class='{stab}{add}module-item'>\ - <td><a class=\"{class}\" href=\"{href}\" \ - title='{title}'>{name}</a>{unsafety_flag}</td>\ - <td class='docblock-short'>{stab_tags}{docs}</td>\ - </tr>", - name = *myitem.name.as_ref().unwrap(), - stab_tags = stability_tags(myitem), - docs = MarkdownSummaryLine(doc_value, &myitem.links()).into_string(), - class = myitem.type_(), - add = add, - stab = stab.unwrap_or_else(String::new), - unsafety_flag = unsafety_flag, - href = item_path(myitem.type_(), myitem.name.as_ref().unwrap()), - title = [full_path(cx, myitem), myitem.type_().to_string()] - .iter() - .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None }) - .collect::<Vec<_>>() - .join(" "), - ); - } - } - } - - if curty.is_some() { - write!(w, "</table>"); - } -} - -/// Render the stability and deprecation tags that are displayed in the item's summary at the -/// module level. -fn stability_tags(item: &clean::Item) -> String { - let mut tags = String::new(); - - fn tag_html(class: &str, contents: &str) -> String { - format!(r#"<span class="stab {}">{}</span>"#, class, contents) - } - - // The trailing space after each tag is to space it properly against the rest of the docs. - if let Some(depr) = &item.deprecation { - let mut message = "Deprecated"; - if !stability::deprecation_in_effect(depr.is_since_rustc_version, depr.since.as_deref()) { - message = "Deprecation planned"; - } - tags += &tag_html("deprecated", message); - } - - // The "rustc_private" crates are permanently unstable so it makes no sense - // to render "unstable" everywhere. - if item - .stability - .as_ref() - .map(|s| s.level == stability::Unstable && s.feature.as_deref() != Some("rustc_private")) - == Some(true) - { - tags += &tag_html("unstable", "Experimental"); - } - - if let Some(ref cfg) = item.attrs.cfg { - tags += &tag_html("portability", &cfg.render_short_html()); - } - - tags -} - -/// Render the stability and/or deprecation warning that is displayed at the top of the item's -/// documentation. -fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> { - let mut stability = vec![]; - let error_codes = cx.shared.codes; - - if let Some(Deprecation { ref note, ref since, is_since_rustc_version }) = 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(is_since_rustc_version, Some(since)) { - format!("Deprecating in {}", Escape(&since)) - } else { - format!("Deprecated since {}", Escape(&since)) - } - } else { - String::from("Deprecated") - }; - - if let Some(note) = note { - let mut ids = cx.id_map.borrow_mut(); - let html = MarkdownHtml( - ¬e, - &mut ids, - error_codes, - cx.shared.edition, - &cx.shared.playground, - ); - message.push_str(&format!(": {}", html.into_string())); - } - stability.push(format!( - "<div class='stab deprecated'><span class='emoji'>👎</span> {}</div>", - message, - )); - } - - // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). - // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. - if let Some(stab) = item.stability.as_ref().filter(|stab| { - stab.level == stability::Unstable && stab.feature.as_deref() != Some("rustc_private") - }) { - let mut message = - "<span class='emoji'>🔬</span> This is a nightly-only experimental API.".to_owned(); - - if let Some(feature) = stab.feature.as_deref() { - let mut feature = format!("<code>{}</code>", Escape(&feature)); - if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) { - feature.push_str(&format!( - " <a href=\"{url}{issue}\">#{issue}</a>", - url = url, - issue = issue - )); - } - - message.push_str(&format!(" ({})", feature)); - } - - if let Some(unstable_reason) = &stab.unstable_reason { - let mut ids = cx.id_map.borrow_mut(); - message = format!( - "<details><summary>{}</summary>{}</details>", - message, - MarkdownHtml( - &unstable_reason, - &mut ids, - error_codes, - cx.shared.edition, - &cx.shared.playground, - ) - .into_string() - ); - } - - stability.push(format!("<div class='stab unstable'>{}</div>", message)); - } - - if let Some(ref cfg) = item.attrs.cfg { - stability.push(format!("<div class='stab portability'>{}</div>", cfg.render_long_html())); - } - - stability -} - -fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) { - write!(w, "<pre class='rust const'>"); - render_attributes(w, it, false); - - write!( - w, - "{vis}const \ - {name}: {typ}", - vis = it.visibility.print_with_space(), - name = it.name.as_ref().unwrap(), - typ = c.type_.print(), - ); - - if c.value.is_some() || c.is_literal { - write!(w, " = {expr};", expr = Escape(&c.expr)); - } else { - write!(w, ";"); - } - - if let Some(value) = &c.value { - if !c.is_literal { - let value_lowercase = value.to_lowercase(); - let expr_lowercase = c.expr.to_lowercase(); - - if value_lowercase != expr_lowercase - && value_lowercase.trim_end_matches("i32") != expr_lowercase - { - write!(w, " // {value}", value = Escape(value)); - } - } - } - - write!(w, "</pre>"); - document(w, cx, it) -} - -fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static) { - write!(w, "<pre class='rust static'>"); - render_attributes(w, it, false); - write!( - w, - "{vis}static {mutability}\ - {name}: {typ}</pre>", - vis = it.visibility.print_with_space(), - mutability = s.mutability.print_with_space(), - name = it.name.as_ref().unwrap(), - typ = s.type_.print() - ); - document(w, cx, it) -} - -fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Function) { - let header_len = format!( - "{}{}{}{}{:#}fn {}{:#}", - it.visibility.print_with_space(), - f.header.constness.print_with_space(), - f.header.asyncness.print_with_space(), - f.header.unsafety.print_with_space(), - print_abi_with_space(f.header.abi), - it.name.as_ref().unwrap(), - f.generics.print() - ) - .len(); - write!(w, "<pre class='rust fn'>"); - render_attributes(w, it, false); - write!( - w, - "{vis}{constness}{asyncness}{unsafety}{abi}fn \ - {name}{generics}{decl}{spotlight}{where_clause}</pre>", - vis = it.visibility.print_with_space(), - constness = f.header.constness.print_with_space(), - asyncness = f.header.asyncness.print_with_space(), - unsafety = f.header.unsafety.print_with_space(), - abi = print_abi_with_space(f.header.abi), - name = it.name.as_ref().unwrap(), - generics = f.generics.print(), - where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, - decl = Function { decl: &f.decl, header_len, indent: 0, asyncness: f.header.asyncness } - .print(), - spotlight = spotlight_decl(&f.decl), - ); - document(w, cx, it) -} - -fn render_implementor( - cx: &Context, - implementor: &Impl, - w: &mut Buffer, - implementor_dups: &FxHashMap<&str, (DefId, bool)>, - aliases: &[String], -) { - // If there's already another implementor that has the same abbridged name, use the - // full path, for example in `std::iter::ExactSizeIterator` - let use_absolute = match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, is_generic: false, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, - .. - } => implementor_dups[path.last_name()].1, - _ => false, - }; - render_impl( - w, - cx, - implementor, - AssocItemLink::Anchor(None), - RenderMode::Normal, - implementor.impl_item.stable_since(), - false, - Some(use_absolute), - false, - false, - aliases, - ); -} - -fn render_impls(cx: &Context, w: &mut Buffer, traits: &[&&Impl], containing_item: &clean::Item) { - let mut impls = traits - .iter() - .map(|i| { - let did = i.trait_did().unwrap(); - let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); - let mut buffer = if w.is_for_html() { Buffer::html() } else { Buffer::new() }; - render_impl( - &mut buffer, - cx, - i, - assoc_link, - RenderMode::Normal, - containing_item.stable_since(), - true, - None, - false, - true, - &[], - ); - buffer.into_inner() - }) - .collect::<Vec<_>>(); - impls.sort(); - w.write_str(&impls.join("")); -} - -fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String { - let mut bounds = String::new(); - if !t_bounds.is_empty() { - if !trait_alias { - bounds.push_str(": "); - } - for (i, p) in t_bounds.iter().enumerate() { - if i > 0 { - bounds.push_str(" + "); - } - bounds.push_str(&p.print().to_string()); - } - } - bounds -} - -fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl) -> Ordering { - let lhs = format!("{}", lhs.inner_impl().print()); - let rhs = format!("{}", rhs.inner_impl().print()); - - // lhs and rhs are formatted as HTML, which may be unnecessary - name_key(&lhs).cmp(&name_key(&rhs)) -} - -fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait) { - 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<_>>(); - let provided = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>(); - - // Output the trait definition - wrap_into_docblock(w, |w| { - write!(w, "<pre class='rust trait'>"); - render_attributes(w, it, true); - write!( - w, - "{}{}{}trait {}{}{}", - it.visibility.print_with_space(), - t.unsafety.print_with_space(), - if t.is_auto { "auto " } else { "" }, - it.name.as_ref().unwrap(), - t.generics.print(), - bounds - ); - - if !t.generics.where_predicates.is_empty() { - write!(w, "{}", WhereClause { gens: &t.generics, indent: 0, end_newline: true }); - } else { - write!(w, " "); - } - - if t.items.is_empty() { - write!(w, "{{ }}"); - } else { - // FIXME: we should be using a derived_id for the Anchors here - write!(w, "{{\n"); - for t in &types { - render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait); - write!(w, ";\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); - write!(w, ";\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); - write!(w, ";\n"); - - if pos < required.len() - 1 { - write!(w, "<div class='item-spacer'></div>"); - } - } - if !required.is_empty() && !provided.is_empty() { - w.write_str("\n"); - } - for (pos, m) in provided.iter().enumerate() { - render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait); - match m.inner { - clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => { - write!(w, ",\n {{ ... }}\n"); - } - _ => { - write!(w, " {{ ... }}\n"); - } - } - if pos < provided.len() - 1 { - write!(w, "<div class='item-spacer'></div>"); - } - } - write!(w, "}}"); - } - write!(w, "</pre>") - }); - - // Trait documentation - document(w, cx, it); - - fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { - 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 Buffer, extra_content: &str) { - write!(w, "{}<span class='loading-content'>Loading content...</span>", extra_content) - } - - fn trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) { - let name = m.name.as_ref().unwrap(); - let item_type = m.type_(); - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h3 id='{id}' class='method'><code>", id = id,); - render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl); - write!(w, "</code>"); - render_stability_since(w, m, t); - write!(w, "</h3>"); - document(w, cx, m); - } - - if !types.is_empty() { - write_small_section_header( - w, - "associated-types", - "Associated Types", - "<div class='methods'>", - ); - for t in &types { - trait_item(w, cx, *t, it); - } - write_loading_content(w, "</div>"); - } - - if !consts.is_empty() { - write_small_section_header( - w, - "associated-const", - "Associated Constants", - "<div class='methods'>", - ); - for t in &consts { - trait_item(w, cx, *t, it); - } - write_loading_content(w, "</div>"); - } - - // Output the documentation for each function individually - if !required.is_empty() { - write_small_section_header( - w, - "required-methods", - "Required methods", - "<div class='methods'>", - ); - for m in &required { - trait_item(w, cx, *m, it); - } - write_loading_content(w, "</div>"); - } - if !provided.is_empty() { - write_small_section_header( - w, - "provided-methods", - "Provided methods", - "<div class='methods'>", - ); - for m in &provided { - trait_item(w, cx, *m, it); - } - 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); - - if let Some(implementors) = cx.cache.implementors.get(&it.def_id) { - // The DefId is for the first Type found with that name. The bool is - // if any Types with the same name but different DefId have been found. - let mut implementor_dups: FxHashMap<&str, (DefId, bool)> = FxHashMap::default(); - for implementor in implementors { - match implementor.inner_impl().for_ { - clean::ResolvedPath { ref path, did, is_generic: false, .. } - | clean::BorrowedRef { - type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, - .. - } => { - let &mut (prev_did, ref mut has_duplicates) = - implementor_dups.entry(path.last_name()).or_insert((did, false)); - if prev_did != did { - *has_duplicates = true; - } - } - _ => {} - } - } - - let (local, foreign) = implementors.iter().partition::<Vec<_>, _>(|i| { - i.inner_impl().for_.def_id().map_or(true, |d| cx.cache.paths.contains_key(&d)) - }); - - let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = - local.iter().partition(|i| i.inner_impl().synthetic); - - synthetic.sort_by(compare_impl); - concrete.sort_by(compare_impl); - - if !foreign.is_empty() { - write_small_section_header(w, "foreign-impls", "Implementations on Foreign Types", ""); - - for implementor in foreign { - let assoc_link = AssocItemLink::GotoSource( - implementor.impl_item.def_id, - &implementor.inner_impl().provided_trait_methods, - ); - render_impl( - w, - cx, - &implementor, - assoc_link, - RenderMode::Normal, - implementor.impl_item.stable_since(), - false, - None, - true, - false, - &[], - ); - } - write_loading_content(w, ""); - } - - 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_loading_content(w, "</div>"); - - if t.auto { - write_small_section_header( - w, - "synthetic-implementors", - "Auto implementors", - "<div class='item-list' id='synthetic-implementors-list'>", - ); - for implementor in synthetic { - render_implementor( - cx, - implementor, - w, - &implementor_dups, - &collect_paths_for_type(implementor.inner_impl().for_.clone()), - ); - } - 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_small_section_header( - w, - "implementors", - "Implementors", - "<div class='item-list' id='implementors-list'>", - ); - write_loading_content(w, "</div>"); - - if t.auto { - write_small_section_header( - w, - "synthetic-implementors", - "Auto implementors", - "<div class='item-list' id='synthetic-implementors-list'>", - ); - write_loading_content(w, "</div>"); - } - } - - write!( - w, - "<script type=\"text/javascript\" \ - src=\"{root_path}/implementors/{path}/{ty}.{name}.js\" async>\ - </script>", - root_path = vec![".."; cx.current.len()].join("/"), - path = if it.def_id.is_local() { - cx.current.join("/") - } else { - let (ref path, _) = cx.cache.external_paths[&it.def_id]; - path[..path.len() - 1].join("/") - }, - ty = it.type_(), - name = *it.name.as_ref().unwrap() - ); -} - -fn naive_assoc_href(it: &clean::Item, link: AssocItemLink<'_>) -> String { - use crate::html::item_type::ItemType::*; - - let name = it.name.as_ref().unwrap(); - let ty = match it.type_() { - Typedef | AssocType => AssocType, - s => s, - }; - - let anchor = format!("#{}.{}", ty, name); - match link { - AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), - AssocItemLink::Anchor(None) => anchor, - AssocItemLink::GotoSource(did, _) => { - href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) - } - } -} - -fn assoc_const( - w: &mut Buffer, - it: &clean::Item, - ty: &clean::Type, - _default: Option<&String>, - link: AssocItemLink<'_>, - extra: &str, -) { - write!( - w, - "{}{}const <a href='{}' class=\"constant\"><b>{}</b></a>: {}", - extra, - it.visibility.print_with_space(), - naive_assoc_href(it, link), - it.name.as_ref().unwrap(), - ty.print() - ); -} - -fn assoc_type( - w: &mut Buffer, - it: &clean::Item, - bounds: &[clean::GenericBound], - default: Option<&clean::Type>, - link: AssocItemLink<'_>, - extra: &str, -) { - write!( - w, - "{}type <a href='{}' class=\"type\">{}</a>", - extra, - naive_assoc_href(it, link), - it.name.as_ref().unwrap() - ); - if !bounds.is_empty() { - write!(w, ": {}", print_generic_bounds(bounds)) - } - if let Some(default) = default { - write!(w, " = {}", default.print()) - } -} - -fn render_stability_since_raw(w: &mut Buffer, ver: Option<&str>, containing_ver: Option<&str>) { - if let Some(v) = ver { - if containing_ver != ver && !v.is_empty() { - write!(w, "<span class='since' title='Stable since Rust version {0}'>{0}</span>", v) - } - } -} - -fn render_stability_since(w: &mut Buffer, item: &clean::Item, containing_item: &clean::Item) { - render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) -} - -fn render_assoc_item( - w: &mut Buffer, - item: &clean::Item, - link: AssocItemLink<'_>, - parent: ItemType, -) { - fn method( - w: &mut Buffer, - meth: &clean::Item, - header: hir::FnHeader, - g: &clean::Generics, - d: &clean::FnDecl, - link: AssocItemLink<'_>, - parent: ItemType, - ) { - let name = meth.name.as_ref().unwrap(); - let anchor = format!("#{}.{}", meth.type_(), name); - let href = match link { - AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), - AssocItemLink::Anchor(None) => anchor, - AssocItemLink::GotoSource(did, provided_methods) => { - // We're creating a link from an impl-item to the corresponding - // trait-item and need to map the anchored type accordingly. - let ty = if provided_methods.contains(name) { - ItemType::Method - } else { - ItemType::TyMethod - }; - - href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) - } - }; - let mut header_len = format!( - "{}{}{}{}{}{:#}fn {}{:#}", - meth.visibility.print_with_space(), - header.constness.print_with_space(), - header.asyncness.print_with_space(), - header.unsafety.print_with_space(), - print_default_space(meth.is_default()), - print_abi_with_space(header.abi), - name, - g.print() - ) - .len(); - let (indent, end_newline) = if parent == ItemType::Trait { - header_len += 4; - (4, false) - } else { - (0, true) - }; - render_attributes(w, meth, false); - write!( - w, - "{}{}{}{}{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\ - {generics}{decl}{spotlight}{where_clause}", - if parent == ItemType::Trait { " " } else { "" }, - meth.visibility.print_with_space(), - header.constness.print_with_space(), - header.asyncness.print_with_space(), - header.unsafety.print_with_space(), - print_default_space(meth.is_default()), - print_abi_with_space(header.abi), - href = href, - name = name, - generics = g.print(), - decl = Function { decl: d, header_len, indent, asyncness: header.asyncness }.print(), - spotlight = spotlight_decl(&d), - where_clause = WhereClause { gens: g, indent, end_newline } - ) - } - match item.inner { - clean::StrippedItem(..) => {} - clean::TyMethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), - clean::MethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent), - clean::AssocConstItem(ref ty, ref default) => assoc_const( - w, - item, - ty, - default.as_ref(), - link, - if parent == ItemType::Trait { " " } else { "" }, - ), - clean::AssocTypeItem(ref bounds, ref default) => assoc_type( - w, - item, - bounds, - default.as_ref(), - link, - if parent == ItemType::Trait { " " } else { "" }, - ), - _ => panic!("render_assoc_item called on non-associated-item"), - } -} - -fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct) { - wrap_into_docblock(w, |w| { - write!(w, "<pre class='rust struct'>"); - render_attributes(w, it, true); - render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true); - write!(w, "</pre>") - }); - - document(w, cx, it); - let mut fields = s - .fields - .iter() - .filter_map(|f| match f.inner { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if let doctree::Plain = s.struct_type { - if fields.peek().is_some() { - write!( - w, - "<h2 id='fields' class='fields small-section-header'> - Fields{}<a href='#fields' class='anchor'></a></h2>", - document_non_exhaustive_header(it) - ); - document_non_exhaustive(w, it); - for (field, ty) in fields { - let id = cx.derive_id(format!( - "{}.{}", - ItemType::StructField, - field.name.as_ref().unwrap() - )); - write!( - w, - "<span id=\"{id}\" class=\"{item_type} small-section-header\">\ - <a href=\"#{id}\" class=\"anchor field\"></a>\ - <code>{name}: {ty}</code>\ - </span>", - item_type = ItemType::StructField, - id = id, - name = field.name.as_ref().unwrap(), - ty = ty.print() - ); - document(w, cx, field); - } - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union) { - wrap_into_docblock(w, |w| { - write!(w, "<pre class='rust union'>"); - render_attributes(w, it, true); - render_union(w, it, Some(&s.generics), &s.fields, "", true); - write!(w, "</pre>") - }); - - document(w, cx, it); - let mut fields = s - .fields - .iter() - .filter_map(|f| match f.inner { - clean::StructFieldItem(ref ty) => Some((f, ty)), - _ => None, - }) - .peekable(); - if fields.peek().is_some() { - write!( - w, - "<h2 id='fields' class='fields small-section-header'> - Fields<a href='#fields' class='anchor'></a></h2>" - ); - for (field, ty) in fields { - let name = field.name.as_ref().expect("union field name"); - let id = format!("{}.{}", ItemType::StructField, name); - write!( - w, - "<span id=\"{id}\" class=\"{shortty} small-section-header\">\ - <a href=\"#{id}\" class=\"anchor field\"></a>\ - <code>{name}: {ty}</code>\ - </span>", - id = id, - name = name, - shortty = ItemType::StructField, - ty = ty.print() - ); - if let Some(stability_class) = field.stability_class() { - write!(w, "<span class='stab {stab}'></span>", stab = stability_class); - } - document(w, cx, field); - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) { - wrap_into_docblock(w, |w| { - write!(w, "<pre class='rust enum'>"); - render_attributes(w, it, true); - write!( - w, - "{}enum {}{}{}", - it.visibility.print_with_space(), - it.name.as_ref().unwrap(), - e.generics.print(), - WhereClause { gens: &e.generics, indent: 0, end_newline: true } - ); - if e.variants.is_empty() && !e.variants_stripped { - write!(w, " {{}}"); - } else { - write!(w, " {{\n"); - for v in &e.variants { - write!(w, " "); - let name = v.name.as_ref().unwrap(); - match v.inner { - clean::VariantItem(ref var) => match var.kind { - clean::VariantKind::CLike => write!(w, "{}", name), - clean::VariantKind::Tuple(ref tys) => { - write!(w, "{}(", name); - for (i, ty) in tys.iter().enumerate() { - if i > 0 { - write!(w, ", ") - } - write!(w, "{}", ty.print()); - } - write!(w, ")"); - } - clean::VariantKind::Struct(ref s) => { - render_struct(w, v, None, s.struct_type, &s.fields, " ", false); - } - }, - _ => unreachable!(), - } - write!(w, ",\n"); - } - - if e.variants_stripped { - write!(w, " // some variants omitted\n"); - } - write!(w, "}}"); - } - write!(w, "</pre>") - }); - - document(w, cx, it); - if !e.variants.is_empty() { - write!( - w, - "<h2 id='variants' class='variants small-section-header'> - Variants{}<a href='#variants' class='anchor'></a></h2>\n", - document_non_exhaustive_header(it) - ); - document_non_exhaustive(w, it); - for variant in &e.variants { - let id = - cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.as_ref().unwrap())); - write!( - w, - "<div id=\"{id}\" class=\"variant small-section-header\">\ - <a href=\"#{id}\" class=\"anchor field\"></a>\ - <code>{name}", - id = id, - name = variant.name.as_ref().unwrap() - ); - if let clean::VariantItem(ref var) = variant.inner { - if let clean::VariantKind::Tuple(ref tys) = var.kind { - write!(w, "("); - for (i, ty) in tys.iter().enumerate() { - if i > 0 { - write!(w, ", "); - } - write!(w, "{}", ty.print()); - } - write!(w, ")"); - } - } - write!(w, "</code></div>"); - document(w, cx, variant); - document_non_exhaustive(w, variant); - - use crate::clean::{Variant, VariantKind}; - if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.inner - { - let variant_id = cx.derive_id(format!( - "{}.{}.fields", - ItemType::Variant, - variant.name.as_ref().unwrap() - )); - write!(w, "<div class='autohide sub-variant' id='{id}'>", id = variant_id); - write!( - w, - "<h3>Fields of <b>{name}</b></h3><div>", - name = variant.name.as_ref().unwrap() - ); - for field in &s.fields { - use crate::clean::StructFieldItem; - if let StructFieldItem(ref ty) = field.inner { - let id = cx.derive_id(format!( - "variant.{}.field.{}", - variant.name.as_ref().unwrap(), - field.name.as_ref().unwrap() - )); - write!( - w, - "<span id=\"{id}\" class=\"variant small-section-header\">\ - <a href=\"#{id}\" class=\"anchor field\"></a>\ - <code>{f}: {t}\ - </code></span>", - id = id, - f = field.name.as_ref().unwrap(), - t = ty.print() - ); - document(w, cx, field); - } - } - write!(w, "</div></div>"); - } - render_stability_since(w, variant, it); - } - } - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -const ALLOWED_ATTRIBUTES: &[Symbol] = &[ - sym::export_name, - sym::lang, - sym::link_section, - sym::must_use, - sym::no_mangle, - sym::repr, - sym::non_exhaustive, -]; - -// The `top` parameter is used when generating the item declaration to ensure it doesn't have a -// left padding. For example: -// -// #[foo] <----- "top" attribute -// struct Foo { -// #[bar] <---- not "top" attribute -// bar: usize, -// } -fn render_attributes(w: &mut Buffer, it: &clean::Item, top: bool) { - let attrs = it - .attrs - .other_attrs - .iter() - .filter_map(|attr| { - if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { - Some(pprust::attribute_to_string(&attr)) - } else { - None - } - }) - .join("\n"); - - if !attrs.is_empty() { - write!( - w, - "<span class=\"docblock attributes{}\">{}</span>", - if top { " top-attr" } else { "" }, - &attrs - ); - } -} - -fn render_struct( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - ty: doctree::StructType, - fields: &[clean::Item], - tab: &str, - structhead: bool, -) { - write!( - w, - "{}{}{}", - it.visibility.print_with_space(), - if structhead { "struct " } else { "" }, - it.name.as_ref().unwrap() - ); - if let Some(g) = g { - write!(w, "{}", g.print()) - } - match ty { - doctree::Plain => { - if let Some(g) = g { - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }) - } - let mut has_visible_fields = false; - write!(w, " {{"); - for field in fields { - if let clean::StructFieldItem(ref ty) = field.inner { - write!( - w, - "\n{} {}{}: {},", - tab, - field.visibility.print_with_space(), - field.name.as_ref().unwrap(), - ty.print() - ); - has_visible_fields = true; - } - } - - if has_visible_fields { - if it.has_stripped_fields().unwrap() { - write!(w, "\n{} // some fields omitted", tab); - } - write!(w, "\n{}", tab); - } else if it.has_stripped_fields().unwrap() { - // If there are no visible fields we can just display - // `{ /* fields omitted */ }` to save space. - write!(w, " /* fields omitted */ "); - } - write!(w, "}}"); - } - doctree::Tuple => { - write!(w, "("); - for (i, field) in fields.iter().enumerate() { - if i > 0 { - write!(w, ", "); - } - match field.inner { - clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), - clean::StructFieldItem(ref ty) => { - write!(w, "{}{}", field.visibility.print_with_space(), ty.print()) - } - _ => unreachable!(), - } - } - write!(w, ")"); - if let Some(g) = g { - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false }) - } - write!(w, ";"); - } - doctree::Unit => { - // Needed for PhantomData. - if let Some(g) = g { - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false }) - } - write!(w, ";"); - } - } -} - -fn render_union( - w: &mut Buffer, - it: &clean::Item, - g: Option<&clean::Generics>, - fields: &[clean::Item], - tab: &str, - structhead: bool, -) { - write!( - w, - "{}{}{}", - it.visibility.print_with_space(), - if structhead { "union " } else { "" }, - it.name.as_ref().unwrap() - ); - if let Some(g) = g { - write!(w, "{}", g.print()); - write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true }); - } - - write!(w, " {{\n{}", tab); - for field in fields { - if let clean::StructFieldItem(ref ty) = field.inner { - write!( - w, - " {}{}: {},\n{}", - field.visibility.print_with_space(), - field.name.as_ref().unwrap(), - ty.print(), - tab - ); - } - } - - if it.has_stripped_fields().unwrap() { - write!(w, " // some fields omitted\n{}", tab); - } - write!(w, "}}"); -} - -#[derive(Copy, Clone)] -enum AssocItemLink<'a> { - Anchor(Option<&'a str>), - GotoSource(DefId, &'a FxHashSet<String>), -} - -impl<'a> AssocItemLink<'a> { - fn anchor(&self, id: &'a String) -> Self { - match *self { - AssocItemLink::Anchor(_) => AssocItemLink::Anchor(Some(&id)), - ref other => *other, - } - } -} - -enum AssocItemRender<'a> { - All, - DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool }, -} - -#[derive(Copy, Clone, PartialEq)] -enum RenderMode { - Normal, - ForDeref { mut_: bool }, -} - -fn render_assoc_items( - w: &mut Buffer, - cx: &Context, - containing_item: &clean::Item, - it: DefId, - what: AssocItemRender<'_>, -) { - let c = &cx.cache; - let v = match c.impls.get(&it) { - Some(v) => v, - None => return, - }; - let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); - if !non_trait.is_empty() { - let render_mode = match what { - AssocItemRender::All => { - write!( - w, - "\ - <h2 id='implementations' class='small-section-header'>\ - Implementations<a href='#implementations' class='anchor'></a>\ - </h2>\ - " - ); - RenderMode::Normal - } - AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { - write!( - w, - "\ - <h2 id='deref-methods' class='small-section-header'>\ - Methods from {}<Target = {}>\ - <a href='#deref-methods' class='anchor'></a>\ - </h2>\ - ", - trait_.print(), - type_.print() - ); - RenderMode::ForDeref { mut_: deref_mut_ } - } - }; - for i in &non_trait { - render_impl( - w, - cx, - i, - AssocItemLink::Anchor(None), - render_mode, - containing_item.stable_since(), - true, - None, - false, - true, - &[], - ); - } - } - if let AssocItemRender::DerefFor { .. } = what { - return; - } - if !traits.is_empty() { - let deref_impl = - traits.iter().find(|t| t.inner_impl().trait_.def_id() == c.deref_trait_did); - if let Some(impl_) = deref_impl { - let has_deref_mut = - traits.iter().any(|t| t.inner_impl().trait_.def_id() == c.deref_mut_trait_did); - render_deref_methods(w, cx, impl_, containing_item, has_deref_mut); - } - - let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = - traits.iter().partition(|t| t.inner_impl().synthetic); - let (blanket_impl, concrete): (Vec<&&Impl>, _) = - concrete.into_iter().partition(|t| t.inner_impl().blanket_impl.is_some()); - - let mut impls = Buffer::empty_from(&w); - render_impls(cx, &mut impls, &concrete, containing_item); - let impls = impls.into_inner(); - if !impls.is_empty() { - write!( - w, - "\ - <h2 id='trait-implementations' class='small-section-header'>\ - Trait Implementations<a href='#trait-implementations' class='anchor'></a>\ - </h2>\ - <div id='trait-implementations-list'>{}</div>", - impls - ); - } - - if !synthetic.is_empty() { - write!( - w, - "\ - <h2 id='synthetic-implementations' class='small-section-header'>\ - Auto Trait Implementations\ - <a href='#synthetic-implementations' class='anchor'></a>\ - </h2>\ - <div id='synthetic-implementations-list'>\ - " - ); - render_impls(cx, w, &synthetic, containing_item); - write!(w, "</div>"); - } - - if !blanket_impl.is_empty() { - write!( - w, - "\ - <h2 id='blanket-implementations' class='small-section-header'>\ - Blanket Implementations\ - <a href='#blanket-implementations' class='anchor'></a>\ - </h2>\ - <div id='blanket-implementations-list'>\ - " - ); - render_impls(cx, w, &blanket_impl, containing_item); - write!(w, "</div>"); - } - } -} - -fn render_deref_methods( - w: &mut Buffer, - cx: &Context, - impl_: &Impl, - container_item: &clean::Item, - deref_mut: bool, -) { - let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); - let (target, real_target) = impl_ - .inner_impl() - .items - .iter() - .find_map(|item| match item.inner { - clean::TypedefItem(ref t, true) => Some(match *t { - clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), - }), - _ => None, - }) - .expect("Expected associated type binding"); - let what = - AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; - if let Some(did) = target.def_id() { - render_assoc_items(w, cx, container_item, did, what); - } else { - if let Some(prim) = target.primitive_type() { - if let Some(&did) = cx.cache.primitive_locations.get(&prim) { - render_assoc_items(w, cx, container_item, did, what); - } - } - } -} - -fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { - let self_type_opt = match item.inner { - clean::MethodItem(ref method) => method.decl.self_type(), - clean::TyMethodItem(ref method) => method.decl.self_type(), - _ => None, - }; - - if let Some(self_ty) = self_type_opt { - let (by_mut_ref, by_box, by_value) = match self_ty { - SelfTy::SelfBorrowed(_, mutability) - | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { - (mutability == Mutability::Mut, false, false) - } - SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => { - (false, Some(did) == cache().owned_box_did, false) - } - SelfTy::SelfValue => (false, false, true), - _ => (false, false, false), - }; - - (deref_mut_ || !by_mut_ref) && !by_box && !by_value - } else { - false - } -} - -fn spotlight_decl(decl: &clean::FnDecl) -> String { - let mut out = Buffer::html(); - let mut trait_ = String::new(); - - if let Some(did) = decl.output.def_id() { - let c = cache(); - if let Some(impls) = c.impls.get(&did) { - for i in impls { - let impl_ = i.inner_impl(); - if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) { - if out.is_empty() { - out.push_str(&format!( - "<h3 class=\"important\">Important traits for {}</h3>\ - <code class=\"content\">", - impl_.for_.print() - )); - trait_.push_str(&impl_.for_.print().to_string()); - } - - //use the "where" class here to make it small - out.push_str(&format!( - "<span class=\"where fmt-newline\">{}</span>", - impl_.print() - )); - let t_did = impl_.trait_.def_id().unwrap(); - for it in &impl_.items { - if let clean::TypedefItem(ref tydef, _) = it.inner { - out.push_str("<span class=\"where fmt-newline\"> "); - assoc_type( - &mut out, - it, - &[], - Some(&tydef.type_), - AssocItemLink::GotoSource(t_did, &FxHashSet::default()), - "", - ); - out.push_str(";</span>"); - } - } - } - } - } - } - - if !out.is_empty() { - out.insert_str( - 0, - "<span class=\"important-traits\"><span class=\"important-traits-tooltip\">ⓘ<div class='important-traits-tooltiptext'><span class=\"docblock\">" - - ); - out.push_str("</code></span></div></span></span>"); - } - - out.into_inner() -} - -fn render_impl( - w: &mut Buffer, - cx: &Context, - i: &Impl, - link: AssocItemLink<'_>, - render_mode: RenderMode, - outer_version: Option<&str>, - show_def_docs: bool, - use_absolute: Option<bool>, - is_on_foreign_type: bool, - show_default_items: bool, - // This argument is used to reference same type with different paths to avoid duplication - // in documentation pages for trait with automatic implementations like "Send" and "Sync". - aliases: &[String], -) { - if render_mode == RenderMode::Normal { - let id = cx.derive_id(match i.inner_impl().trait_ { - Some(ref t) => { - if is_on_foreign_type { - get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t) - } else { - format!("impl-{}", small_url_encode(&format!("{:#}", t.print()))) - } - } - None => "impl".to_string(), - }); - let aliases = if aliases.is_empty() { - String::new() - } else { - format!(" aliases=\"{}\"", aliases.join(",")) - }; - if let Some(use_absolute) = use_absolute { - write!(w, "<h3 id='{}' class='impl'{}><code class='in-band'>", id, aliases); - fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute); - if show_def_docs { - for it in &i.inner_impl().items { - if let clean::TypedefItem(ref tydef, _) = it.inner { - write!(w, "<span class=\"where fmt-newline\"> "); - assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), ""); - write!(w, ";</span>"); - } - } - } - write!(w, "</code>"); - } else { - write!( - w, - "<h3 id='{}' class='impl'{}><code class='in-band'>{}</code>", - id, - aliases, - i.inner_impl().print() - ); - } - write!(w, "<a href='#{}' class='anchor'></a>", id); - let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]); - render_stability_since_raw(w, since, outer_version); - if let Some(l) = cx.src_href(&i.impl_item) { - write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", l, "goto source code"); - } - write!(w, "</h3>"); - if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) { - let mut ids = cx.id_map.borrow_mut(); - write!( - w, - "<div class='docblock'>{}</div>", - Markdown( - &*dox, - &i.impl_item.links(), - &mut ids, - cx.shared.codes, - cx.shared.edition, - &cx.shared.playground - ) - .into_string() - ); - } - } - - fn doc_impl_item( - w: &mut Buffer, - cx: &Context, - item: &clean::Item, - link: AssocItemLink<'_>, - render_mode: RenderMode, - is_default_item: bool, - outer_version: Option<&str>, - trait_: Option<&clean::Trait>, - show_def_docs: bool, - ) { - let item_type = item.type_(); - let name = item.name.as_ref().unwrap(); - - let render_method_item = match render_mode { - RenderMode::Normal => true, - RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_), - }; - - let (is_hidden, extra_class) = - if (trait_.is_none() || item.doc_value().is_some() || item.inner.is_associated()) - && !is_default_item - { - (false, "") - } else { - (true, " hidden") - }; - match item.inner { - clean::MethodItem(clean::Method { .. }) - | clean::TyMethodItem(clean::TyMethod { .. }) => { - // Only render when the method is not static or we allow static methods - if render_method_item { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class); - write!(w, "<code>"); - render_assoc_item(w, item, link.anchor(&id), ItemType::Impl); - write!(w, "</code>"); - render_stability_since_raw(w, item.stable_since(), outer_version); - if let Some(l) = cx.src_href(item) { - write!( - w, - "<a class='srclink' href='{}' title='{}'>[src]</a>", - l, "goto source code" - ); - } - write!(w, "</h4>"); - } - } - clean::TypedefItem(ref tydef, _) => { - let id = cx.derive_id(format!("{}.{}", ItemType::AssocType, name)); - write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class); - assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id), ""); - write!(w, "</code></h4>"); - } - clean::AssocConstItem(ref ty, ref default) => { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class); - assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), ""); - write!(w, "</code>"); - render_stability_since_raw(w, item.stable_since(), outer_version); - if let Some(l) = cx.src_href(item) { - write!( - w, - "<a class='srclink' href='{}' title='{}'>[src]</a>", - l, "goto source code" - ); - } - write!(w, "</h4>"); - } - clean::AssocTypeItem(ref bounds, ref default) => { - let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "<h4 id='{}' class=\"{}{}\"><code>", id, item_type, extra_class); - assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id), ""); - write!(w, "</code></h4>"); - } - clean::StrippedItem(..) => return, - _ => panic!("can't make docs for trait item with name {:?}", item.name), - } - - if render_method_item { - if !is_default_item { - if let Some(t) = trait_ { - // The trait item may have been stripped so we might not - // find any documentation or stability for it. - if let Some(it) = t.items.iter().find(|i| i.name == item.name) { - // We need the stability of the item from the trait - // because impls can't have a stability. - document_stability(w, cx, it, is_hidden); - if item.doc_value().is_some() { - document_full(w, item, cx, "", is_hidden); - } else if show_def_docs { - // In case the item isn't documented, - // provide short documentation from the trait. - document_short(w, cx, it, link, "", is_hidden); - } - } - } else { - document_stability(w, cx, item, is_hidden); - if show_def_docs { - document_full(w, item, cx, "", is_hidden); - } - } - } else { - document_stability(w, cx, item, is_hidden); - if show_def_docs { - document_short(w, cx, item, link, "", is_hidden); - } - } - } - } - - let traits = &cx.cache.traits; - let trait_ = i.trait_did().map(|did| &traits[&did]); - - write!(w, "<div class='impl-items'>"); - for trait_item in &i.inner_impl().items { - doc_impl_item( - w, - cx, - trait_item, - link, - render_mode, - false, - outer_version, - trait_, - show_def_docs, - ); - } - - fn render_default_items( - w: &mut Buffer, - cx: &Context, - t: &clean::Trait, - i: &clean::Impl, - render_mode: RenderMode, - outer_version: Option<&str>, - show_def_docs: bool, - ) { - for trait_item in &t.items { - let n = trait_item.name.clone(); - if i.items.iter().any(|m| m.name == n) { - continue; - } - let did = i.trait_.as_ref().unwrap().def_id().unwrap(); - let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods); - - doc_impl_item( - w, - cx, - trait_item, - assoc_link, - render_mode, - true, - outer_version, - None, - show_def_docs, - ); - } - } - - // If we've implemented a trait, then also emit documentation for all - // default items which weren't overridden in the implementation block. - // We don't emit documentation for default items if they appear in the - // Implementations on Foreign Types or Implementors sections. - if show_default_items { - if let Some(t) = trait_ { - render_default_items( - w, - cx, - t, - &i.inner_impl(), - render_mode, - outer_version, - show_def_docs, - ); - } - } - write!(w, "</div>"); -} - -fn item_opaque_ty(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::OpaqueTy) { - write!(w, "<pre class='rust opaque'>"); - render_attributes(w, it, false); - write!( - w, - "type {}{}{where_clause} = impl {bounds};</pre>", - it.name.as_ref().unwrap(), - t.generics.print(), - where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, - 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 Buffer, cx: &Context, it: &clean::Item, t: &clean::TraitAlias) { - write!(w, "<pre class='rust trait-alias'>"); - render_attributes(w, it, false); - write!( - w, - "trait {}{}{} = {};</pre>", - it.name.as_ref().unwrap(), - t.generics.print(), - WhereClause { gens: &t.generics, indent: 0, end_newline: true }, - bounds(&t.bounds, true) - ); - - 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_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typedef) { - write!(w, "<pre class='rust typedef'>"); - render_attributes(w, it, false); - write!( - w, - "type {}{}{where_clause} = {type_};</pre>", - it.name.as_ref().unwrap(), - t.generics.print(), - where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, - type_ = t.type_.print() - ); - - 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_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item) { - writeln!(w, "<pre class='rust foreigntype'>extern {{"); - render_attributes(w, it, false); - write!( - w, - " {}type {};\n}}</pre>", - it.visibility.print_with_space(), - it.name.as_ref().unwrap(), - ); - - document(w, cx, it); - - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer) { - let parentlen = cx.current.len() - if it.is_mod() { 1 } else { 0 }; - - if it.is_struct() - || it.is_trait() - || it.is_primitive() - || it.is_union() - || it.is_enum() - || it.is_mod() - || it.is_typedef() - { - write!( - buffer, - "<p class='location'>{}{}</p>", - match it.inner { - clean::StructItem(..) => "Struct ", - clean::TraitItem(..) => "Trait ", - clean::PrimitiveItem(..) => "Primitive Type ", - clean::UnionItem(..) => "Union ", - clean::EnumItem(..) => "Enum ", - clean::TypedefItem(..) => "Type Definition ", - clean::ForeignTypeItem => "Foreign Type ", - clean::ModuleItem(..) => - if it.is_crate() { - "Crate " - } else { - "Module " - }, - _ => "", - }, - it.name.as_ref().unwrap() - ); - } - - if it.is_crate() { - if let Some(ref version) = cx.cache.crate_version { - write!( - buffer, - "<div class='block version'>\ - <p>Version {}</p>\ - </div>", - Escape(version) - ); - } - } - - write!(buffer, "<div class=\"sidebar-elems\">"); - if it.is_crate() { - write!( - buffer, - "<a id='all-types' href='all.html'><p>See all {}'s items</p></a>", - it.name.as_ref().expect("crates always have a name") - ); - } - match it.inner { - clean::StructItem(ref s) => sidebar_struct(buffer, it, s), - clean::TraitItem(ref t) => sidebar_trait(buffer, it, t), - clean::PrimitiveItem(_) => sidebar_primitive(buffer, it), - clean::UnionItem(ref u) => sidebar_union(buffer, it, u), - clean::EnumItem(ref e) => sidebar_enum(buffer, it, e), - clean::TypedefItem(_, _) => sidebar_typedef(buffer, it), - clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items), - clean::ForeignTypeItem => sidebar_foreign_type(buffer, it), - _ => (), - } - - // 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). - - write!(buffer, "<p class='location'>"); - for (i, name) in cx.current.iter().take(parentlen).enumerate() { - if i > 0 { - write!(buffer, "::<wbr>"); - } - write!( - buffer, - "<a href='{}index.html'>{}</a>", - &cx.root_path()[..(cx.current.len() - i - 1) * 3], - *name - ); - } - write!(buffer, "</p>"); - - // Sidebar refers to the enclosing module, not this module. - let relpath = if it.is_mod() { "../" } else { "" }; - write!( - buffer, - "<script>window.sidebarCurrent = {{\ - name: '{name}', \ - ty: '{ty}', \ - relpath: '{path}'\ - }};</script>", - name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""), - ty = it.type_(), - path = relpath - ); - if parentlen == 0 { - // There is no sidebar-items.js beyond the crate root path - // FIXME maybe dynamic crate loading can be merged here - } else { - write!(buffer, "<script defer src=\"{path}sidebar-items.js\"></script>", path = relpath); - } - // Closes sidebar-elems div. - write!(buffer, "</div>"); -} - -fn get_next_url(used_links: &mut FxHashSet<String>, 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( - i: &clean::Impl, - for_deref: bool, - used_links: &mut FxHashSet<String>, - deref_mut: bool, -) -> Vec<String> { - i.items - .iter() - .filter_map(|item| match item.name { - Some(ref name) if !name.is_empty() && item.is_method() => { - if !for_deref || should_render_item(item, deref_mut) { - Some(format!( - "<a href=\"#{}\">{}</a>", - get_next_url(used_links, format!("method.{}", name)), - name - )) - } else { - None - } - } - _ => None, - }) - .collect::<Vec<_>>() -} - -// 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 sidebar_assoc_items(it: &clean::Item) -> String { - let mut out = String::new(); - let c = cache(); - if let Some(v) = c.impls.get(&it.def_id) { - let mut used_links = FxHashSet::default(); - - { - let used_links_bor = &mut used_links; - let mut ret = v - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(move |i| get_methods(i.inner_impl(), false, used_links_bor, false)) - .collect::<Vec<_>>(); - if !ret.is_empty() { - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); - out.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#implementations\">Methods</a>\ - <div class=\"sidebar-links\">{}</div>", - ret.join("") - )); - } - } - - if v.iter().any(|i| i.inner_impl().trait_.is_some()) { - if let Some(impl_) = v - .iter() - .filter(|i| i.inner_impl().trait_.is_some()) - .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) - { - if let Some((target, real_target)) = - impl_.inner_impl().items.iter().find_map(|item| match item.inner { - clean::TypedefItem(ref t, true) => Some(match *t { - clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_), - _ => (&t.type_, &t.type_), - }), - _ => None, - }) - { - let deref_mut = v - .iter() - .filter(|i| i.inner_impl().trait_.is_some()) - .any(|i| i.inner_impl().trait_.def_id() == c.deref_mut_trait_did); - let inner_impl = target - .def_id() - .or(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 { - out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">"); - out.push_str(&format!( - "Methods from {}<Target={}>", - Escape(&format!( - "{:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print() - )), - Escape(&format!("{:#}", real_target.print())) - )); - out.push_str("</a>"); - let mut ret = impls - .iter() - .filter(|i| i.inner_impl().trait_.is_none()) - .flat_map(|i| { - get_methods(i.inner_impl(), true, &mut used_links, deref_mut) - }) - .collect::<Vec<_>>(); - // We want links' order to be reproducible so we don't use unstable sort. - ret.sort(); - if !ret.is_empty() { - out.push_str(&format!( - "<div class=\"sidebar-links\">{}</div>", - ret.join("") - )); - } - } - } - } - let format_impls = |impls: Vec<&Impl>| { - let mut links = FxHashSet::default(); - - let mut ret = impls - .iter() - .filter_map(|i| { - let is_negative_impl = is_negative_impl(i.inner_impl()); - if let Some(ref i) = i.inner_impl().trait_ { - let i_display = format!("{:#}", i.print()); - let out = Escape(&i_display); - let encoded = small_url_encode(&format!("{:#}", i.print())); - let generated = format!( - "<a href=\"#impl-{}\">{}{}</a>", - encoded, - if is_negative_impl { "!" } else { "" }, - out - ); - if links.insert(generated.clone()) { Some(generated) } else { None } - } else { - None - } - }) - .collect::<Vec<String>>(); - ret.sort(); - ret.join("") - }; - - let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = - v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().synthetic); - let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) = concrete - .into_iter() - .partition::<Vec<_>, _>(|i| i.inner_impl().blanket_impl.is_some()); - - let concrete_format = format_impls(concrete); - let synthetic_format = format_impls(synthetic); - let blanket_format = format_impls(blanket_impl); - - if !concrete_format.is_empty() { - out.push_str( - "<a class=\"sidebar-title\" href=\"#trait-implementations\">\ - Trait Implementations</a>", - ); - out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format)); - } - - if !synthetic_format.is_empty() { - out.push_str( - "<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\ - Auto Trait Implementations</a>", - ); - out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format)); - } - - if !blanket_format.is_empty() { - out.push_str( - "<a class=\"sidebar-title\" href=\"#blanket-implementations\">\ - Blanket Implementations</a>", - ); - out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", blanket_format)); - } - } - } - - out -} - -fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) { - let mut sidebar = String::new(); - let fields = get_struct_fields_name(&s.fields); - - if !fields.is_empty() { - if let doctree::Plain = s.struct_type { - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\ - <div class=\"sidebar-links\">{}</div>", - fields - )); - } - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\">{}</div>", sidebar); - } -} - -fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) -> String { - small_url_encode(&format!("impl-{:#}-for-{:#}", trait_.print(), for_.print())) -} - -fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> { - match item.inner { - clean::ItemEnum::ImplItem(ref i) => { - if let Some(ref trait_) = i.trait_ { - Some(( - format!("{:#}", i.for_.print()), - get_id_for_impl_on_foreign_type(&i.for_, trait_), - )) - } else { - None - } - } - _ => None, - } -} - -fn is_negative_impl(i: &clean::Impl) -> bool { - i.polarity == Some(clean::ImplPolarity::Negative) -} - -fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) { - let mut sidebar = String::new(); - - let mut types = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_associated_type() => { - Some(format!("<a href=\"#associatedtype.{name}\">{name}</a>", name = name)) - } - _ => None, - }) - .collect::<Vec<_>>(); - let mut consts = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_associated_const() => { - Some(format!("<a href=\"#associatedconstant.{name}\">{name}</a>", name = name)) - } - _ => None, - }) - .collect::<Vec<_>>(); - let mut required = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_ty_method() => { - Some(format!("<a href=\"#tymethod.{name}\">{name}</a>", name = name)) - } - _ => None, - }) - .collect::<Vec<String>>(); - let mut provided = t - .items - .iter() - .filter_map(|m| match m.name { - Some(ref name) if m.is_method() => { - Some(format!("<a href=\"#method.{0}\">{0}</a>", name)) - } - _ => None, - }) - .collect::<Vec<String>>(); - - if !types.is_empty() { - types.sort(); - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#associated-types\">\ - Associated Types</a><div class=\"sidebar-links\">{}</div>", - types.join("") - )); - } - if !consts.is_empty() { - consts.sort(); - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#associated-const\">\ - Associated Constants</a><div class=\"sidebar-links\">{}</div>", - consts.join("") - )); - } - if !required.is_empty() { - required.sort(); - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#required-methods\">\ - Required Methods</a><div class=\"sidebar-links\">{}</div>", - required.join("") - )); - } - if !provided.is_empty() { - provided.sort(); - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#provided-methods\">\ - Provided Methods</a><div class=\"sidebar-links\">{}</div>", - provided.join("") - )); - } - - let c = cache(); - - if let Some(implementors) = c.implementors.get(&it.def_id) { - let mut res = implementors - .iter() - .filter(|i| i.inner_impl().for_.def_id().map_or(false, |d| !c.paths.contains_key(&d))) - .filter_map(|i| extract_for_impl_name(&i.impl_item)) - .collect::<Vec<_>>(); - - if !res.is_empty() { - res.sort(); - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#foreign-impls\">\ - Implementations on Foreign Types</a><div \ - class=\"sidebar-links\">{}</div>", - res.into_iter() - .map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(&name))) - .collect::<Vec<_>>() - .join("") - )); - } - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>"); - if t.auto { - sidebar.push_str( - "<a class=\"sidebar-title\" \ - href=\"#synthetic-implementors\">Auto Implementors</a>", - ); - } - - write!(buf, "<div class=\"block items\">{}</div>", sidebar) -} - -fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); - - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\">{}</div>", sidebar); - } -} - -fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); - - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\">{}</div>", sidebar); - } -} - -fn get_struct_fields_name(fields: &[clean::Item]) -> String { - let mut fields = fields - .iter() - .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false }) - .filter_map(|f| match f.name { - Some(ref name) => { - Some(format!("<a href=\"#structfield.{name}\">{name}</a>", name = name)) - } - _ => None, - }) - .collect::<Vec<_>>(); - fields.sort(); - fields.join("") -} - -fn sidebar_union(buf: &mut Buffer, it: &clean::Item, u: &clean::Union) { - let mut sidebar = String::new(); - let fields = get_struct_fields_name(&u.fields); - - if !fields.is_empty() { - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\ - <div class=\"sidebar-links\">{}</div>", - fields - )); - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\">{}</div>", sidebar); - } -} - -fn sidebar_enum(buf: &mut Buffer, it: &clean::Item, e: &clean::Enum) { - let mut sidebar = String::new(); - - let mut variants = e - .variants - .iter() - .filter_map(|v| match v.name { - Some(ref name) => Some(format!("<a href=\"#variant.{name}\">{name}</a>", name = name)), - _ => None, - }) - .collect::<Vec<_>>(); - if !variants.is_empty() { - variants.sort_unstable(); - sidebar.push_str(&format!( - "<a class=\"sidebar-title\" href=\"#variants\">Variants</a>\ - <div class=\"sidebar-links\">{}</div>", - variants.join(""), - )); - } - - sidebar.push_str(&sidebar_assoc_items(it)); - - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\">{}</div>", sidebar); - } -} - -fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) { - match *ty { - ItemType::ExternCrate | ItemType::Import => ("reexports", "Re-exports"), - ItemType::Module => ("modules", "Modules"), - ItemType::Struct => ("structs", "Structs"), - ItemType::Union => ("unions", "Unions"), - ItemType::Enum => ("enums", "Enums"), - ItemType::Function => ("functions", "Functions"), - ItemType::Typedef => ("types", "Type Definitions"), - ItemType::Static => ("statics", "Statics"), - ItemType::Constant => ("constants", "Constants"), - ItemType::Trait => ("traits", "Traits"), - ItemType::Impl => ("impls", "Implementations"), - ItemType::TyMethod => ("tymethods", "Type Methods"), - ItemType::Method => ("methods", "Methods"), - ItemType::StructField => ("fields", "Struct Fields"), - ItemType::Variant => ("variants", "Variants"), - ItemType::Macro => ("macros", "Macros"), - ItemType::Primitive => ("primitives", "Primitive Types"), - ItemType::AssocType => ("associated-types", "Associated Types"), - ItemType::AssocConst => ("associated-consts", "Associated Constants"), - ItemType::ForeignType => ("foreign-types", "Foreign Types"), - ItemType::Keyword => ("keywords", "Keywords"), - ItemType::OpaqueTy => ("opaque-types", "Opaque Types"), - ItemType::ProcAttribute => ("attributes", "Attribute Macros"), - ItemType::ProcDerive => ("derives", "Derive Macros"), - ItemType::TraitAlias => ("trait-aliases", "Trait aliases"), - } -} - -fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { - let mut sidebar = String::new(); - - if items.iter().any(|it| it.type_() == ItemType::ExternCrate || it.type_() == ItemType::Import) - { - sidebar.push_str(&format!( - "<li><a href=\"#{id}\">{name}</a></li>", - id = "reexports", - name = "Re-exports" - )); - } - - // ordering taken from item_module, reorder, where it prioritized elements in a certain order - // to print its headings - for &myty in &[ - ItemType::Primitive, - ItemType::Module, - ItemType::Macro, - ItemType::Struct, - ItemType::Enum, - ItemType::Constant, - ItemType::Static, - ItemType::Trait, - ItemType::Function, - ItemType::Typedef, - ItemType::Union, - ItemType::Impl, - ItemType::TyMethod, - ItemType::Method, - ItemType::StructField, - ItemType::Variant, - ItemType::AssocType, - ItemType::AssocConst, - ItemType::ForeignType, - ItemType::Keyword, - ] { - if items.iter().any(|it| !it.is_stripped() && it.type_() == myty) { - let (short, name) = item_ty_to_strs(&myty); - sidebar.push_str(&format!( - "<li><a href=\"#{id}\">{name}</a></li>", - id = short, - name = name - )); - } - } - - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\"><ul>{}</ul></div>", sidebar); - } -} - -fn sidebar_foreign_type(buf: &mut Buffer, it: &clean::Item) { - let sidebar = sidebar_assoc_items(it); - if !sidebar.is_empty() { - write!(buf, "<div class=\"block items\">{}</div>", sidebar); - } -} - -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(), - Some("macro"), - None, - None, - )) - }); - document(w, cx, it) -} - -fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::ProcMacro) { - let name = it.name.as_ref().expect("proc-macros always have names"); - match m.kind { - MacroKind::Bang => { - write!(w, "<pre class='rust macro'>"); - write!(w, "{}!() {{ /* proc-macro */ }}", name); - write!(w, "</pre>"); - } - MacroKind::Attr => { - write!(w, "<pre class='rust attr'>"); - write!(w, "#[{}]", name); - write!(w, "</pre>"); - } - MacroKind::Derive => { - write!(w, "<pre class='rust derive'>"); - write!(w, "#[derive({})]", name); - if !m.helpers.is_empty() { - writeln!(w, "\n{{"); - writeln!(w, " // Attributes available to this derive:"); - for attr in &m.helpers { - writeln!(w, " #[{}]", attr); - } - write!(w, "}}"); - } - write!(w, "</pre>"); - } - } - document(w, cx, it) -} - -fn item_primitive(w: &mut Buffer, cx: &Context, it: &clean::Item) { - document(w, cx, it); - render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) -} - -fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) { - document(w, cx, it) -} - -crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; - -fn make_item_keywords(it: &clean::Item) -> String { - format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) -} - -/// 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 -/// types are re-exported, we don't use the corresponding -/// entry from the js file, as inlining will have already -/// picked up the impl -fn collect_paths_for_type(first_ty: clean::Type) -> Vec<String> { - let mut out = Vec::new(); - let mut visited = FxHashSet::default(); - let mut work = VecDeque::new(); - let cache = cache(); - - work.push_back(first_ty); - - while let Some(ty) = work.pop_front() { - if !visited.insert(ty.clone()) { - continue; - } - - match ty { - clean::Type::ResolvedPath { did, .. } => { - let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); - let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); - - if let Some(path) = fqp { - out.push(path.join("::")); - } - } - clean::Type::Tuple(tys) => { - work.extend(tys.into_iter()); - } - clean::Type::Slice(ty) => { - work.push_back(*ty); - } - clean::Type::Array(ty, _) => { - work.push_back(*ty); - } - clean::Type::RawPointer(_, ty) => { - work.push_back(*ty); - } - clean::Type::BorrowedRef { type_, .. } => { - work.push_back(*type_); - } - clean::Type::QPath { self_type, trait_, .. } => { - work.push_back(*self_type); - work.push_back(*trait_); - } - _ => {} - } - } - out -} - -crate fn cache() -> Arc<Cache> { - CACHE_KEY.with(|c| c.borrow().clone()) -} |
