]>
Commit | Line | Data |
---|---|---|
c1a9b12d | 1 | // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
2 | // file at the top-level directory of this distribution and at |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! Rustdoc's HTML Rendering module | |
12 | //! | |
13 | //! This modules contains the bulk of the logic necessary for rendering a | |
14 | //! rustdoc `clean::Crate` instance to a set of static HTML pages. This | |
15 | //! rendering process is largely driven by the `format!` syntax extension to | |
16 | //! perform all I/O into files and streams. | |
17 | //! | |
18 | //! The rendering process is largely driven by the `Context` and `Cache` | |
19 | //! structures. The cache is pre-populated by crawling the crate in question, | |
bd371182 | 20 | //! and then it is shared among the various rendering threads. The cache is meant |
1a4d82fc | 21 | //! to be a fairly large structure not implementing `Clone` (because it's shared |
bd371182 AL |
22 | //! among threads). The context, however, should be a lightweight structure. This |
23 | //! is cloned per-thread and contains information about what is currently being | |
1a4d82fc JJ |
24 | //! rendered. |
25 | //! | |
26 | //! In order to speed up rendering (mostly because of markdown rendering), the | |
27 | //! rendering process has been parallelized. This parallelization is only | |
28 | //! exposed through the `crate` method on the context, and then also from the | |
29 | //! fact that the shared cache is stored in TLS (and must be accessed as such). | |
30 | //! | |
31 | //! In addition to rendering the crate itself, this module is also responsible | |
32 | //! for creating the corresponding search index and source file renderings. | |
bd371182 | 33 | //! These threads are not parallelized (they haven't been a bottleneck yet), and |
1a4d82fc | 34 | //! both occur before the crate is rendered. |
0531ce1d | 35 | |
1a4d82fc JJ |
36 | pub use self::ExternalLocation::*; |
37 | ||
ff7c6d11 | 38 | use std::borrow::Cow; |
1a4d82fc | 39 | use std::cell::RefCell; |
85aaf69f | 40 | use std::cmp::Ordering; |
0531ce1d | 41 | use std::collections::{BTreeMap, HashSet, VecDeque}; |
1a4d82fc | 42 | use std::default::Default; |
b039eaaf | 43 | use std::error; |
476ff2be | 44 | use std::fmt::{self, Display, Formatter, Write as FmtWrite}; |
9e0c209e | 45 | use std::fs::{self, File, OpenOptions}; |
c34b1796 AL |
46 | use std::io::prelude::*; |
47 | use std::io::{self, BufWriter, BufReader}; | |
1a4d82fc | 48 | use std::iter::repeat; |
9346a6ac | 49 | use std::mem; |
7453a54e | 50 | use std::path::{PathBuf, Path, Component}; |
1a4d82fc JJ |
51 | use std::str; |
52 | use std::sync::Arc; | |
53 | ||
54 | use externalfiles::ExternalHtml; | |
55 | ||
7453a54e | 56 | use serialize::json::{ToJson, Json, as_json}; |
83c7162d | 57 | use syntax::ast; |
ff7c6d11 | 58 | use syntax::codemap::FileName; |
476ff2be | 59 | use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId}; |
92a42be0 | 60 | use rustc::middle::privacy::AccessLevels; |
b039eaaf | 61 | use rustc::middle::stability; |
54a0048b | 62 | use rustc::hir; |
476ff2be | 63 | use rustc::util::nodemap::{FxHashMap, FxHashSet}; |
9e0c209e | 64 | use rustc_data_structures::flock; |
83c7162d | 65 | use rustc_target::spec::abi; |
1a4d82fc | 66 | |
0531ce1d | 67 | use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability}; |
1a4d82fc JJ |
68 | use doctree; |
69 | use fold::DocFolder; | |
d9579d0f | 70 | use html::escape::Escape; |
62682a34 | 71 | use html::format::{ConstnessSpace}; |
d9579d0f AL |
72 | use html::format::{TyParamBounds, WhereClause, href, AbiSpace}; |
73 | use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; | |
a7813a04 | 74 | use html::format::fmt_impl_for_trait_page; |
1a4d82fc | 75 | use html::item_type::ItemType; |
0531ce1d | 76 | use html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine}; |
62682a34 | 77 | use html::{highlight, layout}; |
1a4d82fc | 78 | |
85aaf69f | 79 | /// A pair of name and its optional document. |
c34b1796 | 80 | pub type NameDoc = (String, Option<String>); |
85aaf69f | 81 | |
1a4d82fc JJ |
82 | /// Major driving force in all rustdoc rendering. This contains information |
83 | /// about where in the tree-like hierarchy rendering is occurring and controls | |
84 | /// how the current page is being rendered. | |
85 | /// | |
86 | /// It is intended that this context is a lightweight object which can be fairly | |
87 | /// easily cloned because it is cloned per work-job (about once per item in the | |
88 | /// rustdoc tree). | |
89 | #[derive(Clone)] | |
90 | pub struct Context { | |
91 | /// Current hierarchy of components leading down to what's currently being | |
92 | /// rendered | |
93 | pub current: Vec<String>, | |
1a4d82fc JJ |
94 | /// The current destination folder of where HTML artifacts should be placed. |
95 | /// This changes as the context descends into the module hierarchy. | |
c34b1796 | 96 | pub dst: PathBuf, |
54a0048b SL |
97 | /// A flag, which when `true`, will render pages which redirect to the |
98 | /// real location of an item. This is used to allow external links to | |
99 | /// publicly reused items to redirect to the right location. | |
100 | pub render_redirect_pages: bool, | |
101 | pub shared: Arc<SharedContext>, | |
102 | } | |
103 | ||
104 | pub struct SharedContext { | |
105 | /// The path to the crate root source minus the file name. | |
106 | /// Used for simplifying paths to the highlighted source code files. | |
107 | pub src_root: PathBuf, | |
1a4d82fc JJ |
108 | /// This describes the layout of each page, and is not modified after |
109 | /// creation of the context (contains info like the favicon and added html). | |
110 | pub layout: layout::Layout, | |
83c7162d | 111 | /// This flag indicates whether `[src]` links should be generated or not. If |
1a4d82fc JJ |
112 | /// the source files are present in the html rendering, then this will be |
113 | /// `true`. | |
114 | pub include_sources: bool, | |
54a0048b | 115 | /// The local file sources we've emitted and their respective url-paths. |
476ff2be | 116 | pub local_sources: FxHashMap<PathBuf, String>, |
1a4d82fc | 117 | /// All the passes that were run on this crate. |
476ff2be | 118 | pub passes: FxHashSet<String>, |
e9174d1e SL |
119 | /// The base-URL of the issue tracker for when an item has been tagged with |
120 | /// an issue number. | |
121 | pub issue_tracker_base_url: Option<String>, | |
54a0048b SL |
122 | /// The given user css file which allow to customize the generated |
123 | /// documentation theme. | |
124 | pub css_file_extension: Option<PathBuf>, | |
abe05a73 XL |
125 | /// The directories that have already been created in this doc run. Used to reduce the number |
126 | /// of spurious `create_dir_all` calls. | |
127 | pub created_dirs: RefCell<FxHashSet<PathBuf>>, | |
ff7c6d11 XL |
128 | /// This flag indicates whether listings of modules (in the side bar and documentation itself) |
129 | /// should be ordered alphabetically or in order of appearance (in the source code). | |
130 | pub sort_modules_alphabetically: bool, | |
2c00a5a8 XL |
131 | /// Additional themes to be added to the generated docs. |
132 | pub themes: Vec<PathBuf>, | |
0531ce1d XL |
133 | /// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes |
134 | /// "light-v2.css"). | |
135 | pub resource_suffix: String, | |
abe05a73 XL |
136 | } |
137 | ||
138 | impl SharedContext { | |
139 | fn ensure_dir(&self, dst: &Path) -> io::Result<()> { | |
140 | let mut dirs = self.created_dirs.borrow_mut(); | |
141 | if !dirs.contains(dst) { | |
142 | fs::create_dir_all(dst)?; | |
143 | dirs.insert(dst.to_path_buf()); | |
144 | } | |
145 | ||
146 | Ok(()) | |
147 | } | |
1a4d82fc JJ |
148 | } |
149 | ||
ff7c6d11 XL |
150 | impl SharedContext { |
151 | /// Returns whether the `collapse-docs` pass was run on this crate. | |
152 | pub fn was_collapsed(&self) -> bool { | |
153 | self.passes.contains("collapse-docs") | |
154 | } | |
155 | ||
156 | /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the | |
157 | /// `collapsed_doc_value` of the given item. | |
158 | pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<Cow<'a, str>> { | |
159 | if self.was_collapsed() { | |
160 | item.collapsed_doc_value().map(|s| s.into()) | |
161 | } else { | |
162 | item.doc_value().map(|s| s.into()) | |
163 | } | |
164 | } | |
165 | } | |
166 | ||
1a4d82fc JJ |
167 | /// Indicates where an external crate can be found. |
168 | pub enum ExternalLocation { | |
169 | /// Remote URL root of the external crate | |
170 | Remote(String), | |
171 | /// This external crate can be found in the local doc/ folder | |
172 | Local, | |
173 | /// The external crate could not be found. | |
174 | Unknown, | |
175 | } | |
176 | ||
2c00a5a8 | 177 | /// Metadata about implementations for a type or trait. |
1a4d82fc JJ |
178 | #[derive(Clone)] |
179 | pub struct Impl { | |
a7813a04 | 180 | pub impl_item: clean::Item, |
1a4d82fc JJ |
181 | } |
182 | ||
9346a6ac | 183 | impl Impl { |
a7813a04 XL |
184 | fn inner_impl(&self) -> &clean::Impl { |
185 | match self.impl_item.inner { | |
186 | clean::ImplItem(ref impl_) => impl_, | |
187 | _ => panic!("non-impl item found in impl") | |
188 | } | |
189 | } | |
190 | ||
e9174d1e | 191 | fn trait_did(&self) -> Option<DefId> { |
a7813a04 | 192 | self.inner_impl().trait_.def_id() |
9346a6ac AL |
193 | } |
194 | } | |
195 | ||
b039eaaf SL |
196 | #[derive(Debug)] |
197 | pub struct Error { | |
198 | file: PathBuf, | |
199 | error: io::Error, | |
200 | } | |
201 | ||
202 | impl error::Error for Error { | |
203 | fn description(&self) -> &str { | |
204 | self.error.description() | |
205 | } | |
206 | } | |
207 | ||
208 | impl Display for Error { | |
209 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { | |
210 | write!(f, "\"{}\": {}", self.file.display(), self.error) | |
211 | } | |
212 | } | |
213 | ||
214 | impl Error { | |
215 | pub fn new(e: io::Error, file: &Path) -> Error { | |
216 | Error { | |
217 | file: file.to_path_buf(), | |
218 | error: e, | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
2c00a5a8 XL |
223 | macro_rules! try_none { |
224 | ($e:expr, $file:expr) => ({ | |
225 | use std::io; | |
226 | match $e { | |
227 | Some(e) => e, | |
228 | None => return Err(Error::new(io::Error::new(io::ErrorKind::Other, "not found"), | |
229 | $file)) | |
230 | } | |
231 | }) | |
232 | } | |
233 | ||
b039eaaf SL |
234 | macro_rules! try_err { |
235 | ($e:expr, $file:expr) => ({ | |
236 | match $e { | |
237 | Ok(e) => e, | |
238 | Err(e) => return Err(Error::new(e, $file)), | |
239 | } | |
240 | }) | |
241 | } | |
242 | ||
1a4d82fc JJ |
243 | /// This cache is used to store information about the `clean::Crate` being |
244 | /// rendered in order to provide more useful documentation. This contains | |
245 | /// information like all implementors of a trait, all traits a type implements, | |
246 | /// documentation for all known traits, etc. | |
247 | /// | |
248 | /// This structure purposefully does not implement `Clone` because it's intended | |
249 | /// to be a fairly large and expensive structure to clone. Instead this adheres | |
250 | /// to `Send` so it may be stored in a `Arc` instance and shared among the various | |
bd371182 | 251 | /// rendering threads. |
1a4d82fc JJ |
252 | #[derive(Default)] |
253 | pub struct Cache { | |
254 | /// Mapping of typaram ids to the name of the type parameter. This is used | |
255 | /// when pretty-printing a type (so pretty printing doesn't have to | |
256 | /// painfully maintain a context like this) | |
476ff2be | 257 | pub typarams: FxHashMap<DefId, String>, |
1a4d82fc JJ |
258 | |
259 | /// Maps a type id to all known implementations for that type. This is only | |
260 | /// recognized for intra-crate `ResolvedPath` types, and is used to print | |
261 | /// out extra documentation on the page of an enum/struct. | |
262 | /// | |
263 | /// The values of the map are a list of implementations and documentation | |
264 | /// found on that implementation. | |
476ff2be | 265 | pub impls: FxHashMap<DefId, Vec<Impl>>, |
1a4d82fc JJ |
266 | |
267 | /// Maintains a mapping of local crate node ids to the fully qualified name | |
268 | /// and "short type description" of that node. This is used when generating | |
269 | /// URLs when a type is being linked to. External paths are not located in | |
270 | /// this map because the `External` type itself has all the information | |
271 | /// necessary. | |
476ff2be | 272 | pub paths: FxHashMap<DefId, (Vec<String>, ItemType)>, |
1a4d82fc JJ |
273 | |
274 | /// Similar to `paths`, but only holds external paths. This is only used for | |
275 | /// generating explicit hyperlinks to other crates. | |
476ff2be | 276 | pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>, |
1a4d82fc | 277 | |
0531ce1d XL |
278 | /// Maps local def ids of exported types to fully qualified paths. |
279 | /// Unlike 'paths', this mapping ignores any renames that occur | |
280 | /// due to 'use' statements. | |
281 | /// | |
282 | /// This map is used when writing out the special 'implementors' | |
283 | /// javascript file. By using the exact path that the type | |
284 | /// is declared with, we ensure that each path will be identical | |
285 | /// to the path used if the corresponding type is inlined. By | |
286 | /// doing this, we can detect duplicate impls on a trait page, and only display | |
287 | /// the impl for the inlined type. | |
288 | pub exact_paths: FxHashMap<DefId, Vec<String>>, | |
289 | ||
1a4d82fc JJ |
290 | /// This map contains information about all known traits of this crate. |
291 | /// Implementations of a crate should inherit the documentation of the | |
292 | /// parent trait if no extra documentation is specified, and default methods | |
293 | /// should show up in documentation about trait implementations. | |
476ff2be | 294 | pub traits: FxHashMap<DefId, clean::Trait>, |
1a4d82fc JJ |
295 | |
296 | /// When rendering traits, it's often useful to be able to list all | |
297 | /// implementors of the trait, and this mapping is exactly, that: a mapping | |
298 | /// of trait ids to the list of known implementors of the trait | |
2c00a5a8 | 299 | pub implementors: FxHashMap<DefId, Vec<Impl>>, |
1a4d82fc JJ |
300 | |
301 | /// Cache of where external crate documentation can be found. | |
476ff2be | 302 | pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>, |
1a4d82fc JJ |
303 | |
304 | /// Cache of where documentation for primitives can be found. | |
476ff2be | 305 | pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>, |
1a4d82fc | 306 | |
a7813a04 XL |
307 | // Note that external items for which `doc(hidden)` applies to are shown as |
308 | // non-reachable while local items aren't. This is because we're reusing | |
309 | // the access levels from crateanalysis. | |
310 | pub access_levels: Arc<AccessLevels<DefId>>, | |
311 | ||
abe05a73 XL |
312 | /// The version of the crate being documented, if given fron the `--crate-version` flag. |
313 | pub crate_version: Option<String>, | |
314 | ||
1a4d82fc JJ |
315 | // Private fields only used when initially crawling a crate to build a cache |
316 | ||
317 | stack: Vec<String>, | |
e9174d1e | 318 | parent_stack: Vec<DefId>, |
7453a54e | 319 | parent_is_trait_impl: bool, |
1a4d82fc | 320 | search_index: Vec<IndexItem>, |
54a0048b | 321 | stripped_mod: bool, |
e9174d1e | 322 | deref_trait_did: Option<DefId>, |
9e0c209e | 323 | deref_mut_trait_did: Option<DefId>, |
041b39d2 | 324 | owned_box_did: Option<DefId>, |
ea8adc8c | 325 | masked_crates: FxHashSet<CrateNum>, |
1a4d82fc JJ |
326 | |
327 | // In rare case where a structure is defined in one module but implemented | |
328 | // in another, if the implementing module is parsed before defining module, | |
329 | // then the fully qualified name of the structure isn't presented in `paths` | |
330 | // yet when its implementation methods are being indexed. Caches such methods | |
331 | // and their parent id here and indexes them at the end of crate parsing. | |
9e0c209e | 332 | orphan_impl_items: Vec<(DefId, clean::Item)>, |
83c7162d XL |
333 | |
334 | /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, | |
335 | /// we need the alias element to have an array of items. | |
336 | aliases: FxHashMap<String, Vec<IndexItem>>, | |
1a4d82fc JJ |
337 | } |
338 | ||
a7813a04 XL |
339 | /// Temporary storage for data obtained during `RustdocVisitor::clean()`. |
340 | /// Later on moved into `CACHE_KEY`. | |
341 | #[derive(Default)] | |
342 | pub struct RenderInfo { | |
476ff2be | 343 | pub inlined: FxHashSet<DefId>, |
a7813a04 | 344 | pub external_paths: ::core::ExternalPaths, |
476ff2be | 345 | pub external_typarams: FxHashMap<DefId, String>, |
0531ce1d | 346 | pub exact_paths: FxHashMap<DefId, Vec<String>>, |
a7813a04 | 347 | pub deref_trait_did: Option<DefId>, |
9e0c209e | 348 | pub deref_mut_trait_did: Option<DefId>, |
041b39d2 | 349 | pub owned_box_did: Option<DefId>, |
a7813a04 XL |
350 | } |
351 | ||
1a4d82fc JJ |
352 | /// Helper struct to render all source code to HTML pages |
353 | struct SourceCollector<'a> { | |
54a0048b | 354 | scx: &'a mut SharedContext, |
1a4d82fc | 355 | |
1a4d82fc | 356 | /// Root destination to place all HTML output into |
c34b1796 | 357 | dst: PathBuf, |
1a4d82fc JJ |
358 | } |
359 | ||
360 | /// Wrapper struct to render the source code of a file. This will do things like | |
361 | /// adding line numbers to the left-hand side. | |
362 | struct Source<'a>(&'a str); | |
363 | ||
364 | // Helper structs for rendering items/sidebars and carrying along contextual | |
365 | // information | |
366 | ||
c34b1796 | 367 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
368 | struct Item<'a> { |
369 | cx: &'a Context, | |
370 | item: &'a clean::Item, | |
371 | } | |
372 | ||
373 | struct Sidebar<'a> { cx: &'a Context, item: &'a clean::Item, } | |
374 | ||
375 | /// Struct representing one entry in the JS search index. These are all emitted | |
376 | /// by hand to a large JS file at the end of cache-creation. | |
83c7162d | 377 | #[derive(Debug)] |
1a4d82fc JJ |
378 | struct IndexItem { |
379 | ty: ItemType, | |
380 | name: String, | |
381 | path: String, | |
382 | desc: String, | |
e9174d1e | 383 | parent: Option<DefId>, |
7453a54e | 384 | parent_idx: Option<usize>, |
c34b1796 AL |
385 | search_type: Option<IndexItemFunctionType>, |
386 | } | |
387 | ||
7453a54e SL |
388 | impl ToJson for IndexItem { |
389 | fn to_json(&self) -> Json { | |
390 | assert_eq!(self.parent.is_some(), self.parent_idx.is_some()); | |
391 | ||
392 | let mut data = Vec::with_capacity(6); | |
393 | data.push((self.ty as usize).to_json()); | |
394 | data.push(self.name.to_json()); | |
395 | data.push(self.path.to_json()); | |
396 | data.push(self.desc.to_json()); | |
397 | data.push(self.parent_idx.to_json()); | |
398 | data.push(self.search_type.to_json()); | |
399 | ||
400 | Json::Array(data) | |
401 | } | |
402 | } | |
403 | ||
c34b1796 | 404 | /// A type used for the search index. |
83c7162d | 405 | #[derive(Debug)] |
c34b1796 AL |
406 | struct Type { |
407 | name: Option<String>, | |
abe05a73 | 408 | generics: Option<Vec<String>>, |
c34b1796 AL |
409 | } |
410 | ||
7453a54e SL |
411 | impl ToJson for Type { |
412 | fn to_json(&self) -> Json { | |
c34b1796 | 413 | match self.name { |
7453a54e SL |
414 | Some(ref name) => { |
415 | let mut data = BTreeMap::new(); | |
416 | data.insert("name".to_owned(), name.to_json()); | |
abe05a73 XL |
417 | if let Some(ref generics) = self.generics { |
418 | data.insert("generics".to_owned(), generics.to_json()); | |
419 | } | |
7453a54e SL |
420 | Json::Object(data) |
421 | }, | |
422 | None => Json::Null | |
c34b1796 AL |
423 | } |
424 | } | |
425 | } | |
426 | ||
427 | /// Full type of functions/methods in the search index. | |
83c7162d | 428 | #[derive(Debug)] |
c34b1796 AL |
429 | struct IndexItemFunctionType { |
430 | inputs: Vec<Type>, | |
83c7162d | 431 | output: Option<Type>, |
c34b1796 AL |
432 | } |
433 | ||
7453a54e SL |
434 | impl ToJson for IndexItemFunctionType { |
435 | fn to_json(&self) -> Json { | |
c34b1796 | 436 | // If we couldn't figure out a type, just write `null`. |
7453a54e SL |
437 | if self.inputs.iter().chain(self.output.iter()).any(|ref i| i.name.is_none()) { |
438 | Json::Null | |
439 | } else { | |
440 | let mut data = BTreeMap::new(); | |
441 | data.insert("inputs".to_owned(), self.inputs.to_json()); | |
442 | data.insert("output".to_owned(), self.output.to_json()); | |
443 | Json::Object(data) | |
c34b1796 | 444 | } |
c34b1796 | 445 | } |
1a4d82fc JJ |
446 | } |
447 | ||
1a4d82fc | 448 | thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default()); |
0531ce1d XL |
449 | thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> = RefCell::new(Vec::new())); |
450 | thread_local!(pub static USED_ID_MAP: RefCell<FxHashMap<String, usize>> = RefCell::new(init_ids())); | |
ff7c6d11 | 451 | |
476ff2be | 452 | fn init_ids() -> FxHashMap<String, usize> { |
92a42be0 SL |
453 | [ |
454 | "main", | |
455 | "search", | |
456 | "help", | |
457 | "TOC", | |
458 | "render-detail", | |
459 | "associated-types", | |
460 | "associated-const", | |
461 | "required-methods", | |
462 | "provided-methods", | |
463 | "implementors", | |
0531ce1d | 464 | "synthetic-implementors", |
92a42be0 | 465 | "implementors-list", |
0531ce1d | 466 | "synthetic-implementors-list", |
92a42be0 SL |
467 | "methods", |
468 | "deref-methods", | |
469 | "implementations", | |
abe05a73 | 470 | ].into_iter().map(|id| (String::from(*id), 1)).collect() |
92a42be0 SL |
471 | } |
472 | ||
473 | /// This method resets the local table of used ID attributes. This is typically | |
474 | /// used at the beginning of rendering an entire HTML page to reset from the | |
475 | /// previous state (if any). | |
54a0048b SL |
476 | pub fn reset_ids(embedded: bool) { |
477 | USED_ID_MAP.with(|s| { | |
478 | *s.borrow_mut() = if embedded { | |
479 | init_ids() | |
480 | } else { | |
476ff2be | 481 | FxHashMap() |
54a0048b SL |
482 | }; |
483 | }); | |
92a42be0 SL |
484 | } |
485 | ||
486 | pub fn derive_id(candidate: String) -> String { | |
487 | USED_ID_MAP.with(|map| { | |
488 | let id = match map.borrow_mut().get_mut(&candidate) { | |
489 | None => candidate, | |
490 | Some(a) => { | |
491 | let id = format!("{}-{}", candidate, *a); | |
492 | *a += 1; | |
493 | id | |
494 | } | |
495 | }; | |
496 | ||
497 | map.borrow_mut().insert(id.clone(), 1); | |
498 | id | |
499 | }) | |
500 | } | |
1a4d82fc JJ |
501 | |
502 | /// Generates the documentation for `crate` into the directory `dst` | |
503 | pub fn run(mut krate: clean::Crate, | |
504 | external_html: &ExternalHtml, | |
476ff2be | 505 | playground_url: Option<String>, |
c34b1796 | 506 | dst: PathBuf, |
0531ce1d | 507 | resource_suffix: String, |
476ff2be | 508 | passes: FxHashSet<String>, |
a7813a04 | 509 | css_file_extension: Option<PathBuf>, |
cc61c64b | 510 | renderinfo: RenderInfo, |
ff7c6d11 | 511 | sort_modules_alphabetically: bool, |
2c00a5a8 | 512 | themes: Vec<PathBuf>) -> Result<(), Error> { |
ff7c6d11 XL |
513 | let src_root = match krate.src { |
514 | FileName::Real(ref p) => match p.parent() { | |
515 | Some(p) => p.to_path_buf(), | |
516 | None => PathBuf::new(), | |
517 | }, | |
518 | _ => PathBuf::new(), | |
c34b1796 | 519 | }; |
54a0048b | 520 | let mut scx = SharedContext { |
3b2f2976 XL |
521 | src_root, |
522 | passes, | |
54a0048b | 523 | include_sources: true, |
476ff2be | 524 | local_sources: FxHashMap(), |
54a0048b | 525 | issue_tracker_base_url: None, |
1a4d82fc JJ |
526 | layout: layout::Layout { |
527 | logo: "".to_string(), | |
528 | favicon: "".to_string(), | |
529 | external_html: external_html.clone(), | |
530 | krate: krate.name.clone(), | |
1a4d82fc | 531 | }, |
54a0048b | 532 | css_file_extension: css_file_extension.clone(), |
abe05a73 | 533 | created_dirs: RefCell::new(FxHashSet()), |
ff7c6d11 | 534 | sort_modules_alphabetically, |
2c00a5a8 | 535 | themes, |
0531ce1d | 536 | resource_suffix, |
1a4d82fc JJ |
537 | }; |
538 | ||
476ff2be SL |
539 | // If user passed in `--playground-url` arg, we fill in crate name here |
540 | if let Some(url) = playground_url { | |
541 | markdown::PLAYGROUND.with(|slot| { | |
542 | *slot.borrow_mut() = Some((Some(krate.name.clone()), url)); | |
543 | }); | |
544 | } | |
545 | ||
1a4d82fc JJ |
546 | // Crawl the crate attributes looking for attributes which control how we're |
547 | // going to emit HTML | |
476ff2be SL |
548 | if let Some(attrs) = krate.module.as_ref().map(|m| &m.attrs) { |
549 | for attr in attrs.lists("doc") { | |
550 | let name = attr.name().map(|s| s.as_str()); | |
551 | match (name.as_ref().map(|s| &s[..]), attr.value_str()) { | |
552 | (Some("html_favicon_url"), Some(s)) => { | |
54a0048b SL |
553 | scx.layout.favicon = s.to_string(); |
554 | } | |
476ff2be | 555 | (Some("html_logo_url"), Some(s)) => { |
54a0048b SL |
556 | scx.layout.logo = s.to_string(); |
557 | } | |
476ff2be | 558 | (Some("html_playground_url"), Some(s)) => { |
c30ab7b3 | 559 | markdown::PLAYGROUND.with(|slot| { |
476ff2be SL |
560 | let name = krate.name.clone(); |
561 | *slot.borrow_mut() = Some((Some(name), s.to_string())); | |
54a0048b | 562 | }); |
1a4d82fc | 563 | } |
476ff2be | 564 | (Some("issue_tracker_base_url"), Some(s)) => { |
54a0048b SL |
565 | scx.issue_tracker_base_url = Some(s.to_string()); |
566 | } | |
476ff2be | 567 | (Some("html_no_source"), None) if attr.is_word() => { |
54a0048b SL |
568 | scx.include_sources = false; |
569 | } | |
570 | _ => {} | |
1a4d82fc JJ |
571 | } |
572 | } | |
1a4d82fc | 573 | } |
041b39d2 | 574 | try_err!(fs::create_dir_all(&dst), &dst); |
54a0048b SL |
575 | krate = render_sources(&dst, &mut scx, krate)?; |
576 | let cx = Context { | |
577 | current: Vec::new(), | |
3b2f2976 | 578 | dst, |
54a0048b SL |
579 | render_redirect_pages: false, |
580 | shared: Arc::new(scx), | |
581 | }; | |
1a4d82fc JJ |
582 | |
583 | // Crawl the crate to build various caches used for the output | |
a7813a04 | 584 | let RenderInfo { |
5bcae85e | 585 | inlined: _, |
a7813a04 XL |
586 | external_paths, |
587 | external_typarams, | |
0531ce1d | 588 | exact_paths, |
a7813a04 | 589 | deref_trait_did, |
9e0c209e | 590 | deref_mut_trait_did, |
041b39d2 | 591 | owned_box_did, |
a7813a04 XL |
592 | } = renderinfo; |
593 | ||
5bcae85e | 594 | let external_paths = external_paths.into_iter() |
9e0c209e | 595 | .map(|(k, (v, t))| (k, (v, ItemType::from(t)))) |
5bcae85e | 596 | .collect(); |
a7813a04 | 597 | |
1a4d82fc | 598 | let mut cache = Cache { |
476ff2be | 599 | impls: FxHashMap(), |
3b2f2976 | 600 | external_paths, |
0531ce1d | 601 | exact_paths, |
476ff2be SL |
602 | paths: FxHashMap(), |
603 | implementors: FxHashMap(), | |
1a4d82fc JJ |
604 | stack: Vec::new(), |
605 | parent_stack: Vec::new(), | |
606 | search_index: Vec::new(), | |
7453a54e | 607 | parent_is_trait_impl: false, |
476ff2be SL |
608 | extern_locations: FxHashMap(), |
609 | primitive_locations: FxHashMap(), | |
54a0048b | 610 | stripped_mod: false, |
a7813a04 | 611 | access_levels: krate.access_levels.clone(), |
abe05a73 | 612 | crate_version: krate.version.take(), |
9e0c209e | 613 | orphan_impl_items: Vec::new(), |
476ff2be | 614 | traits: mem::replace(&mut krate.external_traits, FxHashMap()), |
3b2f2976 XL |
615 | deref_trait_did, |
616 | deref_mut_trait_did, | |
617 | owned_box_did, | |
ea8adc8c | 618 | masked_crates: mem::replace(&mut krate.masked_crates, FxHashSet()), |
a7813a04 | 619 | typarams: external_typarams, |
83c7162d | 620 | aliases: FxHashMap(), |
1a4d82fc | 621 | }; |
1a4d82fc JJ |
622 | |
623 | // Cache where all our extern crates are located | |
85aaf69f | 624 | for &(n, ref e) in &krate.externs { |
ff7c6d11 XL |
625 | let src_root = match e.src { |
626 | FileName::Real(ref p) => match p.parent() { | |
627 | Some(p) => p.to_path_buf(), | |
628 | None => PathBuf::new(), | |
629 | }, | |
630 | _ => PathBuf::new(), | |
476ff2be SL |
631 | }; |
632 | cache.extern_locations.insert(n, (e.name.clone(), src_root, | |
d9579d0f | 633 | extern_location(e, &cx.dst))); |
476ff2be | 634 | |
b039eaaf | 635 | let did = DefId { krate: n, index: CRATE_DEF_INDEX }; |
5bcae85e | 636 | cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); |
1a4d82fc JJ |
637 | } |
638 | ||
639 | // Cache where all known primitives have their documentation located. | |
640 | // | |
641 | // Favor linking to as local extern as possible, so iterate all crates in | |
642 | // reverse topological order. | |
476ff2be SL |
643 | for &(_, ref e) in krate.externs.iter().rev() { |
644 | for &(def_id, prim, _) in &e.primitives { | |
645 | cache.primitive_locations.insert(prim, def_id); | |
1a4d82fc JJ |
646 | } |
647 | } | |
476ff2be SL |
648 | for &(def_id, prim, _) in &krate.primitives { |
649 | cache.primitive_locations.insert(prim, def_id); | |
1a4d82fc JJ |
650 | } |
651 | ||
d9579d0f AL |
652 | cache.stack.push(krate.name.clone()); |
653 | krate = cache.fold_crate(krate); | |
654 | ||
1a4d82fc | 655 | // Build our search index |
b039eaaf | 656 | let index = build_index(&krate, &mut cache); |
1a4d82fc JJ |
657 | |
658 | // Freeze the cache now that the index has been built. Put an Arc into TLS | |
659 | // for future parallelization opportunities | |
660 | let cache = Arc::new(cache); | |
661 | CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); | |
662 | CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear()); | |
663 | ||
54a0048b | 664 | write_shared(&cx, &krate, &*cache, index)?; |
1a4d82fc | 665 | |
1a4d82fc | 666 | // And finally render the whole crate's documentation |
0531ce1d | 667 | cx.krate(krate) |
1a4d82fc JJ |
668 | } |
669 | ||
7453a54e | 670 | /// Build the search index from the collected metadata |
b039eaaf | 671 | fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { |
476ff2be | 672 | let mut nodeid_to_pathid = FxHashMap(); |
7453a54e SL |
673 | let mut crate_items = Vec::with_capacity(cache.search_index.len()); |
674 | let mut crate_paths = Vec::<Json>::new(); | |
675 | ||
676 | let Cache { ref mut search_index, | |
9e0c209e | 677 | ref orphan_impl_items, |
7453a54e SL |
678 | ref mut paths, .. } = *cache; |
679 | ||
9e0c209e | 680 | // Attach all orphan items to the type's definition if the type |
7453a54e | 681 | // has since been learned. |
9e0c209e | 682 | for &(did, ref item) in orphan_impl_items { |
3157f602 XL |
683 | if let Some(&(ref fqp, _)) = paths.get(&did) { |
684 | search_index.push(IndexItem { | |
c30ab7b3 | 685 | ty: item.type_(), |
3157f602 XL |
686 | name: item.name.clone().unwrap(), |
687 | path: fqp[..fqp.len() - 1].join("::"), | |
32a655c1 | 688 | desc: plain_summary_line(item.doc_value()), |
3157f602 XL |
689 | parent: Some(did), |
690 | parent_idx: None, | |
691 | search_type: get_index_search_type(&item), | |
692 | }); | |
7453a54e SL |
693 | } |
694 | } | |
695 | ||
696 | // Reduce `NodeId` in paths into smaller sequential numbers, | |
697 | // and prune the paths that do not appear in the index. | |
698 | let mut lastpath = String::new(); | |
699 | let mut lastpathid = 0usize; | |
700 | ||
701 | for item in search_index { | |
702 | item.parent_idx = item.parent.map(|nodeid| { | |
703 | if nodeid_to_pathid.contains_key(&nodeid) { | |
704 | *nodeid_to_pathid.get(&nodeid).unwrap() | |
705 | } else { | |
706 | let pathid = lastpathid; | |
707 | nodeid_to_pathid.insert(nodeid, pathid); | |
708 | lastpathid += 1; | |
1a4d82fc | 709 | |
7453a54e SL |
710 | let &(ref fqp, short) = paths.get(&nodeid).unwrap(); |
711 | crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json()); | |
712 | pathid | |
1a4d82fc | 713 | } |
7453a54e | 714 | }); |
1a4d82fc | 715 | |
7453a54e | 716 | // Omit the parent path if it is same to that of the prior item. |
1a4d82fc | 717 | if lastpath == item.path { |
7453a54e | 718 | item.path.clear(); |
1a4d82fc | 719 | } else { |
7453a54e | 720 | lastpath = item.path.clone(); |
1a4d82fc | 721 | } |
7453a54e | 722 | crate_items.push(item.to_json()); |
1a4d82fc JJ |
723 | } |
724 | ||
7453a54e | 725 | let crate_doc = krate.module.as_ref().map(|module| { |
32a655c1 | 726 | plain_summary_line(module.doc_value()) |
7453a54e | 727 | }).unwrap_or(String::new()); |
1a4d82fc | 728 | |
7453a54e SL |
729 | let mut crate_data = BTreeMap::new(); |
730 | crate_data.insert("doc".to_owned(), Json::String(crate_doc)); | |
731 | crate_data.insert("items".to_owned(), Json::Array(crate_items)); | |
732 | crate_data.insert("paths".to_owned(), Json::Array(crate_paths)); | |
1a4d82fc | 733 | |
7453a54e SL |
734 | // Collect the index into a string |
735 | format!("searchIndex[{}] = {};", | |
736 | as_json(&krate.name), | |
737 | Json::Object(crate_data)) | |
1a4d82fc JJ |
738 | } |
739 | ||
740 | fn write_shared(cx: &Context, | |
741 | krate: &clean::Crate, | |
742 | cache: &Cache, | |
b039eaaf | 743 | search_index: String) -> Result<(), Error> { |
1a4d82fc JJ |
744 | // Write out the shared files. Note that these are shared among all rustdoc |
745 | // docs placed in the output directory, so this needs to be a synchronized | |
746 | // operation with respect to all other rustdocs running around. | |
9e0c209e | 747 | let _lock = flock::Lock::panicking_new(&cx.dst.join(".lock"), true, true, true); |
1a4d82fc JJ |
748 | |
749 | // Add all the static files. These may already exist, but we just | |
750 | // overwrite them anyway to make sure that they're fresh and up-to-date. | |
54a0048b | 751 | |
0531ce1d | 752 | write(cx.dst.join(&format!("rustdoc{}.css", cx.shared.resource_suffix)), |
54a0048b | 753 | include_bytes!("static/rustdoc.css"))?; |
83c7162d XL |
754 | write(cx.dst.join(&format!("settings{}.css", cx.shared.resource_suffix)), |
755 | include_bytes!("static/settings.css"))?; | |
2c00a5a8 | 756 | |
0531ce1d | 757 | // To avoid "light.css" to be overwritten, we'll first run over the received themes and only |
2c00a5a8 XL |
758 | // then we'll run over the "official" styles. |
759 | let mut themes: HashSet<String> = HashSet::new(); | |
760 | ||
761 | for entry in &cx.shared.themes { | |
762 | let mut content = Vec::with_capacity(100000); | |
763 | ||
764 | let mut f = try_err!(File::open(&entry), &entry); | |
765 | try_err!(f.read_to_end(&mut content), &entry); | |
0531ce1d XL |
766 | let theme = try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry); |
767 | let extension = try_none!(try_none!(entry.extension(), &entry).to_str(), &entry); | |
768 | write(cx.dst.join(format!("{}{}.{}", theme, cx.shared.resource_suffix, extension)), | |
769 | content.as_slice())?; | |
770 | themes.insert(theme.to_owned()); | |
2c00a5a8 XL |
771 | } |
772 | ||
0531ce1d | 773 | write(cx.dst.join(&format!("brush{}.svg", cx.shared.resource_suffix)), |
2c00a5a8 | 774 | include_bytes!("static/brush.svg"))?; |
83c7162d XL |
775 | write(cx.dst.join(&format!("wheel{}.svg", cx.shared.resource_suffix)), |
776 | include_bytes!("static/wheel.svg"))?; | |
0531ce1d XL |
777 | write(cx.dst.join(&format!("light{}.css", cx.shared.resource_suffix)), |
778 | include_bytes!("static/themes/light.css"))?; | |
779 | themes.insert("light".to_owned()); | |
780 | write(cx.dst.join(&format!("dark{}.css", cx.shared.resource_suffix)), | |
2c00a5a8 XL |
781 | include_bytes!("static/themes/dark.css"))?; |
782 | themes.insert("dark".to_owned()); | |
783 | ||
784 | let mut themes: Vec<&String> = themes.iter().collect(); | |
785 | themes.sort(); | |
786 | // To avoid theme switch latencies as much as possible, we put everything theme related | |
787 | // at the beginning of the html files into another js file. | |
0531ce1d XL |
788 | write(cx.dst.join(&format!("theme{}.js", cx.shared.resource_suffix)), |
789 | format!( | |
2c00a5a8 XL |
790 | r#"var themes = document.getElementById("theme-choices"); |
791 | var themePicker = document.getElementById("theme-picker"); | |
792 | themePicker.onclick = function() {{ | |
793 | if (themes.style.display === "block") {{ | |
794 | themes.style.display = "none"; | |
795 | themePicker.style.borderBottomRightRadius = "3px"; | |
796 | themePicker.style.borderBottomLeftRadius = "3px"; | |
797 | }} else {{ | |
798 | themes.style.display = "block"; | |
799 | themePicker.style.borderBottomRightRadius = "0"; | |
800 | themePicker.style.borderBottomLeftRadius = "0"; | |
801 | }} | |
802 | }}; | |
803 | [{}].forEach(function(item) {{ | |
804 | var but = document.createElement('button'); | |
805 | but.innerHTML = item; | |
806 | but.onclick = function(el) {{ | |
807 | switchTheme(currentTheme, mainTheme, item); | |
808 | }}; | |
809 | themes.appendChild(but); | |
83c7162d | 810 | }});"#, |
0531ce1d XL |
811 | themes.iter() |
812 | .map(|s| format!("\"{}\"", s)) | |
813 | .collect::<Vec<String>>() | |
814 | .join(",")).as_bytes(), | |
815 | )?; | |
2c00a5a8 | 816 | |
0531ce1d XL |
817 | write(cx.dst.join(&format!("main{}.js", cx.shared.resource_suffix)), |
818 | include_bytes!("static/main.js"))?; | |
83c7162d XL |
819 | write(cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)), |
820 | include_bytes!("static/settings.js"))?; | |
0531ce1d XL |
821 | |
822 | { | |
823 | let mut data = format!("var resourcesSuffix = \"{}\";\n", | |
824 | cx.shared.resource_suffix).into_bytes(); | |
825 | data.extend_from_slice(include_bytes!("static/storage.js")); | |
826 | write(cx.dst.join(&format!("storage{}.js", cx.shared.resource_suffix)), &data)?; | |
827 | } | |
2c00a5a8 | 828 | |
54a0048b | 829 | if let Some(ref css) = cx.shared.css_file_extension { |
0531ce1d | 830 | let out = cx.dst.join(&format!("theme{}.css", cx.shared.resource_suffix)); |
2c00a5a8 | 831 | try_err!(fs::copy(css, out), css); |
54a0048b | 832 | } |
0531ce1d | 833 | write(cx.dst.join(&format!("normalize{}.css", cx.shared.resource_suffix)), |
54a0048b SL |
834 | include_bytes!("static/normalize.css"))?; |
835 | write(cx.dst.join("FiraSans-Regular.woff"), | |
836 | include_bytes!("static/FiraSans-Regular.woff"))?; | |
837 | write(cx.dst.join("FiraSans-Medium.woff"), | |
838 | include_bytes!("static/FiraSans-Medium.woff"))?; | |
839 | write(cx.dst.join("FiraSans-LICENSE.txt"), | |
840 | include_bytes!("static/FiraSans-LICENSE.txt"))?; | |
841 | write(cx.dst.join("Heuristica-Italic.woff"), | |
842 | include_bytes!("static/Heuristica-Italic.woff"))?; | |
843 | write(cx.dst.join("Heuristica-LICENSE.txt"), | |
844 | include_bytes!("static/Heuristica-LICENSE.txt"))?; | |
845 | write(cx.dst.join("SourceSerifPro-Regular.woff"), | |
846 | include_bytes!("static/SourceSerifPro-Regular.woff"))?; | |
847 | write(cx.dst.join("SourceSerifPro-Bold.woff"), | |
848 | include_bytes!("static/SourceSerifPro-Bold.woff"))?; | |
849 | write(cx.dst.join("SourceSerifPro-LICENSE.txt"), | |
850 | include_bytes!("static/SourceSerifPro-LICENSE.txt"))?; | |
851 | write(cx.dst.join("SourceCodePro-Regular.woff"), | |
852 | include_bytes!("static/SourceCodePro-Regular.woff"))?; | |
853 | write(cx.dst.join("SourceCodePro-Semibold.woff"), | |
854 | include_bytes!("static/SourceCodePro-Semibold.woff"))?; | |
855 | write(cx.dst.join("SourceCodePro-LICENSE.txt"), | |
856 | include_bytes!("static/SourceCodePro-LICENSE.txt"))?; | |
857 | write(cx.dst.join("LICENSE-MIT.txt"), | |
858 | include_bytes!("static/LICENSE-MIT.txt"))?; | |
859 | write(cx.dst.join("LICENSE-APACHE.txt"), | |
860 | include_bytes!("static/LICENSE-APACHE.txt"))?; | |
861 | write(cx.dst.join("COPYRIGHT.txt"), | |
862 | include_bytes!("static/COPYRIGHT.txt"))?; | |
1a4d82fc | 863 | |
83c7162d | 864 | fn collect(path: &Path, krate: &str, key: &str) -> io::Result<Vec<String>> { |
1a4d82fc JJ |
865 | let mut ret = Vec::new(); |
866 | if path.exists() { | |
54a0048b SL |
867 | for line in BufReader::new(File::open(path)?).lines() { |
868 | let line = line?; | |
1a4d82fc | 869 | if !line.starts_with(key) { |
9e0c209e | 870 | continue; |
1a4d82fc | 871 | } |
7453a54e | 872 | if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) { |
9e0c209e | 873 | continue; |
1a4d82fc JJ |
874 | } |
875 | ret.push(line.to_string()); | |
876 | } | |
877 | } | |
c30ab7b3 | 878 | Ok(ret) |
1a4d82fc JJ |
879 | } |
880 | ||
83c7162d XL |
881 | fn show_item(item: &IndexItem, krate: &str) -> String { |
882 | format!("{{'crate':'{}','ty':{},'name':'{}','path':'{}'{}}}", | |
883 | krate, item.ty as usize, item.name, item.path, | |
884 | if let Some(p) = item.parent_idx { | |
885 | format!(",'parent':{}", p) | |
886 | } else { | |
887 | String::new() | |
888 | }) | |
889 | } | |
890 | ||
891 | let dst = cx.dst.join("aliases.js"); | |
892 | { | |
893 | let mut all_aliases = try_err!(collect(&dst, &krate.name, "ALIASES"), &dst); | |
894 | let mut w = try_err!(File::create(&dst), &dst); | |
895 | let mut output = String::with_capacity(100); | |
896 | for (alias, items) in &cache.aliases { | |
897 | if items.is_empty() { | |
898 | continue | |
899 | } | |
900 | output.push_str(&format!("\"{}\":[{}],", | |
901 | alias, | |
902 | items.iter() | |
903 | .map(|v| show_item(v, &krate.name)) | |
904 | .collect::<Vec<_>>() | |
905 | .join(","))); | |
906 | } | |
907 | all_aliases.push(format!("ALIASES['{}'] = {{{}}};", krate.name, output)); | |
908 | all_aliases.sort(); | |
909 | try_err!(writeln!(&mut w, "var ALIASES = {{}};"), &dst); | |
910 | for aliases in &all_aliases { | |
911 | try_err!(writeln!(&mut w, "{}", aliases), &dst); | |
912 | } | |
913 | } | |
914 | ||
1a4d82fc JJ |
915 | // Update the search index |
916 | let dst = cx.dst.join("search-index.js"); | |
476ff2be SL |
917 | let mut all_indexes = try_err!(collect(&dst, &krate.name, "searchIndex"), &dst); |
918 | all_indexes.push(search_index); | |
919 | // Sort the indexes by crate so the file will be generated identically even | |
920 | // with rustdoc running in parallel. | |
921 | all_indexes.sort(); | |
b039eaaf SL |
922 | let mut w = try_err!(File::create(&dst), &dst); |
923 | try_err!(writeln!(&mut w, "var searchIndex = {{}};"), &dst); | |
85aaf69f | 924 | for index in &all_indexes { |
b039eaaf | 925 | try_err!(writeln!(&mut w, "{}", *index), &dst); |
1a4d82fc | 926 | } |
b039eaaf | 927 | try_err!(writeln!(&mut w, "initSearch(searchIndex);"), &dst); |
1a4d82fc JJ |
928 | |
929 | // Update the list of all implementors for traits | |
930 | let dst = cx.dst.join("implementors"); | |
85aaf69f | 931 | for (&did, imps) in &cache.implementors { |
1a4d82fc JJ |
932 | // Private modules can leak through to this phase of rustdoc, which |
933 | // could contain implementations for otherwise private types. In some | |
934 | // rare cases we could find an implementation for an item which wasn't | |
935 | // indexed, so we just skip this step in that case. | |
936 | // | |
937 | // FIXME: this is a vague explanation for why this can't be a `get`, in | |
938 | // theory it should be... | |
939 | let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) { | |
940 | Some(p) => p, | |
5bcae85e SL |
941 | None => match cache.external_paths.get(&did) { |
942 | Some(p) => p, | |
943 | None => continue, | |
944 | } | |
1a4d82fc JJ |
945 | }; |
946 | ||
3b2f2976 | 947 | let mut have_impls = false; |
476ff2be SL |
948 | let mut implementors = format!(r#"implementors["{}"] = ["#, krate.name); |
949 | for imp in imps { | |
950 | // If the trait and implementation are in the same crate, then | |
951 | // there's no need to emit information about it (there's inlining | |
952 | // going on). If they're in different crates then the crate defining | |
953 | // the trait will be interested in our implementation. | |
2c00a5a8 | 954 | if imp.impl_item.def_id.krate == did.krate { continue } |
3b2f2976 XL |
955 | // If the implementation is from another crate then that crate |
956 | // should add it. | |
2c00a5a8 | 957 | if !imp.impl_item.def_id.is_local() { continue } |
3b2f2976 | 958 | have_impls = true; |
0531ce1d XL |
959 | write!(implementors, "{{text:{},synthetic:{},types:{}}},", |
960 | as_json(&imp.inner_impl().to_string()), | |
961 | imp.inner_impl().synthetic, | |
962 | as_json(&collect_paths_for_type(imp.inner_impl().for_.clone()))).unwrap(); | |
476ff2be SL |
963 | } |
964 | implementors.push_str("];"); | |
965 | ||
3b2f2976 XL |
966 | // Only create a js file if we have impls to add to it. If the trait is |
967 | // documented locally though we always create the file to avoid dead | |
968 | // links. | |
969 | if !have_impls && !cache.paths.contains_key(&did) { | |
970 | continue; | |
971 | } | |
972 | ||
1a4d82fc | 973 | let mut mydst = dst.clone(); |
85aaf69f SL |
974 | for part in &remote_path[..remote_path.len() - 1] { |
975 | mydst.push(part); | |
1a4d82fc | 976 | } |
476ff2be | 977 | try_err!(fs::create_dir_all(&mydst), &mydst); |
c34b1796 | 978 | mydst.push(&format!("{}.{}.js", |
9e0c209e | 979 | remote_item_type.css_class(), |
c34b1796 | 980 | remote_path[remote_path.len() - 1])); |
1a4d82fc | 981 | |
476ff2be SL |
982 | let mut all_implementors = try_err!(collect(&mydst, &krate.name, "implementors"), &mydst); |
983 | all_implementors.push(implementors); | |
984 | // Sort the implementors by crate so the file will be generated | |
985 | // identically even with rustdoc running in parallel. | |
986 | all_implementors.sort(); | |
1a4d82fc | 987 | |
476ff2be SL |
988 | let mut f = try_err!(File::create(&mydst), &mydst); |
989 | try_err!(writeln!(&mut f, "(function() {{var implementors = {{}};"), &mydst); | |
85aaf69f | 990 | for implementor in &all_implementors { |
476ff2be | 991 | try_err!(writeln!(&mut f, "{}", *implementor), &mydst); |
1a4d82fc | 992 | } |
b039eaaf | 993 | try_err!(writeln!(&mut f, "{}", r" |
1a4d82fc JJ |
994 | if (window.register_implementors) { |
995 | window.register_implementors(implementors); | |
996 | } else { | |
997 | window.pending_implementors = implementors; | |
998 | } | |
b039eaaf SL |
999 | "), &mydst); |
1000 | try_err!(writeln!(&mut f, r"}})()"), &mydst); | |
1a4d82fc JJ |
1001 | } |
1002 | Ok(()) | |
1003 | } | |
1004 | ||
54a0048b | 1005 | fn render_sources(dst: &Path, scx: &mut SharedContext, |
b039eaaf | 1006 | krate: clean::Crate) -> Result<clean::Crate, Error> { |
1a4d82fc | 1007 | info!("emitting source files"); |
041b39d2 XL |
1008 | let dst = dst.join("src").join(&krate.name); |
1009 | try_err!(fs::create_dir_all(&dst), &dst); | |
1a4d82fc | 1010 | let mut folder = SourceCollector { |
3b2f2976 XL |
1011 | dst, |
1012 | scx, | |
1a4d82fc | 1013 | }; |
1a4d82fc JJ |
1014 | Ok(folder.fold_crate(krate)) |
1015 | } | |
1016 | ||
1017 | /// Writes the entire contents of a string to a destination, not attempting to | |
1018 | /// catch any errors. | |
b039eaaf | 1019 | fn write(dst: PathBuf, contents: &[u8]) -> Result<(), Error> { |
2c00a5a8 | 1020 | Ok(try_err!(fs::write(&dst, contents), &dst)) |
1a4d82fc JJ |
1021 | } |
1022 | ||
1a4d82fc JJ |
1023 | /// Takes a path to a source file and cleans the path to it. This canonicalizes |
1024 | /// things like ".." to components which preserve the "top down" hierarchy of a | |
c34b1796 AL |
1025 | /// static HTML tree. Each component in the cleaned path will be passed as an |
1026 | /// argument to `f`. The very last component of the path (ie the file name) will | |
1027 | /// be passed to `f` if `keep_filename` is true, and ignored otherwise. | |
1a4d82fc JJ |
1028 | // FIXME (#9639): The closure should deal with &[u8] instead of &str |
1029 | // FIXME (#9639): This is too conservative, rejecting non-UTF-8 paths | |
c34b1796 | 1030 | fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) where |
1a4d82fc JJ |
1031 | F: FnMut(&str), |
1032 | { | |
1a4d82fc | 1033 | // make it relative, if possible |
9cc50fc6 | 1034 | let p = p.strip_prefix(src_root).unwrap_or(p); |
1a4d82fc | 1035 | |
7453a54e SL |
1036 | let mut iter = p.components().peekable(); |
1037 | ||
c34b1796 AL |
1038 | while let Some(c) = iter.next() { |
1039 | if !keep_filename && iter.peek().is_none() { | |
1040 | break; | |
1041 | } | |
1042 | ||
7453a54e SL |
1043 | match c { |
1044 | Component::ParentDir => f("up"), | |
1045 | Component::Normal(c) => f(c.to_str().unwrap()), | |
1046 | _ => continue, | |
1a4d82fc JJ |
1047 | } |
1048 | } | |
1049 | } | |
1050 | ||
1051 | /// Attempts to find where an external crate is located, given that we're | |
1052 | /// rendering in to the specified source destination. | |
1053 | fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation { | |
1054 | // See if there's documentation generated into the local directory | |
85aaf69f | 1055 | let local_location = dst.join(&e.name); |
1a4d82fc JJ |
1056 | if local_location.is_dir() { |
1057 | return Local; | |
1058 | } | |
1059 | ||
1060 | // Failing that, see if there's an attribute specifying where to find this | |
1061 | // external crate | |
476ff2be SL |
1062 | e.attrs.lists("doc") |
1063 | .filter(|a| a.check_name("html_root_url")) | |
1064 | .filter_map(|a| a.value_str()) | |
1065 | .map(|url| { | |
1066 | let mut url = url.to_string(); | |
54a0048b SL |
1067 | if !url.ends_with("/") { |
1068 | url.push('/') | |
1a4d82fc | 1069 | } |
54a0048b | 1070 | Remote(url) |
476ff2be | 1071 | }).next().unwrap_or(Unknown) // Well, at least we tried. |
1a4d82fc JJ |
1072 | } |
1073 | ||
1074 | impl<'a> DocFolder for SourceCollector<'a> { | |
1075 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | |
1076 | // If we're including source files, and we haven't seen this file yet, | |
476ff2be | 1077 | // then we need to render it out to the filesystem. |
54a0048b | 1078 | if self.scx.include_sources |
ff7c6d11 XL |
1079 | // skip all invalid or macro spans |
1080 | && item.source.filename.is_real() | |
476ff2be | 1081 | // skip non-local items |
ff7c6d11 | 1082 | && item.def_id.is_local() { |
1a4d82fc JJ |
1083 | |
1084 | // If it turns out that we couldn't read this file, then we probably | |
1085 | // can't read any of the files (generating html output from json or | |
1086 | // something like that), so just don't include sources for the | |
1087 | // entire crate. The other option is maintaining this mapping on a | |
1088 | // per-file basis, but that's probably not worth it... | |
54a0048b | 1089 | self.scx |
7453a54e | 1090 | .include_sources = match self.emit_source(&item.source.filename) { |
1a4d82fc JJ |
1091 | Ok(()) => true, |
1092 | Err(e) => { | |
1093 | println!("warning: source code was requested to be rendered, \ | |
1094 | but processing `{}` had an error: {}", | |
1095 | item.source.filename, e); | |
1096 | println!(" skipping rendering of source code"); | |
1097 | false | |
1098 | } | |
1099 | }; | |
1a4d82fc | 1100 | } |
1a4d82fc JJ |
1101 | self.fold_item_recur(item) |
1102 | } | |
1103 | } | |
1104 | ||
1105 | impl<'a> SourceCollector<'a> { | |
1106 | /// Renders the given filename into its corresponding HTML source file. | |
ff7c6d11 XL |
1107 | fn emit_source(&mut self, filename: &FileName) -> io::Result<()> { |
1108 | let p = match *filename { | |
1109 | FileName::Real(ref file) => file, | |
1110 | _ => return Ok(()), | |
1111 | }; | |
1112 | if self.scx.local_sources.contains_key(&**p) { | |
54a0048b SL |
1113 | // We've already emitted this source |
1114 | return Ok(()); | |
1115 | } | |
1a4d82fc | 1116 | |
0531ce1d | 1117 | let contents = fs::read_to_string(&p)?; |
1a4d82fc JJ |
1118 | |
1119 | // Remove the utf-8 BOM if any | |
1120 | let contents = if contents.starts_with("\u{feff}") { | |
85aaf69f | 1121 | &contents[3..] |
1a4d82fc | 1122 | } else { |
2c00a5a8 | 1123 | &contents[..] |
1a4d82fc JJ |
1124 | }; |
1125 | ||
1126 | // Create the intermediate directories | |
1127 | let mut cur = self.dst.clone(); | |
62682a34 | 1128 | let mut root_path = String::from("../../"); |
54a0048b SL |
1129 | let mut href = String::new(); |
1130 | clean_srcpath(&self.scx.src_root, &p, false, |component| { | |
1a4d82fc | 1131 | cur.push(component); |
041b39d2 | 1132 | fs::create_dir_all(&cur).unwrap(); |
1a4d82fc | 1133 | root_path.push_str("../"); |
54a0048b SL |
1134 | href.push_str(component); |
1135 | href.push('/'); | |
1a4d82fc | 1136 | }); |
83c7162d XL |
1137 | let mut fname = p.file_name() |
1138 | .expect("source has no filename") | |
c34b1796 AL |
1139 | .to_os_string(); |
1140 | fname.push(".html"); | |
9e0c209e | 1141 | cur.push(&fname); |
54a0048b SL |
1142 | href.push_str(&fname.to_string_lossy()); |
1143 | ||
1144 | let mut w = BufWriter::new(File::create(&cur)?); | |
c34b1796 AL |
1145 | let title = format!("{} -- source", cur.file_name().unwrap() |
1146 | .to_string_lossy()); | |
1a4d82fc JJ |
1147 | let desc = format!("Source to the Rust file `{}`.", filename); |
1148 | let page = layout::Page { | |
85aaf69f | 1149 | title: &title, |
9e0c209e | 1150 | css_class: "source", |
85aaf69f SL |
1151 | root_path: &root_path, |
1152 | description: &desc, | |
54a0048b | 1153 | keywords: BASIC_KEYWORDS, |
0531ce1d | 1154 | resource_suffix: &self.scx.resource_suffix, |
1a4d82fc | 1155 | }; |
54a0048b SL |
1156 | layout::render(&mut w, &self.scx.layout, |
1157 | &page, &(""), &Source(contents), | |
2c00a5a8 XL |
1158 | self.scx.css_file_extension.is_some(), |
1159 | &self.scx.themes)?; | |
54a0048b | 1160 | w.flush()?; |
ff7c6d11 | 1161 | self.scx.local_sources.insert(p.clone(), href); |
54a0048b | 1162 | Ok(()) |
1a4d82fc JJ |
1163 | } |
1164 | } | |
1165 | ||
1166 | impl DocFolder for Cache { | |
1167 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | |
54a0048b SL |
1168 | // If this is a stripped module, |
1169 | // we don't want it or its children in the search index. | |
1170 | let orig_stripped_mod = match item.inner { | |
1171 | clean::StrippedItem(box clean::ModuleItem(..)) => { | |
a7813a04 | 1172 | mem::replace(&mut self.stripped_mod, true) |
1a4d82fc | 1173 | } |
54a0048b | 1174 | _ => self.stripped_mod, |
1a4d82fc JJ |
1175 | }; |
1176 | ||
2c00a5a8 XL |
1177 | // If the impl is from a masked crate or references something from a |
1178 | // masked crate then remove it completely. | |
1179 | if let clean::ImplItem(ref i) = item.inner { | |
1180 | if self.masked_crates.contains(&item.def_id.krate) || | |
1181 | i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) || | |
1182 | i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) { | |
1183 | return None; | |
1184 | } | |
1185 | } | |
1186 | ||
1a4d82fc | 1187 | // Register any generics to their corresponding string. This is used |
476ff2be | 1188 | // when pretty-printing types. |
9e0c209e SL |
1189 | if let Some(generics) = item.inner.generics() { |
1190 | self.generics(generics); | |
1a4d82fc JJ |
1191 | } |
1192 | ||
476ff2be SL |
1193 | // Propagate a trait method's documentation to all implementors of the |
1194 | // trait. | |
1195 | if let clean::TraitItem(ref t) = item.inner { | |
1196 | self.traits.entry(item.def_id).or_insert_with(|| t.clone()); | |
1197 | } | |
1a4d82fc | 1198 | |
476ff2be SL |
1199 | // Collect all the implementors of traits. |
1200 | if let clean::ImplItem(ref i) = item.inner { | |
2c00a5a8 XL |
1201 | if let Some(did) = i.trait_.def_id() { |
1202 | self.implementors.entry(did).or_insert(vec![]).push(Impl { | |
1203 | impl_item: item.clone(), | |
1204 | }); | |
1a4d82fc JJ |
1205 | } |
1206 | } | |
1207 | ||
476ff2be | 1208 | // Index this method for searching later on. |
1a4d82fc | 1209 | if let Some(ref s) = item.name { |
9e0c209e | 1210 | let (parent, is_inherent_impl_item) = match item.inner { |
54a0048b | 1211 | clean::StrippedItem(..) => ((None, None), false), |
7453a54e SL |
1212 | clean::AssociatedConstItem(..) | |
1213 | clean::TypedefItem(_, true) if self.parent_is_trait_impl => { | |
1214 | // skip associated items in trait impls | |
1215 | ((None, None), false) | |
1216 | } | |
d9579d0f | 1217 | clean::AssociatedTypeItem(..) | |
1a4d82fc JJ |
1218 | clean::TyMethodItem(..) | |
1219 | clean::StructFieldItem(..) | | |
1220 | clean::VariantItem(..) => { | |
1221 | ((Some(*self.parent_stack.last().unwrap()), | |
85aaf69f | 1222 | Some(&self.stack[..self.stack.len() - 1])), |
1a4d82fc JJ |
1223 | false) |
1224 | } | |
9e0c209e | 1225 | clean::MethodItem(..) | clean::AssociatedConstItem(..) => { |
9346a6ac | 1226 | if self.parent_stack.is_empty() { |
1a4d82fc JJ |
1227 | ((None, None), false) |
1228 | } else { | |
1229 | let last = self.parent_stack.last().unwrap(); | |
1230 | let did = *last; | |
1231 | let path = match self.paths.get(&did) { | |
9346a6ac AL |
1232 | // The current stack not necessarily has correlation |
1233 | // for where the type was defined. On the other | |
1234 | // hand, `paths` always has the right | |
1235 | // information if present. | |
5bcae85e | 1236 | Some(&(ref fqp, ItemType::Trait)) | |
1a4d82fc | 1237 | Some(&(ref fqp, ItemType::Struct)) | |
9e0c209e | 1238 | Some(&(ref fqp, ItemType::Union)) | |
1a4d82fc | 1239 | Some(&(ref fqp, ItemType::Enum)) => |
85aaf69f SL |
1240 | Some(&fqp[..fqp.len() - 1]), |
1241 | Some(..) => Some(&*self.stack), | |
1a4d82fc JJ |
1242 | None => None |
1243 | }; | |
1244 | ((Some(*last), path), true) | |
1245 | } | |
1246 | } | |
85aaf69f | 1247 | _ => ((None, Some(&*self.stack)), false) |
1a4d82fc | 1248 | }; |
1a4d82fc JJ |
1249 | |
1250 | match parent { | |
9e0c209e | 1251 | (parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => { |
54a0048b | 1252 | debug_assert!(!item.is_stripped()); |
c34b1796 | 1253 | |
54a0048b SL |
1254 | // A crate has a module at its root, containing all items, |
1255 | // which should not be indexed. The crate-item itself is | |
1256 | // inserted later on when serializing the search-index. | |
9cc50fc6 SL |
1257 | if item.def_id.index != CRATE_DEF_INDEX { |
1258 | self.search_index.push(IndexItem { | |
c30ab7b3 | 1259 | ty: item.type_(), |
9cc50fc6 SL |
1260 | name: s.to_string(), |
1261 | path: path.join("::").to_string(), | |
32a655c1 | 1262 | desc: plain_summary_line(item.doc_value()), |
3b2f2976 | 1263 | parent, |
7453a54e | 1264 | parent_idx: None, |
a7813a04 | 1265 | search_type: get_index_search_type(&item), |
9cc50fc6 SL |
1266 | }); |
1267 | } | |
1a4d82fc | 1268 | } |
9e0c209e | 1269 | (Some(parent), None) if is_inherent_impl_item => { |
5bcae85e SL |
1270 | // We have a parent, but we don't know where they're |
1271 | // defined yet. Wait for later to index this item. | |
9e0c209e | 1272 | self.orphan_impl_items.push((parent, item.clone())); |
1a4d82fc JJ |
1273 | } |
1274 | _ => {} | |
1275 | } | |
1276 | } | |
1277 | ||
1278 | // Keep track of the fully qualified path for this item. | |
54a0048b SL |
1279 | let pushed = match item.name { |
1280 | Some(ref n) if !n.is_empty() => { | |
1a4d82fc JJ |
1281 | self.stack.push(n.to_string()); |
1282 | true | |
54a0048b SL |
1283 | } |
1284 | _ => false, | |
1285 | }; | |
1286 | ||
1a4d82fc JJ |
1287 | match item.inner { |
1288 | clean::StructItem(..) | clean::EnumItem(..) | | |
1289 | clean::TypedefItem(..) | clean::TraitItem(..) | | |
1290 | clean::FunctionItem(..) | clean::ModuleItem(..) | | |
3157f602 | 1291 | clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) | |
9e0c209e | 1292 | clean::ConstantItem(..) | clean::StaticItem(..) | |
2c00a5a8 | 1293 | clean::UnionItem(..) | clean::ForeignTypeItem | clean::MacroItem(..) |
3157f602 | 1294 | if !self.stripped_mod => { |
2c00a5a8 | 1295 | // Re-exported items mean that the same id can show up twice |
1a4d82fc | 1296 | // in the rustdoc ast that we're looking at. We know, |
2c00a5a8 | 1297 | // however, that a re-exported item doesn't show up in the |
1a4d82fc JJ |
1298 | // `public_items` map, so we can skip inserting into the |
1299 | // paths map if there was already an entry present and we're | |
1300 | // not a public item. | |
83c7162d XL |
1301 | if !self.paths.contains_key(&item.def_id) || |
1302 | self.access_levels.is_public(item.def_id) | |
b039eaaf | 1303 | { |
1a4d82fc | 1304 | self.paths.insert(item.def_id, |
c30ab7b3 | 1305 | (self.stack.clone(), item.type_())); |
1a4d82fc | 1306 | } |
83c7162d | 1307 | self.add_aliases(&item); |
1a4d82fc | 1308 | } |
476ff2be SL |
1309 | // Link variants to their parent enum because pages aren't emitted |
1310 | // for each variant. | |
54a0048b | 1311 | clean::VariantItem(..) if !self.stripped_mod => { |
1a4d82fc JJ |
1312 | let mut stack = self.stack.clone(); |
1313 | stack.pop(); | |
1314 | self.paths.insert(item.def_id, (stack, ItemType::Enum)); | |
1315 | } | |
1316 | ||
1317 | clean::PrimitiveItem(..) if item.visibility.is_some() => { | |
83c7162d | 1318 | self.add_aliases(&item); |
1a4d82fc | 1319 | self.paths.insert(item.def_id, (self.stack.clone(), |
c30ab7b3 | 1320 | item.type_())); |
1a4d82fc JJ |
1321 | } |
1322 | ||
1323 | _ => {} | |
1324 | } | |
1325 | ||
1326 | // Maintain the parent stack | |
7453a54e | 1327 | let orig_parent_is_trait_impl = self.parent_is_trait_impl; |
1a4d82fc | 1328 | let parent_pushed = match item.inner { |
abe05a73 | 1329 | clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem | |
9e0c209e | 1330 | clean::StructItem(..) | clean::UnionItem(..) => { |
1a4d82fc | 1331 | self.parent_stack.push(item.def_id); |
7453a54e | 1332 | self.parent_is_trait_impl = false; |
1a4d82fc JJ |
1333 | true |
1334 | } | |
1335 | clean::ImplItem(ref i) => { | |
7453a54e | 1336 | self.parent_is_trait_impl = i.trait_.is_some(); |
1a4d82fc JJ |
1337 | match i.for_ { |
1338 | clean::ResolvedPath{ did, .. } => { | |
1339 | self.parent_stack.push(did); | |
1340 | true | |
1341 | } | |
9346a6ac | 1342 | ref t => { |
476ff2be SL |
1343 | let prim_did = t.primitive_type().and_then(|t| { |
1344 | self.primitive_locations.get(&t).cloned() | |
1345 | }); | |
1346 | match prim_did { | |
1347 | Some(did) => { | |
9346a6ac AL |
1348 | self.parent_stack.push(did); |
1349 | true | |
1350 | } | |
476ff2be | 1351 | None => false, |
9346a6ac AL |
1352 | } |
1353 | } | |
1a4d82fc JJ |
1354 | } |
1355 | } | |
1356 | _ => false | |
1357 | }; | |
1358 | ||
476ff2be SL |
1359 | // Once we've recursively found all the generics, hoard off all the |
1360 | // implementations elsewhere. | |
54a0048b | 1361 | let ret = self.fold_item_recur(item).and_then(|item| { |
a7813a04 | 1362 | if let clean::Item { inner: clean::ImplItem(_), .. } = item { |
54a0048b SL |
1363 | // Figure out the id of this impl. This may map to a |
1364 | // primitive rather than always to a struct/enum. | |
a7813a04 | 1365 | // Note: matching twice to restrict the lifetime of the `i` borrow. |
abe05a73 XL |
1366 | let mut dids = FxHashSet(); |
1367 | if let clean::Item { inner: clean::ImplItem(ref i), .. } = item { | |
2c00a5a8 XL |
1368 | match i.for_ { |
1369 | clean::ResolvedPath { did, .. } | | |
1370 | clean::BorrowedRef { | |
1371 | type_: box clean::ResolvedPath { did, .. }, .. | |
1372 | } => { | |
1373 | dids.insert(did); | |
1374 | } | |
1375 | ref t => { | |
1376 | let did = t.primitive_type().and_then(|t| { | |
1377 | self.primitive_locations.get(&t).cloned() | |
1378 | }); | |
abe05a73 | 1379 | |
2c00a5a8 XL |
1380 | if let Some(did) = did { |
1381 | dids.insert(did); | |
abe05a73 XL |
1382 | } |
1383 | } | |
1384 | } | |
1385 | ||
1386 | if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { | |
1387 | for bound in generics { | |
1388 | if let Some(did) = bound.def_id() { | |
1389 | dids.insert(did); | |
ea8adc8c | 1390 | } |
a7813a04 | 1391 | } |
1a4d82fc | 1392 | } |
a7813a04 XL |
1393 | } else { |
1394 | unreachable!() | |
54a0048b | 1395 | }; |
abe05a73 | 1396 | for did in dids { |
476ff2be | 1397 | self.impls.entry(did).or_insert(vec![]).push(Impl { |
abe05a73 | 1398 | impl_item: item.clone(), |
476ff2be | 1399 | }); |
1a4d82fc | 1400 | } |
54a0048b SL |
1401 | None |
1402 | } else { | |
1403 | Some(item) | |
1a4d82fc | 1404 | } |
54a0048b | 1405 | }); |
1a4d82fc JJ |
1406 | |
1407 | if pushed { self.stack.pop().unwrap(); } | |
1408 | if parent_pushed { self.parent_stack.pop().unwrap(); } | |
54a0048b | 1409 | self.stripped_mod = orig_stripped_mod; |
7453a54e | 1410 | self.parent_is_trait_impl = orig_parent_is_trait_impl; |
c30ab7b3 | 1411 | ret |
1a4d82fc JJ |
1412 | } |
1413 | } | |
1414 | ||
1415 | impl<'a> Cache { | |
1416 | fn generics(&mut self, generics: &clean::Generics) { | |
ff7c6d11 XL |
1417 | for param in &generics.params { |
1418 | if let clean::GenericParam::Type(ref typ) = *param { | |
1419 | self.typarams.insert(typ.did, typ.name.clone()); | |
1420 | } | |
1a4d82fc JJ |
1421 | } |
1422 | } | |
83c7162d XL |
1423 | |
1424 | fn add_aliases(&mut self, item: &clean::Item) { | |
1425 | if item.def_id.index == CRATE_DEF_INDEX { | |
1426 | return | |
1427 | } | |
1428 | if let Some(ref item_name) = item.name { | |
1429 | let path = self.paths.get(&item.def_id) | |
1430 | .map(|p| p.0[..p.0.len() - 1].join("::")) | |
1431 | .unwrap_or("std".to_owned()); | |
1432 | for alias in item.attrs.lists("doc") | |
1433 | .filter(|a| a.check_name("alias")) | |
1434 | .filter_map(|a| a.value_str() | |
1435 | .map(|s| s.to_string().replace("\"", ""))) | |
1436 | .filter(|v| !v.is_empty()) | |
1437 | .collect::<FxHashSet<_>>() | |
1438 | .into_iter() { | |
1439 | self.aliases.entry(alias) | |
1440 | .or_insert(Vec::with_capacity(1)) | |
1441 | .push(IndexItem { | |
1442 | ty: item.type_(), | |
1443 | name: item_name.to_string(), | |
1444 | path: path.clone(), | |
1445 | desc: String::new(), | |
1446 | parent: None, | |
1447 | parent_idx: None, | |
1448 | search_type: get_index_search_type(&item), | |
1449 | }); | |
1450 | } | |
1451 | } | |
1452 | } | |
1453 | } | |
1454 | ||
1455 | #[derive(Debug, Eq, PartialEq, Hash)] | |
1456 | struct ItemEntry { | |
1457 | url: String, | |
1458 | name: String, | |
1459 | } | |
1460 | ||
1461 | impl ItemEntry { | |
1462 | fn new(mut url: String, name: String) -> ItemEntry { | |
1463 | while url.starts_with('/') { | |
1464 | url.remove(0); | |
1465 | } | |
1466 | ItemEntry { | |
1467 | url, | |
1468 | name, | |
1469 | } | |
1470 | } | |
1471 | } | |
1472 | ||
1473 | impl fmt::Display for ItemEntry { | |
1474 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1475 | write!(f, "<a href='{}'>{}</a>", self.url, Escape(&self.name)) | |
1476 | } | |
1477 | } | |
1478 | ||
1479 | impl PartialOrd for ItemEntry { | |
1480 | fn partial_cmp(&self, other: &ItemEntry) -> Option<::std::cmp::Ordering> { | |
1481 | Some(self.cmp(other)) | |
1482 | } | |
1483 | } | |
1484 | ||
1485 | impl Ord for ItemEntry { | |
1486 | fn cmp(&self, other: &ItemEntry) -> ::std::cmp::Ordering { | |
1487 | self.name.cmp(&other.name) | |
1488 | } | |
1489 | } | |
1490 | ||
1491 | #[derive(Debug)] | |
1492 | struct AllTypes { | |
1493 | structs: HashSet<ItemEntry>, | |
1494 | enums: HashSet<ItemEntry>, | |
1495 | unions: HashSet<ItemEntry>, | |
1496 | primitives: HashSet<ItemEntry>, | |
1497 | traits: HashSet<ItemEntry>, | |
1498 | macros: HashSet<ItemEntry>, | |
1499 | functions: HashSet<ItemEntry>, | |
1500 | typedefs: HashSet<ItemEntry>, | |
1501 | statics: HashSet<ItemEntry>, | |
1502 | constants: HashSet<ItemEntry>, | |
1503 | } | |
1504 | ||
1505 | impl AllTypes { | |
1506 | fn new() -> AllTypes { | |
1507 | AllTypes { | |
1508 | structs: HashSet::with_capacity(100), | |
1509 | enums: HashSet::with_capacity(100), | |
1510 | unions: HashSet::with_capacity(100), | |
1511 | primitives: HashSet::with_capacity(26), | |
1512 | traits: HashSet::with_capacity(100), | |
1513 | macros: HashSet::with_capacity(100), | |
1514 | functions: HashSet::with_capacity(100), | |
1515 | typedefs: HashSet::with_capacity(100), | |
1516 | statics: HashSet::with_capacity(100), | |
1517 | constants: HashSet::with_capacity(100), | |
1518 | } | |
1519 | } | |
1520 | ||
1521 | fn append(&mut self, item_name: String, item_type: &ItemType) { | |
1522 | let mut url: Vec<_> = item_name.split("::").skip(1).collect(); | |
1523 | if let Some(name) = url.pop() { | |
1524 | let new_url = format!("{}/{}.{}.html", url.join("/"), item_type, name); | |
1525 | url.push(name); | |
1526 | let name = url.join("::"); | |
1527 | match *item_type { | |
1528 | ItemType::Struct => self.structs.insert(ItemEntry::new(new_url, name)), | |
1529 | ItemType::Enum => self.enums.insert(ItemEntry::new(new_url, name)), | |
1530 | ItemType::Union => self.unions.insert(ItemEntry::new(new_url, name)), | |
1531 | ItemType::Primitive => self.primitives.insert(ItemEntry::new(new_url, name)), | |
1532 | ItemType::Trait => self.traits.insert(ItemEntry::new(new_url, name)), | |
1533 | ItemType::Macro => self.macros.insert(ItemEntry::new(new_url, name)), | |
1534 | ItemType::Function => self.functions.insert(ItemEntry::new(new_url, name)), | |
1535 | ItemType::Typedef => self.typedefs.insert(ItemEntry::new(new_url, name)), | |
1536 | ItemType::Static => self.statics.insert(ItemEntry::new(new_url, name)), | |
1537 | ItemType::Constant => self.constants.insert(ItemEntry::new(new_url, name)), | |
1538 | _ => true, | |
1539 | }; | |
1540 | } | |
1541 | } | |
1542 | } | |
1543 | ||
1544 | fn print_entries(f: &mut fmt::Formatter, e: &HashSet<ItemEntry>, title: &str, | |
1545 | class: &str) -> fmt::Result { | |
1546 | if !e.is_empty() { | |
1547 | let mut e: Vec<&ItemEntry> = e.iter().collect(); | |
1548 | e.sort(); | |
1549 | write!(f, "<h3 id='{}'>{}</h3><ul class='{} docblock'>{}</ul>", | |
1550 | title, | |
1551 | Escape(title), | |
1552 | class, | |
1553 | e.iter().map(|s| format!("<li>{}</li>", s)).collect::<String>())?; | |
1554 | } | |
1555 | Ok(()) | |
1556 | } | |
1557 | ||
1558 | impl fmt::Display for AllTypes { | |
1559 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1560 | write!(f, | |
1561 | "<h1 class='fqn'>\ | |
1562 | <span class='in-band'>List of all items</span>\ | |
1563 | <span class='out-of-band'>\ | |
1564 | <span id='render-detail'>\ | |
1565 | <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" title=\"collapse all docs\">\ | |
1566 | [<span class='inner'>−</span>]\ | |
1567 | </a>\ | |
1568 | </span> | |
1569 | </span> | |
1570 | </h1>")?; | |
1571 | print_entries(f, &self.structs, "Structs", "structs")?; | |
1572 | print_entries(f, &self.enums, "Enums", "enums")?; | |
1573 | print_entries(f, &self.unions, "Unions", "unions")?; | |
1574 | print_entries(f, &self.primitives, "Primitives", "primitives")?; | |
1575 | print_entries(f, &self.traits, "Traits", "traits")?; | |
1576 | print_entries(f, &self.macros, "Macros", "macros")?; | |
1577 | print_entries(f, &self.functions, "Functions", "functions")?; | |
1578 | print_entries(f, &self.typedefs, "Typedefs", "typedefs")?; | |
1579 | print_entries(f, &self.statics, "Statics", "statics")?; | |
1580 | print_entries(f, &self.constants, "Constants", "constants") | |
1581 | } | |
1582 | } | |
1583 | ||
1584 | #[derive(Debug)] | |
1585 | struct Settings<'a> { | |
1586 | // (id, explanation, default value) | |
1587 | settings: Vec<(&'static str, &'static str, bool)>, | |
1588 | root_path: &'a str, | |
1589 | suffix: &'a str, | |
1590 | } | |
1591 | ||
1592 | impl<'a> Settings<'a> { | |
1593 | pub fn new(root_path: &'a str, suffix: &'a str) -> Settings<'a> { | |
1594 | Settings { | |
1595 | settings: vec![ | |
1596 | ("item-declarations", "Auto-hide item declarations.", true), | |
1597 | ("item-attributes", "Auto-hide item attributes.", true), | |
1598 | ("go-to-only-result", "Directly go to item in search if there is only one result", | |
1599 | false), | |
1600 | ], | |
1601 | root_path, | |
1602 | suffix, | |
1603 | } | |
1604 | } | |
1605 | } | |
1606 | ||
1607 | impl<'a> fmt::Display for Settings<'a> { | |
1608 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
1609 | write!(f, | |
1610 | "<h1 class='fqn'>\ | |
1611 | <span class='in-band'>Rustdoc settings</span>\ | |
1612 | </h1>\ | |
1613 | <div class='settings'>{}</div>\ | |
1614 | <script src='{}settings{}.js'></script>", | |
1615 | self.settings.iter() | |
1616 | .map(|(id, text, enabled)| { | |
1617 | format!("<div class='setting-line'>\ | |
1618 | <label class='toggle'>\ | |
1619 | <input type='checkbox' id='{}' {}>\ | |
1620 | <span class='slider'></span>\ | |
1621 | </label>\ | |
1622 | <div>{}</div>\ | |
1623 | </div>", id, if *enabled { " checked" } else { "" }, text) | |
1624 | }) | |
1625 | .collect::<String>(), | |
1626 | self.root_path, | |
1627 | self.suffix) | |
1628 | } | |
1a4d82fc JJ |
1629 | } |
1630 | ||
1631 | impl Context { | |
c30ab7b3 SL |
1632 | /// String representation of how to get back to the root path of the 'doc/' |
1633 | /// folder in terms of a relative URL. | |
1634 | fn root_path(&self) -> String { | |
1635 | repeat("../").take(self.current.len()).collect::<String>() | |
1636 | } | |
1637 | ||
1a4d82fc | 1638 | /// Recurse in the directory structure and change the "root path" to make |
476ff2be | 1639 | /// sure it always points to the top (relatively). |
1a4d82fc JJ |
1640 | fn recurse<T, F>(&mut self, s: String, f: F) -> T where |
1641 | F: FnOnce(&mut Context) -> T, | |
1642 | { | |
9346a6ac | 1643 | if s.is_empty() { |
1a4d82fc JJ |
1644 | panic!("Unexpected empty destination: {:?}", self.current); |
1645 | } | |
1646 | let prev = self.dst.clone(); | |
85aaf69f | 1647 | self.dst.push(&s); |
1a4d82fc JJ |
1648 | self.current.push(s); |
1649 | ||
1650 | info!("Recursing into {}", self.dst.display()); | |
1651 | ||
1a4d82fc JJ |
1652 | let ret = f(self); |
1653 | ||
1654 | info!("Recursed; leaving {}", self.dst.display()); | |
1655 | ||
1656 | // Go back to where we were at | |
1657 | self.dst = prev; | |
1a4d82fc JJ |
1658 | self.current.pop().unwrap(); |
1659 | ||
c30ab7b3 | 1660 | ret |
1a4d82fc JJ |
1661 | } |
1662 | ||
1663 | /// Main method for rendering a crate. | |
1664 | /// | |
1665 | /// This currently isn't parallelized, but it'd be pretty easy to add | |
1666 | /// parallelization to this function. | |
b039eaaf | 1667 | fn krate(self, mut krate: clean::Crate) -> Result<(), Error> { |
1a4d82fc JJ |
1668 | let mut item = match krate.module.take() { |
1669 | Some(i) => i, | |
476ff2be | 1670 | None => return Ok(()), |
1a4d82fc | 1671 | }; |
83c7162d XL |
1672 | let final_file = self.dst.join(&krate.name) |
1673 | .join("all.html"); | |
1674 | let settings_file = self.dst.join("settings.html"); | |
1675 | ||
1676 | let crate_name = krate.name.clone(); | |
1a4d82fc JJ |
1677 | item.name = Some(krate.name); |
1678 | ||
83c7162d XL |
1679 | let mut all = AllTypes::new(); |
1680 | ||
1681 | { | |
1682 | // Render the crate documentation | |
1683 | let mut work = vec![(self.clone(), item)]; | |
1684 | ||
1685 | while let Some((mut cx, item)) = work.pop() { | |
1686 | cx.item(item, &mut all, |cx, item| { | |
1687 | work.push((cx.clone(), item)) | |
1688 | })? | |
1689 | } | |
1690 | } | |
1a4d82fc | 1691 | |
83c7162d XL |
1692 | let mut w = BufWriter::new(try_err!(File::create(&final_file), &final_file)); |
1693 | let mut root_path = self.dst.to_str().expect("invalid path").to_owned(); | |
1694 | if !root_path.ends_with('/') { | |
1695 | root_path.push('/'); | |
54a0048b | 1696 | } |
83c7162d XL |
1697 | let mut page = layout::Page { |
1698 | title: "List of all items in this crate", | |
1699 | css_class: "mod", | |
1700 | root_path: "../", | |
1701 | description: "List of all items in this crate", | |
1702 | keywords: BASIC_KEYWORDS, | |
1703 | resource_suffix: &self.shared.resource_suffix, | |
1704 | }; | |
1705 | let sidebar = if let Some(ref version) = cache().crate_version { | |
1706 | format!("<p class='location'>Crate {}</p>\ | |
1707 | <div class='block version'>\ | |
1708 | <p>Version {}</p>\ | |
1709 | </div>\ | |
1710 | <a id='all-types' href='index.html'><p>Back to index</p></a>", | |
1711 | crate_name, version) | |
1712 | } else { | |
1713 | String::new() | |
1714 | }; | |
1715 | try_err!(layout::render(&mut w, &self.shared.layout, | |
1716 | &page, &sidebar, &all, | |
1717 | self.shared.css_file_extension.is_some(), | |
1718 | &self.shared.themes), | |
1719 | &final_file); | |
1720 | ||
1721 | // Generating settings page. | |
1722 | let settings = Settings::new("./", &self.shared.resource_suffix); | |
1723 | page.title = "Rustdoc settings"; | |
1724 | page.description = "Settings of Rustdoc"; | |
1725 | page.root_path = "./"; | |
1726 | ||
1727 | let mut w = BufWriter::new(try_err!(File::create(&settings_file), &settings_file)); | |
1728 | let mut themes = self.shared.themes.clone(); | |
1729 | let sidebar = "<p class='location'>Settings</p><div class='sidebar-elems'></div>"; | |
1730 | themes.push(PathBuf::from("settings.css")); | |
1731 | let mut layout = self.shared.layout.clone(); | |
1732 | layout.krate = String::new(); | |
1733 | layout.logo = String::new(); | |
1734 | layout.favicon = String::new(); | |
1735 | try_err!(layout::render(&mut w, &layout, | |
1736 | &page, &sidebar, &settings, | |
1737 | self.shared.css_file_extension.is_some(), | |
1738 | &themes), | |
1739 | &settings_file); | |
1740 | ||
1a4d82fc JJ |
1741 | Ok(()) |
1742 | } | |
1743 | ||
9e0c209e SL |
1744 | fn render_item(&self, |
1745 | writer: &mut io::Write, | |
1746 | it: &clean::Item, | |
1747 | pushname: bool) | |
1748 | -> io::Result<()> { | |
1749 | // A little unfortunate that this is done like this, but it sure | |
1750 | // does make formatting *a lot* nicer. | |
1751 | CURRENT_LOCATION_KEY.with(|slot| { | |
1752 | *slot.borrow_mut() = self.current.clone(); | |
1753 | }); | |
1a4d82fc | 1754 | |
9e0c209e SL |
1755 | let mut title = if it.is_primitive() { |
1756 | // No need to include the namespace for primitive types | |
1757 | String::new() | |
1758 | } else { | |
1759 | self.current.join("::") | |
1760 | }; | |
1761 | if pushname { | |
1762 | if !title.is_empty() { | |
1763 | title.push_str("::"); | |
1a4d82fc | 1764 | } |
9e0c209e SL |
1765 | title.push_str(it.name.as_ref().unwrap()); |
1766 | } | |
1767 | title.push_str(" - Rust"); | |
c30ab7b3 | 1768 | let tyname = it.type_().css_class(); |
9e0c209e SL |
1769 | let desc = if it.is_crate() { |
1770 | format!("API documentation for the Rust `{}` crate.", | |
1771 | self.shared.layout.krate) | |
1772 | } else { | |
1773 | format!("API documentation for the Rust `{}` {} in crate `{}`.", | |
1774 | it.name.as_ref().unwrap(), tyname, self.shared.layout.krate) | |
1775 | }; | |
1776 | let keywords = make_item_keywords(it); | |
1777 | let page = layout::Page { | |
1778 | css_class: tyname, | |
c30ab7b3 | 1779 | root_path: &self.root_path(), |
9e0c209e SL |
1780 | title: &title, |
1781 | description: &desc, | |
1782 | keywords: &keywords, | |
0531ce1d | 1783 | resource_suffix: &self.shared.resource_suffix, |
9e0c209e | 1784 | }; |
1a4d82fc | 1785 | |
9e0c209e | 1786 | reset_ids(true); |
1a4d82fc | 1787 | |
9e0c209e SL |
1788 | if !self.render_redirect_pages { |
1789 | layout::render(writer, &self.shared.layout, &page, | |
1790 | &Sidebar{ cx: self, item: it }, | |
1791 | &Item{ cx: self, item: it }, | |
2c00a5a8 XL |
1792 | self.shared.css_file_extension.is_some(), |
1793 | &self.shared.themes)?; | |
9e0c209e | 1794 | } else { |
c30ab7b3 | 1795 | let mut url = self.root_path(); |
9e0c209e SL |
1796 | if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) { |
1797 | for name in &names[..names.len() - 1] { | |
1798 | url.push_str(name); | |
1799 | url.push_str("/"); | |
1a4d82fc | 1800 | } |
9e0c209e SL |
1801 | url.push_str(&item_path(ty, names.last().unwrap())); |
1802 | layout::redirect(writer, &url)?; | |
1a4d82fc | 1803 | } |
1a4d82fc | 1804 | } |
9e0c209e SL |
1805 | Ok(()) |
1806 | } | |
1a4d82fc | 1807 | |
9e0c209e SL |
1808 | /// Non-parallelized version of rendering an item. This will take the input |
1809 | /// item, render its contents, and then invoke the specified closure with | |
1810 | /// all sub-items which need to be rendered. | |
1811 | /// | |
1812 | /// The rendering driver uses this closure to queue up more work. | |
83c7162d XL |
1813 | fn item<F>(&mut self, item: clean::Item, all: &mut AllTypes, mut f: F) -> Result<(), Error> |
1814 | where F: FnMut(&mut Context, clean::Item), | |
9e0c209e | 1815 | { |
54a0048b SL |
1816 | // Stripped modules survive the rustdoc passes (i.e. `strip-private`) |
1817 | // if they contain impls for public types. These modules can also | |
2c00a5a8 | 1818 | // contain items such as publicly re-exported structures. |
1a4d82fc JJ |
1819 | // |
1820 | // External crates will provide links to these structures, so | |
54a0048b SL |
1821 | // these modules are recursed into, but not rendered normally |
1822 | // (a flag on the context). | |
1a4d82fc | 1823 | if !self.render_redirect_pages { |
041b39d2 | 1824 | self.render_redirect_pages = item.is_stripped(); |
1a4d82fc JJ |
1825 | } |
1826 | ||
54a0048b | 1827 | if item.is_mod() { |
1a4d82fc JJ |
1828 | // modules are special because they add a namespace. We also need to |
1829 | // recurse into the items of the module as well. | |
54a0048b SL |
1830 | let name = item.name.as_ref().unwrap().to_string(); |
1831 | let mut item = Some(item); | |
1832 | self.recurse(name, |this| { | |
1833 | let item = item.take().unwrap(); | |
3157f602 XL |
1834 | |
1835 | let mut buf = Vec::new(); | |
9e0c209e | 1836 | this.render_item(&mut buf, &item, false).unwrap(); |
3157f602 XL |
1837 | // buf will be empty if the module is stripped and there is no redirect for it |
1838 | if !buf.is_empty() { | |
abe05a73 | 1839 | try_err!(this.shared.ensure_dir(&this.dst), &this.dst); |
3157f602 | 1840 | let joint_dst = this.dst.join("index.html"); |
3157f602 XL |
1841 | let mut dst = try_err!(File::create(&joint_dst), &joint_dst); |
1842 | try_err!(dst.write_all(&buf), &joint_dst); | |
1843 | } | |
c34b1796 | 1844 | |
54a0048b SL |
1845 | let m = match item.inner { |
1846 | clean::StrippedItem(box clean::ModuleItem(m)) | | |
1847 | clean::ModuleItem(m) => m, | |
1848 | _ => unreachable!() | |
1849 | }; | |
1a4d82fc | 1850 | |
9e0c209e | 1851 | // Render sidebar-items.js used throughout this module. |
3157f602 | 1852 | if !this.render_redirect_pages { |
54a0048b SL |
1853 | let items = this.build_sidebar_items(&m); |
1854 | let js_dst = this.dst.join("sidebar-items.js"); | |
1855 | let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst)); | |
1856 | try_err!(write!(&mut js_out, "initSidebarItems({});", | |
1857 | as_json(&items)), &js_dst); | |
1858 | } | |
b039eaaf | 1859 | |
54a0048b | 1860 | for item in m.items { |
83c7162d | 1861 | f(this, item); |
54a0048b | 1862 | } |
9e0c209e | 1863 | |
b039eaaf | 1864 | Ok(()) |
9e0c209e | 1865 | })?; |
54a0048b | 1866 | } else if item.name.is_some() { |
3157f602 | 1867 | let mut buf = Vec::new(); |
9e0c209e | 1868 | self.render_item(&mut buf, &item, true).unwrap(); |
3157f602 XL |
1869 | // buf will be empty if the item is stripped and there is no redirect for it |
1870 | if !buf.is_empty() { | |
9e0c209e | 1871 | let name = item.name.as_ref().unwrap(); |
c30ab7b3 | 1872 | let item_type = item.type_(); |
9e0c209e | 1873 | let file_name = &item_path(item_type, name); |
abe05a73 | 1874 | try_err!(self.shared.ensure_dir(&self.dst), &self.dst); |
9e0c209e | 1875 | let joint_dst = self.dst.join(file_name); |
3157f602 XL |
1876 | let mut dst = try_err!(File::create(&joint_dst), &joint_dst); |
1877 | try_err!(dst.write_all(&buf), &joint_dst); | |
9e0c209e | 1878 | |
83c7162d XL |
1879 | if !self.render_redirect_pages { |
1880 | all.append(full_path(self, &item), &item_type); | |
1881 | } | |
9e0c209e SL |
1882 | // Redirect from a sane URL using the namespace to Rustdoc's |
1883 | // URL for the page. | |
1884 | let redir_name = format!("{}.{}.html", name, item_type.name_space()); | |
1885 | let redir_dst = self.dst.join(redir_name); | |
abe05a73 | 1886 | if let Ok(redirect_out) = OpenOptions::new().create_new(true) |
83c7162d XL |
1887 | .write(true) |
1888 | .open(&redir_dst) { | |
abe05a73 | 1889 | let mut redirect_out = BufWriter::new(redirect_out); |
9e0c209e SL |
1890 | try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst); |
1891 | } | |
1892 | ||
1893 | // If the item is a macro, redirect from the old macro URL (with !) | |
1894 | // to the new one (without). | |
1895 | // FIXME(#35705) remove this redirect. | |
1896 | if item_type == ItemType::Macro { | |
1897 | let redir_name = format!("{}.{}!.html", item_type, name); | |
1898 | let redir_dst = self.dst.join(redir_name); | |
abe05a73 XL |
1899 | let redirect_out = try_err!(File::create(&redir_dst), &redir_dst); |
1900 | let mut redirect_out = BufWriter::new(redirect_out); | |
9e0c209e SL |
1901 | try_err!(layout::redirect(&mut redirect_out, file_name), &redir_dst); |
1902 | } | |
3157f602 | 1903 | } |
1a4d82fc | 1904 | } |
9e0c209e | 1905 | Ok(()) |
1a4d82fc JJ |
1906 | } |
1907 | ||
d9579d0f AL |
1908 | fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> { |
1909 | // BTreeMap instead of HashMap to get a sorted output | |
1910 | let mut map = BTreeMap::new(); | |
85aaf69f | 1911 | for item in &m.items { |
041b39d2 | 1912 | if item.is_stripped() { continue } |
1a4d82fc | 1913 | |
c30ab7b3 | 1914 | let short = item.type_().css_class(); |
1a4d82fc JJ |
1915 | let myname = match item.name { |
1916 | None => continue, | |
1917 | Some(ref s) => s.to_string(), | |
1918 | }; | |
1919 | let short = short.to_string(); | |
c34b1796 AL |
1920 | map.entry(short).or_insert(vec![]) |
1921 | .push((myname, Some(plain_summary_line(item.doc_value())))); | |
1a4d82fc JJ |
1922 | } |
1923 | ||
ff7c6d11 XL |
1924 | if self.shared.sort_modules_alphabetically { |
1925 | for (_, items) in &mut map { | |
1926 | items.sort(); | |
1927 | } | |
1a4d82fc | 1928 | } |
c30ab7b3 | 1929 | map |
1a4d82fc | 1930 | } |
1a4d82fc JJ |
1931 | } |
1932 | ||
1933 | impl<'a> Item<'a> { | |
1a4d82fc JJ |
1934 | /// Generate a url appropriate for an `href` attribute back to the source of |
1935 | /// this item. | |
1936 | /// | |
1937 | /// The url generated, when clicked, will redirect the browser back to the | |
1938 | /// original source code. | |
1939 | /// | |
1940 | /// If `None` is returned, then a source link couldn't be generated. This | |
1941 | /// may happen, for example, with externally inlined items where the source | |
1942 | /// of their crate documentation isn't known. | |
476ff2be SL |
1943 | fn src_href(&self) -> Option<String> { |
1944 | let mut root = self.cx.root_path(); | |
d9579d0f | 1945 | |
476ff2be SL |
1946 | let cache = cache(); |
1947 | let mut path = String::new(); | |
ff7c6d11 XL |
1948 | |
1949 | // We can safely ignore macros from other libraries | |
1950 | let file = match self.item.source.filename { | |
1951 | FileName::Real(ref path) => path, | |
1952 | _ => return None, | |
1953 | }; | |
1954 | ||
476ff2be | 1955 | let (krate, path) = if self.item.def_id.is_local() { |
ff7c6d11 | 1956 | if let Some(path) = self.cx.shared.local_sources.get(file) { |
476ff2be SL |
1957 | (&self.cx.shared.layout.krate, path) |
1958 | } else { | |
1959 | return None; | |
1960 | } | |
1a4d82fc | 1961 | } else { |
476ff2be SL |
1962 | let (krate, src_root) = match cache.extern_locations.get(&self.item.def_id.krate) { |
1963 | Some(&(ref name, ref src, Local)) => (name, src), | |
1964 | Some(&(ref name, ref src, Remote(ref s))) => { | |
1965 | root = s.to_string(); | |
1966 | (name, src) | |
1967 | } | |
1968 | Some(&(_, _, Unknown)) | None => return None, | |
1969 | }; | |
1970 | ||
476ff2be SL |
1971 | clean_srcpath(&src_root, file, false, |component| { |
1972 | path.push_str(component); | |
1973 | path.push('/'); | |
1974 | }); | |
1975 | let mut fname = file.file_name().expect("source has no filename") | |
1976 | .to_os_string(); | |
1977 | fname.push(".html"); | |
1978 | path.push_str(&fname.to_string_lossy()); | |
1979 | (krate, &path) | |
1980 | }; | |
1981 | ||
1982 | let lines = if self.item.source.loline == self.item.source.hiline { | |
1983 | format!("{}", self.item.source.loline) | |
1984 | } else { | |
1985 | format!("{}-{}", self.item.source.loline, self.item.source.hiline) | |
1986 | }; | |
1987 | Some(format!("{root}src/{krate}/{path}#{lines}", | |
abe05a73 | 1988 | root = Escape(&root), |
476ff2be SL |
1989 | krate = krate, |
1990 | path = path, | |
1991 | lines = lines)) | |
1a4d82fc JJ |
1992 | } |
1993 | } | |
1994 | ||
0531ce1d XL |
1995 | fn wrap_into_docblock<F>(w: &mut fmt::Formatter, |
1996 | f: F) -> fmt::Result | |
1997 | where F: Fn(&mut fmt::Formatter) -> fmt::Result { | |
1998 | write!(w, "<div class=\"docblock type-decl\">")?; | |
1999 | f(w)?; | |
2000 | write!(w, "</div>") | |
2001 | } | |
2002 | ||
85aaf69f | 2003 | impl<'a> fmt::Display for Item<'a> { |
1a4d82fc | 2004 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
54a0048b | 2005 | debug_assert!(!self.item.is_stripped()); |
1a4d82fc | 2006 | // Write the breadcrumb trail header for the top |
0531ce1d | 2007 | write!(fmt, "<h1 class='fqn'><span class='in-band'>")?; |
1a4d82fc JJ |
2008 | match self.item.inner { |
2009 | clean::ModuleItem(ref m) => if m.is_crate { | |
54a0048b | 2010 | write!(fmt, "Crate ")?; |
1a4d82fc | 2011 | } else { |
54a0048b | 2012 | write!(fmt, "Module ")?; |
1a4d82fc | 2013 | }, |
ea8adc8c | 2014 | clean::FunctionItem(..) | clean::ForeignFunctionItem(..) => write!(fmt, "Function ")?, |
54a0048b SL |
2015 | clean::TraitItem(..) => write!(fmt, "Trait ")?, |
2016 | clean::StructItem(..) => write!(fmt, "Struct ")?, | |
9e0c209e | 2017 | clean::UnionItem(..) => write!(fmt, "Union ")?, |
54a0048b | 2018 | clean::EnumItem(..) => write!(fmt, "Enum ")?, |
9e0c209e SL |
2019 | clean::TypedefItem(..) => write!(fmt, "Type Definition ")?, |
2020 | clean::MacroItem(..) => write!(fmt, "Macro ")?, | |
54a0048b | 2021 | clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?, |
ea8adc8c | 2022 | clean::StaticItem(..) | clean::ForeignStaticItem(..) => write!(fmt, "Static ")?, |
9e0c209e | 2023 | clean::ConstantItem(..) => write!(fmt, "Constant ")?, |
abe05a73 | 2024 | clean::ForeignTypeItem => write!(fmt, "Foreign Type ")?, |
9e0c209e SL |
2025 | _ => { |
2026 | // We don't generate pages for any other type. | |
2027 | unreachable!(); | |
2028 | } | |
1a4d82fc | 2029 | } |
5bcae85e | 2030 | if !self.item.is_primitive() { |
85aaf69f | 2031 | let cur = &self.cx.current; |
54a0048b | 2032 | let amt = if self.item.is_mod() { cur.len() - 1 } else { cur.len() }; |
1a4d82fc | 2033 | for (i, component) in cur.iter().enumerate().take(amt) { |
54a0048b SL |
2034 | write!(fmt, "<a href='{}index.html'>{}</a>::<wbr>", |
2035 | repeat("../").take(cur.len() - i - 1) | |
2036 | .collect::<String>(), | |
2037 | component)?; | |
1a4d82fc JJ |
2038 | } |
2039 | } | |
8bb4bdeb | 2040 | write!(fmt, "<a class=\"{}\" href=''>{}</a>", |
c30ab7b3 | 2041 | self.item.type_(), self.item.name.as_ref().unwrap())?; |
1a4d82fc | 2042 | |
54a0048b SL |
2043 | write!(fmt, "</span>")?; // in-band |
2044 | write!(fmt, "<span class='out-of-band'>")?; | |
a7813a04 XL |
2045 | if let Some(version) = self.item.stable_since() { |
2046 | write!(fmt, "<span class='since' title='Stable since Rust version {0}'>{0}</span>", | |
2047 | version)?; | |
2048 | } | |
54a0048b | 2049 | write!(fmt, |
83c7162d XL |
2050 | "<span id='render-detail'>\ |
2051 | <a id=\"toggle-all-docs\" href=\"javascript:void(0)\" \ | |
2052 | title=\"collapse all docs\">\ | |
2053 | [<span class='inner'>−</span>]\ | |
2054 | </a>\ | |
2055 | </span>")?; | |
1a4d82fc JJ |
2056 | |
2057 | // Write `src` tag | |
2058 | // | |
2059 | // When this item is part of a `pub use` in a downstream crate, the | |
2060 | // [src] link in the downstream documentation will actually come back to | |
2061 | // this page, and this link will be auto-clicked. The `id` attribute is | |
2062 | // used to find the link to auto-click. | |
5bcae85e | 2063 | if self.cx.shared.include_sources && !self.item.is_primitive() { |
476ff2be SL |
2064 | if let Some(l) = self.src_href() { |
2065 | write!(fmt, "<a class='srclink' href='{}' title='{}'>[src]</a>", | |
2066 | l, "goto source code")?; | |
1a4d82fc JJ |
2067 | } |
2068 | } | |
2069 | ||
0531ce1d | 2070 | write!(fmt, "</span></h1>")?; // out-of-band |
1a4d82fc JJ |
2071 | |
2072 | match self.item.inner { | |
0531ce1d XL |
2073 | clean::ModuleItem(ref m) => |
2074 | item_module(fmt, self.cx, self.item, &m.items), | |
1a4d82fc | 2075 | clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => |
e9174d1e | 2076 | item_function(fmt, self.cx, self.item, f), |
1a4d82fc | 2077 | clean::TraitItem(ref t) => item_trait(fmt, self.cx, self.item, t), |
e9174d1e | 2078 | clean::StructItem(ref s) => item_struct(fmt, self.cx, self.item, s), |
9e0c209e | 2079 | clean::UnionItem(ref s) => item_union(fmt, self.cx, self.item, s), |
e9174d1e SL |
2080 | clean::EnumItem(ref e) => item_enum(fmt, self.cx, self.item, e), |
2081 | clean::TypedefItem(ref t, _) => item_typedef(fmt, self.cx, self.item, t), | |
2082 | clean::MacroItem(ref m) => item_macro(fmt, self.cx, self.item, m), | |
2083 | clean::PrimitiveItem(ref p) => item_primitive(fmt, self.cx, self.item, p), | |
1a4d82fc | 2084 | clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => |
e9174d1e SL |
2085 | item_static(fmt, self.cx, self.item, i), |
2086 | clean::ConstantItem(ref c) => item_constant(fmt, self.cx, self.item, c), | |
abe05a73 | 2087 | clean::ForeignTypeItem => item_foreign_type(fmt, self.cx, self.item), |
9e0c209e SL |
2088 | _ => { |
2089 | // We don't generate pages for any other type. | |
2090 | unreachable!(); | |
2091 | } | |
1a4d82fc JJ |
2092 | } |
2093 | } | |
2094 | } | |
2095 | ||
3157f602 XL |
2096 | fn item_path(ty: ItemType, name: &str) -> String { |
2097 | match ty { | |
2098 | ItemType::Module => format!("{}/index.html", name), | |
9e0c209e | 2099 | _ => format!("{}.{}.html", ty.css_class(), name), |
1a4d82fc JJ |
2100 | } |
2101 | } | |
2102 | ||
2103 | fn full_path(cx: &Context, item: &clean::Item) -> String { | |
c1a9b12d | 2104 | let mut s = cx.current.join("::"); |
1a4d82fc | 2105 | s.push_str("::"); |
85aaf69f | 2106 | s.push_str(item.name.as_ref().unwrap()); |
c30ab7b3 | 2107 | s |
1a4d82fc JJ |
2108 | } |
2109 | ||
c34b1796 | 2110 | fn shorter<'a>(s: Option<&'a str>) -> String { |
1a4d82fc | 2111 | match s { |
ff7c6d11 XL |
2112 | Some(s) => s.lines() |
2113 | .skip_while(|s| s.chars().all(|c| c.is_whitespace())) | |
2114 | .take_while(|line|{ | |
c34b1796 AL |
2115 | (*line).chars().any(|chr|{ |
2116 | !chr.is_whitespace() | |
2117 | }) | |
c1a9b12d | 2118 | }).collect::<Vec<_>>().join("\n"), |
c34b1796 | 2119 | None => "".to_string() |
1a4d82fc JJ |
2120 | } |
2121 | } | |
2122 | ||
85aaf69f | 2123 | #[inline] |
c34b1796 AL |
2124 | fn plain_summary_line(s: Option<&str>) -> String { |
2125 | let line = shorter(s).replace("\n", " "); | |
2126 | markdown::plain_summary_line(&line[..]) | |
85aaf69f SL |
2127 | } |
2128 | ||
e9174d1e | 2129 | fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result { |
ff7c6d11 XL |
2130 | if let Some(ref name) = item.name { |
2131 | info!("Documenting {}", name); | |
2132 | } | |
3157f602 | 2133 | document_stability(w, cx, item)?; |
041b39d2 | 2134 | let prefix = render_assoc_const_value(item); |
ea8adc8c | 2135 | document_full(w, item, cx, &prefix)?; |
1a4d82fc JJ |
2136 | Ok(()) |
2137 | } | |
2138 | ||
0531ce1d | 2139 | /// Render md_text as markdown. |
ea8adc8c XL |
2140 | fn render_markdown(w: &mut fmt::Formatter, |
2141 | md_text: &str, | |
2c00a5a8 | 2142 | links: Vec<(String, String)>, |
0531ce1d | 2143 | prefix: &str,) |
ea8adc8c | 2144 | -> fmt::Result { |
0531ce1d | 2145 | write!(w, "<div class='docblock'>{}{}</div>", prefix, Markdown(md_text, &links)) |
ea8adc8c XL |
2146 | } |
2147 | ||
cc61c64b | 2148 | fn document_short(w: &mut fmt::Formatter, item: &clean::Item, link: AssocItemLink, |
0531ce1d | 2149 | prefix: &str) -> fmt::Result { |
a7813a04 XL |
2150 | if let Some(s) = item.doc_value() { |
2151 | let markdown = if s.contains('\n') { | |
2152 | format!("{} [Read more]({})", | |
2153 | &plain_summary_line(Some(s)), naive_assoc_href(item, link)) | |
2154 | } else { | |
2155 | format!("{}", &plain_summary_line(Some(s))) | |
2156 | }; | |
0531ce1d | 2157 | render_markdown(w, &markdown, item.links(), prefix)?; |
041b39d2 XL |
2158 | } else if !prefix.is_empty() { |
2159 | write!(w, "<div class='docblock'>{}</div>", prefix)?; | |
a7813a04 XL |
2160 | } |
2161 | Ok(()) | |
2162 | } | |
2163 | ||
041b39d2 | 2164 | fn render_assoc_const_value(item: &clean::Item) -> String { |
8bb4bdeb | 2165 | match item.inner { |
041b39d2 XL |
2166 | clean::AssociatedConstItem(ref ty, Some(ref default)) => { |
2167 | highlight::render_with_highlighting( | |
2168 | &format!("{}: {:#} = {}", item.name.as_ref().unwrap(), ty, default), | |
2169 | None, | |
2170 | None, | |
2171 | None, | |
ea8adc8c | 2172 | None, |
041b39d2 | 2173 | ) |
8bb4bdeb XL |
2174 | } |
2175 | _ => String::new(), | |
2176 | } | |
2177 | } | |
2178 | ||
cc61c64b | 2179 | fn document_full(w: &mut fmt::Formatter, item: &clean::Item, |
ea8adc8c | 2180 | cx: &Context, prefix: &str) -> fmt::Result { |
ff7c6d11 XL |
2181 | if let Some(s) = cx.shared.maybe_collapsed_doc_value(item) { |
2182 | debug!("Doc block: =====\n{}\n=====", s); | |
0531ce1d | 2183 | render_markdown(w, &*s, item.links(), prefix)?; |
041b39d2 XL |
2184 | } else if !prefix.is_empty() { |
2185 | write!(w, "<div class='docblock'>{}</div>", prefix)?; | |
3157f602 XL |
2186 | } |
2187 | Ok(()) | |
2188 | } | |
2189 | ||
2190 | fn document_stability(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result { | |
32a655c1 SL |
2191 | let stabilities = short_stability(item, cx, true); |
2192 | if !stabilities.is_empty() { | |
2193 | write!(w, "<div class='stability'>")?; | |
2194 | for stability in stabilities { | |
2195 | write!(w, "{}", stability)?; | |
2196 | } | |
2197 | write!(w, "</div>")?; | |
3157f602 XL |
2198 | } |
2199 | Ok(()) | |
2200 | } | |
2201 | ||
cc61c64b XL |
2202 | fn name_key(name: &str) -> (&str, u64, usize) { |
2203 | // find number at end | |
2204 | let split = name.bytes().rposition(|b| b < b'0' || b'9' < b).map_or(0, |s| s + 1); | |
2205 | ||
2206 | // count leading zeroes | |
2207 | let after_zeroes = | |
2208 | name[split..].bytes().position(|b| b != b'0').map_or(name.len(), |extra| split + extra); | |
2209 | ||
2210 | // sort leading zeroes last | |
2211 | let num_zeroes = after_zeroes - split; | |
2212 | ||
2213 | match name[split..].parse() { | |
2214 | Ok(n) => (&name[..split], n, num_zeroes), | |
2215 | Err(_) => (name, 0, num_zeroes), | |
2216 | } | |
2217 | } | |
2218 | ||
1a4d82fc JJ |
2219 | fn item_module(w: &mut fmt::Formatter, cx: &Context, |
2220 | item: &clean::Item, items: &[clean::Item]) -> fmt::Result { | |
54a0048b | 2221 | document(w, cx, item)?; |
1a4d82fc | 2222 | |
0531ce1d | 2223 | let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::<Vec<usize>>(); |
1a4d82fc JJ |
2224 | |
2225 | // the order of item types in the listing | |
2226 | fn reorder(ty: ItemType) -> u8 { | |
2227 | match ty { | |
85aaf69f SL |
2228 | ItemType::ExternCrate => 0, |
2229 | ItemType::Import => 1, | |
2230 | ItemType::Primitive => 2, | |
2231 | ItemType::Module => 3, | |
2232 | ItemType::Macro => 4, | |
2233 | ItemType::Struct => 5, | |
2234 | ItemType::Enum => 6, | |
2235 | ItemType::Constant => 7, | |
2236 | ItemType::Static => 8, | |
2237 | ItemType::Trait => 9, | |
2238 | ItemType::Function => 10, | |
2239 | ItemType::Typedef => 12, | |
9e0c209e SL |
2240 | ItemType::Union => 13, |
2241 | _ => 14 + ty as u8, | |
1a4d82fc JJ |
2242 | } |
2243 | } | |
2244 | ||
c34b1796 | 2245 | fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering { |
c30ab7b3 SL |
2246 | let ty1 = i1.type_(); |
2247 | let ty2 = i2.type_(); | |
d9579d0f AL |
2248 | if ty1 != ty2 { |
2249 | return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)) | |
2250 | } | |
2251 | let s1 = i1.stability.as_ref().map(|s| s.level); | |
2252 | let s2 = i2.stability.as_ref().map(|s| s.level); | |
2253 | match (s1, s2) { | |
b039eaaf SL |
2254 | (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater, |
2255 | (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less, | |
d9579d0f | 2256 | _ => {} |
1a4d82fc | 2257 | } |
cc61c64b XL |
2258 | let lhs = i1.name.as_ref().map_or("", |s| &**s); |
2259 | let rhs = i2.name.as_ref().map_or("", |s| &**s); | |
2260 | name_key(lhs).cmp(&name_key(rhs)) | |
1a4d82fc JJ |
2261 | } |
2262 | ||
ff7c6d11 XL |
2263 | if cx.shared.sort_modules_alphabetically { |
2264 | indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); | |
2265 | } | |
2c00a5a8 | 2266 | // This call is to remove re-export duplicates in cases such as: |
3b2f2976 XL |
2267 | // |
2268 | // ``` | |
2269 | // pub mod foo { | |
2270 | // pub mod bar { | |
2271 | // pub trait Double { fn foo(); } | |
2272 | // } | |
2273 | // } | |
2274 | // | |
2275 | // pub use foo::bar::*; | |
2276 | // pub use foo::*; | |
2277 | // ``` | |
2278 | // | |
2279 | // `Double` will appear twice in the generated docs. | |
2280 | // | |
2281 | // FIXME: This code is quite ugly and could be improved. Small issue: DefId | |
2282 | // can be identical even if the elements are different (mostly in imports). | |
2283 | // So in case this is an import, we keep everything by adding a "unique id" | |
2284 | // (which is the position in the vector). | |
2285 | indices.dedup_by_key(|i| (items[*i].def_id, | |
2286 | if items[*i].name.as_ref().is_some() { | |
2287 | Some(full_path(cx, &items[*i]).clone()) | |
2288 | } else { | |
2289 | None | |
2290 | }, | |
2291 | items[*i].type_(), | |
2292 | if items[*i].is_import() { | |
2293 | *i | |
2294 | } else { | |
2295 | 0 | |
2296 | })); | |
1a4d82fc JJ |
2297 | |
2298 | debug!("{:?}", indices); | |
2299 | let mut curty = None; | |
85aaf69f | 2300 | for &idx in &indices { |
1a4d82fc | 2301 | let myitem = &items[idx]; |
54a0048b SL |
2302 | if myitem.is_stripped() { |
2303 | continue; | |
2304 | } | |
1a4d82fc | 2305 | |
c30ab7b3 | 2306 | let myty = Some(myitem.type_()); |
85aaf69f SL |
2307 | if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { |
2308 | // Put `extern crate` and `use` re-exports in the same section. | |
2309 | curty = myty; | |
2310 | } else if myty != curty { | |
1a4d82fc | 2311 | if curty.is_some() { |
54a0048b | 2312 | write!(w, "</table>")?; |
1a4d82fc JJ |
2313 | } |
2314 | curty = myty; | |
2315 | let (short, name) = match myty.unwrap() { | |
85aaf69f | 2316 | ItemType::ExternCrate | |
2c00a5a8 | 2317 | ItemType::Import => ("reexports", "Re-exports"), |
1a4d82fc JJ |
2318 | ItemType::Module => ("modules", "Modules"), |
2319 | ItemType::Struct => ("structs", "Structs"), | |
9e0c209e | 2320 | ItemType::Union => ("unions", "Unions"), |
1a4d82fc JJ |
2321 | ItemType::Enum => ("enums", "Enums"), |
2322 | ItemType::Function => ("functions", "Functions"), | |
2323 | ItemType::Typedef => ("types", "Type Definitions"), | |
2324 | ItemType::Static => ("statics", "Statics"), | |
2325 | ItemType::Constant => ("constants", "Constants"), | |
2326 | ItemType::Trait => ("traits", "Traits"), | |
2327 | ItemType::Impl => ("impls", "Implementations"), | |
1a4d82fc JJ |
2328 | ItemType::TyMethod => ("tymethods", "Type Methods"), |
2329 | ItemType::Method => ("methods", "Methods"), | |
2330 | ItemType::StructField => ("fields", "Struct Fields"), | |
2331 | ItemType::Variant => ("variants", "Variants"), | |
2332 | ItemType::Macro => ("macros", "Macros"), | |
2333 | ItemType::Primitive => ("primitives", "Primitive Types"), | |
2334 | ItemType::AssociatedType => ("associated-types", "Associated Types"), | |
d9579d0f | 2335 | ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), |
abe05a73 | 2336 | ItemType::ForeignType => ("foreign-types", "Foreign Types"), |
1a4d82fc | 2337 | }; |
54a0048b SL |
2338 | write!(w, "<h2 id='{id}' class='section-header'>\ |
2339 | <a href=\"#{id}\">{name}</a></h2>\n<table>", | |
2340 | id = derive_id(short.to_owned()), name = name)?; | |
1a4d82fc JJ |
2341 | } |
2342 | ||
2343 | match myitem.inner { | |
85aaf69f | 2344 | clean::ExternCrateItem(ref name, ref src) => { |
a7813a04 XL |
2345 | use html::format::HRef; |
2346 | ||
85aaf69f SL |
2347 | match *src { |
2348 | Some(ref src) => { | |
54a0048b SL |
2349 | write!(w, "<tr><td><code>{}extern crate {} as {};", |
2350 | VisSpace(&myitem.visibility), | |
a7813a04 | 2351 | HRef::new(myitem.def_id, src), |
54a0048b | 2352 | name)? |
1a4d82fc | 2353 | } |
85aaf69f | 2354 | None => { |
54a0048b | 2355 | write!(w, "<tr><td><code>{}extern crate {};", |
a7813a04 XL |
2356 | VisSpace(&myitem.visibility), |
2357 | HRef::new(myitem.def_id, name))? | |
1a4d82fc JJ |
2358 | } |
2359 | } | |
54a0048b | 2360 | write!(w, "</code></td></tr>")?; |
85aaf69f | 2361 | } |
1a4d82fc | 2362 | |
85aaf69f | 2363 | clean::ImportItem(ref import) => { |
54a0048b SL |
2364 | write!(w, "<tr><td><code>{}{}</code></td></tr>", |
2365 | VisSpace(&myitem.visibility), *import)?; | |
1a4d82fc JJ |
2366 | } |
2367 | ||
2368 | _ => { | |
2369 | if myitem.name.is_none() { continue } | |
a7813a04 XL |
2370 | |
2371 | let stabilities = short_stability(myitem, cx, false); | |
2372 | ||
2373 | let stab_docs = if !stabilities.is_empty() { | |
2374 | stabilities.iter() | |
2375 | .map(|s| format!("[{}]", s)) | |
2376 | .collect::<Vec<_>>() | |
2377 | .as_slice() | |
2378 | .join(" ") | |
d9579d0f AL |
2379 | } else { |
2380 | String::new() | |
2381 | }; | |
476ff2be | 2382 | |
8bb4bdeb XL |
2383 | let unsafety_flag = match myitem.inner { |
2384 | clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) | |
2385 | if func.unsafety == hir::Unsafety::Unsafe => { | |
2386 | "<a title='unsafe function' href='#'><sup>âš </sup></a>" | |
476ff2be | 2387 | } |
8bb4bdeb XL |
2388 | _ => "", |
2389 | }; | |
476ff2be | 2390 | |
7453a54e | 2391 | let doc_value = myitem.doc_value().unwrap_or(""); |
54a0048b SL |
2392 | write!(w, " |
2393 | <tr class='{stab} module-item'> | |
8bb4bdeb XL |
2394 | <td><a class=\"{class}\" href=\"{href}\" |
2395 | title='{title_type} {title}'>{name}</a>{unsafety_flag}</td> | |
9e0c209e | 2396 | <td class='docblock-short'> |
54a0048b SL |
2397 | {stab_docs} {docs} |
2398 | </td> | |
2399 | </tr>", | |
2400 | name = *myitem.name.as_ref().unwrap(), | |
2401 | stab_docs = stab_docs, | |
0531ce1d | 2402 | docs = MarkdownSummaryLine(doc_value, &myitem.links()), |
c30ab7b3 | 2403 | class = myitem.type_(), |
8bb4bdeb | 2404 | stab = myitem.stability_class().unwrap_or("".to_string()), |
476ff2be | 2405 | unsafety_flag = unsafety_flag, |
c30ab7b3 | 2406 | href = item_path(myitem.type_(), myitem.name.as_ref().unwrap()), |
8bb4bdeb | 2407 | title_type = myitem.type_(), |
54a0048b | 2408 | title = full_path(cx, myitem))?; |
1a4d82fc JJ |
2409 | } |
2410 | } | |
2411 | } | |
2412 | ||
3157f602 XL |
2413 | if curty.is_some() { |
2414 | write!(w, "</table>")?; | |
2415 | } | |
2416 | Ok(()) | |
1a4d82fc JJ |
2417 | } |
2418 | ||
a7813a04 XL |
2419 | fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Vec<String> { |
2420 | let mut stability = vec![]; | |
2421 | ||
2422 | if let Some(stab) = item.stability.as_ref() { | |
476ff2be SL |
2423 | let deprecated_reason = if show_reason && !stab.deprecated_reason.is_empty() { |
2424 | format!(": {}", stab.deprecated_reason) | |
d9579d0f AL |
2425 | } else { |
2426 | String::new() | |
2427 | }; | |
a7813a04 | 2428 | if !stab.deprecated_since.is_empty() { |
d9579d0f AL |
2429 | let since = if show_reason { |
2430 | format!(" since {}", Escape(&stab.deprecated_since)) | |
2431 | } else { | |
2432 | String::new() | |
2433 | }; | |
83c7162d XL |
2434 | let text = if stability::deprecation_in_effect(&stab.deprecated_since) { |
2435 | format!("Deprecated{}{}", | |
2436 | since, | |
2437 | MarkdownHtml(&deprecated_reason)) | |
2438 | } else { | |
2439 | format!("Deprecating in {}{}", | |
2440 | Escape(&stab.deprecated_since), | |
2441 | MarkdownHtml(&deprecated_reason)) | |
2442 | }; | |
32a655c1 | 2443 | stability.push(format!("<div class='stab deprecated'>{}</div>", text)) |
a7813a04 XL |
2444 | }; |
2445 | ||
2446 | if stab.level == stability::Unstable { | |
32a655c1 SL |
2447 | if show_reason { |
2448 | let unstable_extra = match (!stab.feature.is_empty(), | |
2449 | &cx.shared.issue_tracker_base_url, | |
2450 | stab.issue) { | |
9cc50fc6 | 2451 | (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => |
8bb4bdeb | 2452 | format!(" (<code>{} </code><a href=\"{}{}\">#{}</a>)", |
e9174d1e | 2453 | Escape(&stab.feature), tracker_url, issue_no, issue_no), |
9cc50fc6 | 2454 | (false, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => |
e9174d1e SL |
2455 | format!(" (<a href=\"{}{}\">#{}</a>)", Escape(&tracker_url), issue_no, |
2456 | issue_no), | |
9e0c209e | 2457 | (true, ..) => |
e9174d1e SL |
2458 | format!(" (<code>{}</code>)", Escape(&stab.feature)), |
2459 | _ => String::new(), | |
32a655c1 SL |
2460 | }; |
2461 | if stab.unstable_reason.is_empty() { | |
2462 | stability.push(format!("<div class='stab unstable'>\ | |
2463 | <span class=microscope>🔬</span> \ | |
8bb4bdeb | 2464 | This is a nightly-only experimental API. {}\ |
32a655c1 | 2465 | </div>", |
8bb4bdeb | 2466 | unstable_extra)); |
32a655c1 SL |
2467 | } else { |
2468 | let text = format!("<summary><span class=microscope>🔬</span> \ | |
8bb4bdeb | 2469 | This is a nightly-only experimental API. {}\ |
32a655c1 | 2470 | </summary>{}", |
cc61c64b | 2471 | unstable_extra, |
0531ce1d | 2472 | MarkdownHtml(&stab.unstable_reason)); |
32a655c1 SL |
2473 | stability.push(format!("<div class='stab unstable'><details>{}</details></div>", |
2474 | text)); | |
e9174d1e SL |
2475 | } |
2476 | } else { | |
32a655c1 SL |
2477 | stability.push(format!("<div class='stab unstable'>Experimental</div>")) |
2478 | } | |
a7813a04 XL |
2479 | }; |
2480 | } else if let Some(depr) = item.deprecation.as_ref() { | |
2481 | let note = if show_reason && !depr.note.is_empty() { | |
2482 | format!(": {}", depr.note) | |
d9579d0f | 2483 | } else { |
a7813a04 XL |
2484 | String::new() |
2485 | }; | |
2486 | let since = if show_reason && !depr.since.is_empty() { | |
2487 | format!(" since {}", Escape(&depr.since)) | |
2488 | } else { | |
2489 | String::new() | |
d9579d0f | 2490 | }; |
9cc50fc6 | 2491 | |
83c7162d XL |
2492 | let text = if stability::deprecation_in_effect(&depr.since) { |
2493 | format!("Deprecated{}{}", | |
2494 | since, | |
2495 | MarkdownHtml(¬e)) | |
2496 | } else { | |
2497 | format!("Deprecating in {}{}", | |
2498 | Escape(&depr.since), | |
2499 | MarkdownHtml(¬e)) | |
2500 | }; | |
32a655c1 | 2501 | stability.push(format!("<div class='stab deprecated'>{}</div>", text)) |
a7813a04 XL |
2502 | } |
2503 | ||
3b2f2976 XL |
2504 | if let Some(ref cfg) = item.attrs.cfg { |
2505 | stability.push(format!("<div class='stab portability'>{}</div>", if show_reason { | |
2506 | cfg.render_long_html() | |
2507 | } else { | |
2508 | cfg.render_short_html() | |
2509 | })); | |
2510 | } | |
2511 | ||
a7813a04 | 2512 | stability |
d9579d0f AL |
2513 | } |
2514 | ||
1a4d82fc JJ |
2515 | struct Initializer<'a>(&'a str); |
2516 | ||
85aaf69f | 2517 | impl<'a> fmt::Display for Initializer<'a> { |
1a4d82fc JJ |
2518 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
2519 | let Initializer(s) = *self; | |
9346a6ac | 2520 | if s.is_empty() { return Ok(()); } |
54a0048b | 2521 | write!(f, "<code> = </code>")?; |
a7813a04 | 2522 | write!(f, "<code>{}</code>", Escape(s)) |
1a4d82fc JJ |
2523 | } |
2524 | } | |
2525 | ||
e9174d1e | 2526 | fn item_constant(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2527 | c: &clean::Constant) -> fmt::Result { |
8bb4bdeb XL |
2528 | write!(w, "<pre class='rust const'>")?; |
2529 | render_attributes(w, it)?; | |
2530 | write!(w, "{vis}const \ | |
54a0048b SL |
2531 | {name}: {typ}{init}</pre>", |
2532 | vis = VisSpace(&it.visibility), | |
85aaf69f | 2533 | name = it.name.as_ref().unwrap(), |
1a4d82fc | 2534 | typ = c.type_, |
54a0048b | 2535 | init = Initializer(&c.expr))?; |
e9174d1e | 2536 | document(w, cx, it) |
1a4d82fc JJ |
2537 | } |
2538 | ||
e9174d1e | 2539 | fn item_static(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2540 | s: &clean::Static) -> fmt::Result { |
8bb4bdeb XL |
2541 | write!(w, "<pre class='rust static'>")?; |
2542 | render_attributes(w, it)?; | |
2543 | write!(w, "{vis}static {mutability}\ | |
54a0048b SL |
2544 | {name}: {typ}{init}</pre>", |
2545 | vis = VisSpace(&it.visibility), | |
1a4d82fc | 2546 | mutability = MutableSpace(s.mutability), |
85aaf69f | 2547 | name = it.name.as_ref().unwrap(), |
1a4d82fc | 2548 | typ = s.type_, |
54a0048b | 2549 | init = Initializer(&s.expr))?; |
e9174d1e | 2550 | document(w, cx, it) |
1a4d82fc JJ |
2551 | } |
2552 | ||
e9174d1e | 2553 | fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2554 | f: &clean::Function) -> fmt::Result { |
cc61c64b XL |
2555 | let name_len = format!("{}{}{}{:#}fn {}{:#}", |
2556 | VisSpace(&it.visibility), | |
ea8adc8c | 2557 | ConstnessSpace(f.constness), |
cc61c64b XL |
2558 | UnsafetySpace(f.unsafety), |
2559 | AbiSpace(f.abi), | |
2560 | it.name.as_ref().unwrap(), | |
2561 | f.generics).len(); | |
ff7c6d11 | 2562 | write!(w, "{}<pre class='rust fn'>", render_spotlight_traits(it)?)?; |
8bb4bdeb | 2563 | render_attributes(w, it)?; |
ff7c6d11 XL |
2564 | write!(w, |
2565 | "{vis}{constness}{unsafety}{abi}fn {name}{generics}{decl}{where_clause}</pre>", | |
54a0048b | 2566 | vis = VisSpace(&it.visibility), |
ea8adc8c | 2567 | constness = ConstnessSpace(f.constness), |
1a4d82fc | 2568 | unsafety = UnsafetySpace(f.unsafety), |
9346a6ac | 2569 | abi = AbiSpace(f.abi), |
85aaf69f | 2570 | name = it.name.as_ref().unwrap(), |
1a4d82fc | 2571 | generics = f.generics, |
cc61c64b XL |
2572 | where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true }, |
2573 | decl = Method { | |
ff7c6d11 XL |
2574 | decl: &f.decl, |
2575 | name_len, | |
2576 | indent: 0, | |
cc61c64b | 2577 | })?; |
e9174d1e | 2578 | document(w, cx, it) |
1a4d82fc JJ |
2579 | } |
2580 | ||
0531ce1d XL |
2581 | fn render_implementor(cx: &Context, implementor: &Impl, w: &mut fmt::Formatter, |
2582 | implementor_dups: &FxHashMap<&str, (DefId, bool)>) -> Result<(), fmt::Error> { | |
2583 | write!(w, "<li><table class='table-display'><tbody><tr><td><code>")?; | |
2584 | // If there's already another implementor that has the same abbridged name, use the | |
2585 | // full path, for example in `std::iter::ExactSizeIterator` | |
2586 | let use_absolute = match implementor.inner_impl().for_ { | |
2587 | clean::ResolvedPath { ref path, is_generic: false, .. } | | |
2588 | clean::BorrowedRef { | |
2589 | type_: box clean::ResolvedPath { ref path, is_generic: false, .. }, | |
2590 | .. | |
2591 | } => implementor_dups[path.last_name()].1, | |
2592 | _ => false, | |
2593 | }; | |
2594 | fmt_impl_for_trait_page(&implementor.inner_impl(), w, use_absolute)?; | |
2595 | for it in &implementor.inner_impl().items { | |
2596 | if let clean::TypedefItem(ref tydef, _) = it.inner { | |
2597 | write!(w, "<span class=\"where fmt-newline\"> ")?; | |
2598 | assoc_type(w, it, &vec![], Some(&tydef.type_), AssocItemLink::Anchor(None))?; | |
2599 | write!(w, ";</span>")?; | |
2600 | } | |
2601 | } | |
2602 | write!(w, "</code><td>")?; | |
2603 | if let Some(l) = (Item { cx, item: &implementor.impl_item }).src_href() { | |
2604 | write!(w, "<div class='out-of-band'>")?; | |
2605 | write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", | |
2606 | l, "goto source code")?; | |
2607 | write!(w, "</div>")?; | |
2608 | } | |
2609 | writeln!(w, "</td></tr></tbody></table></li>")?; | |
2610 | Ok(()) | |
2611 | } | |
2612 | ||
2613 | fn render_impls(cx: &Context, w: &mut fmt::Formatter, | |
83c7162d | 2614 | traits: &[&&Impl], |
0531ce1d | 2615 | containing_item: &clean::Item) -> Result<(), fmt::Error> { |
83c7162d | 2616 | for i in traits { |
0531ce1d XL |
2617 | let did = i.trait_did().unwrap(); |
2618 | let assoc_link = AssocItemLink::GotoSource(did, &i.inner_impl().provided_trait_methods); | |
2619 | render_impl(w, cx, i, assoc_link, | |
2620 | RenderMode::Normal, containing_item.stable_since(), true)?; | |
2621 | } | |
2622 | Ok(()) | |
2623 | } | |
2624 | ||
1a4d82fc JJ |
2625 | fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
2626 | t: &clean::Trait) -> fmt::Result { | |
2627 | let mut bounds = String::new(); | |
476ff2be | 2628 | let mut bounds_plain = String::new(); |
9346a6ac AL |
2629 | if !t.bounds.is_empty() { |
2630 | if !bounds.is_empty() { | |
1a4d82fc | 2631 | bounds.push(' '); |
476ff2be | 2632 | bounds_plain.push(' '); |
1a4d82fc JJ |
2633 | } |
2634 | bounds.push_str(": "); | |
476ff2be | 2635 | bounds_plain.push_str(": "); |
1a4d82fc | 2636 | for (i, p) in t.bounds.iter().enumerate() { |
476ff2be SL |
2637 | if i > 0 { |
2638 | bounds.push_str(" + "); | |
2639 | bounds_plain.push_str(" + "); | |
2640 | } | |
85aaf69f | 2641 | bounds.push_str(&format!("{}", *p)); |
476ff2be | 2642 | bounds_plain.push_str(&format!("{:#}", *p)); |
1a4d82fc JJ |
2643 | } |
2644 | } | |
2645 | ||
54a0048b SL |
2646 | let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); |
2647 | let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); | |
2648 | let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); | |
2649 | let provided = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>(); | |
1a4d82fc | 2650 | |
0531ce1d XL |
2651 | // Output the trait definition |
2652 | wrap_into_docblock(w, |w| { | |
2653 | write!(w, "<pre class='rust trait'>")?; | |
2654 | render_attributes(w, it)?; | |
2655 | write!(w, "{}{}{}trait {}{}{}", | |
2656 | VisSpace(&it.visibility), | |
2657 | UnsafetySpace(t.unsafety), | |
2658 | if t.is_auto { "auto " } else { "" }, | |
2659 | it.name.as_ref().unwrap(), | |
2660 | t.generics, | |
2661 | bounds)?; | |
2662 | ||
2663 | if !t.generics.where_predicates.is_empty() { | |
2664 | write!(w, "{}", WhereClause { gens: &t.generics, indent: 0, end_newline: true })?; | |
2665 | } else { | |
2666 | write!(w, " ")?; | |
1a4d82fc | 2667 | } |
041b39d2 | 2668 | |
0531ce1d XL |
2669 | if t.items.is_empty() { |
2670 | write!(w, "{{ }}")?; | |
2671 | } else { | |
2672 | // FIXME: we should be using a derived_id for the Anchors here | |
2673 | write!(w, "{{\n")?; | |
2674 | for t in &types { | |
2675 | write!(w, " ")?; | |
2676 | render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait)?; | |
2677 | write!(w, ";\n")?; | |
041b39d2 | 2678 | } |
0531ce1d XL |
2679 | if !types.is_empty() && !consts.is_empty() { |
2680 | w.write_str("\n")?; | |
2681 | } | |
2682 | for t in &consts { | |
2683 | write!(w, " ")?; | |
2684 | render_assoc_item(w, t, AssocItemLink::Anchor(None), ItemType::Trait)?; | |
2685 | write!(w, ";\n")?; | |
2686 | } | |
2687 | if !consts.is_empty() && !required.is_empty() { | |
2688 | w.write_str("\n")?; | |
2689 | } | |
2690 | for (pos, m) in required.iter().enumerate() { | |
2691 | write!(w, " ")?; | |
2692 | render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait)?; | |
2693 | write!(w, ";\n")?; | |
2694 | ||
2695 | if pos < required.len() - 1 { | |
2696 | write!(w, "<div class='item-spacer'></div>")?; | |
2697 | } | |
2698 | } | |
2699 | if !required.is_empty() && !provided.is_empty() { | |
2700 | w.write_str("\n")?; | |
cc61c64b | 2701 | } |
0531ce1d XL |
2702 | for (pos, m) in provided.iter().enumerate() { |
2703 | write!(w, " ")?; | |
2704 | render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait)?; | |
2705 | match m.inner { | |
2706 | clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => { | |
2707 | write!(w, ",\n {{ ... }}\n")?; | |
2708 | }, | |
2709 | _ => { | |
2710 | write!(w, " {{ ... }}\n")?; | |
2711 | }, | |
2712 | } | |
2713 | if pos < provided.len() - 1 { | |
2714 | write!(w, "<div class='item-spacer'></div>")?; | |
2715 | } | |
041b39d2 | 2716 | } |
0531ce1d | 2717 | write!(w, "}}")?; |
1a4d82fc | 2718 | } |
0531ce1d XL |
2719 | write!(w, "</pre>") |
2720 | })?; | |
1a4d82fc JJ |
2721 | |
2722 | // Trait documentation | |
54a0048b | 2723 | document(w, cx, it)?; |
1a4d82fc | 2724 | |
7453a54e | 2725 | fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item, t: &clean::Item) |
1a4d82fc | 2726 | -> fmt::Result { |
92a42be0 | 2727 | let name = m.name.as_ref().unwrap(); |
c30ab7b3 | 2728 | let item_type = m.type_(); |
9e0c209e SL |
2729 | let id = derive_id(format!("{}.{}", item_type, name)); |
2730 | let ns_id = derive_id(format!("{}.{}", name, item_type.name_space())); | |
ff7c6d11 | 2731 | write!(w, "{extra}<h3 id='{id}' class='method'>\ |
9e0c209e | 2732 | <span id='{ns_id}' class='invisible'><code>", |
ff7c6d11 | 2733 | extra = render_spotlight_traits(m)?, |
54a0048b | 2734 | id = id, |
9e0c209e | 2735 | ns_id = ns_id)?; |
476ff2be | 2736 | render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl)?; |
54a0048b SL |
2737 | write!(w, "</code>")?; |
2738 | render_stability_since(w, m, t)?; | |
9e0c209e | 2739 | write!(w, "</span></h3>")?; |
54a0048b | 2740 | document(w, cx, m)?; |
1a4d82fc JJ |
2741 | Ok(()) |
2742 | } | |
2743 | ||
9346a6ac | 2744 | if !types.is_empty() { |
54a0048b | 2745 | write!(w, " |
3b2f2976 XL |
2746 | <h2 id='associated-types' class='small-section-header'> |
2747 | Associated Types<a href='#associated-types' class='anchor'></a> | |
2748 | </h2> | |
1a4d82fc | 2749 | <div class='methods'> |
54a0048b | 2750 | ")?; |
85aaf69f | 2751 | for t in &types { |
54a0048b | 2752 | trait_item(w, cx, *t, it)?; |
1a4d82fc | 2753 | } |
54a0048b | 2754 | write!(w, "</div>")?; |
1a4d82fc JJ |
2755 | } |
2756 | ||
d9579d0f | 2757 | if !consts.is_empty() { |
54a0048b | 2758 | write!(w, " |
3b2f2976 XL |
2759 | <h2 id='associated-const' class='small-section-header'> |
2760 | Associated Constants<a href='#associated-const' class='anchor'></a> | |
2761 | </h2> | |
d9579d0f | 2762 | <div class='methods'> |
54a0048b | 2763 | ")?; |
d9579d0f | 2764 | for t in &consts { |
54a0048b | 2765 | trait_item(w, cx, *t, it)?; |
d9579d0f | 2766 | } |
54a0048b | 2767 | write!(w, "</div>")?; |
d9579d0f AL |
2768 | } |
2769 | ||
1a4d82fc | 2770 | // Output the documentation for each function individually |
9346a6ac | 2771 | if !required.is_empty() { |
54a0048b | 2772 | write!(w, " |
3b2f2976 XL |
2773 | <h2 id='required-methods' class='small-section-header'> |
2774 | Required Methods<a href='#required-methods' class='anchor'></a> | |
2775 | </h2> | |
1a4d82fc | 2776 | <div class='methods'> |
54a0048b | 2777 | ")?; |
85aaf69f | 2778 | for m in &required { |
54a0048b | 2779 | trait_item(w, cx, *m, it)?; |
1a4d82fc | 2780 | } |
54a0048b | 2781 | write!(w, "</div>")?; |
1a4d82fc | 2782 | } |
9346a6ac | 2783 | if !provided.is_empty() { |
54a0048b | 2784 | write!(w, " |
3b2f2976 XL |
2785 | <h2 id='provided-methods' class='small-section-header'> |
2786 | Provided Methods<a href='#provided-methods' class='anchor'></a> | |
2787 | </h2> | |
1a4d82fc | 2788 | <div class='methods'> |
54a0048b | 2789 | ")?; |
85aaf69f | 2790 | for m in &provided { |
54a0048b | 2791 | trait_item(w, cx, *m, it)?; |
1a4d82fc | 2792 | } |
54a0048b | 2793 | write!(w, "</div>")?; |
1a4d82fc JJ |
2794 | } |
2795 | ||
9346a6ac | 2796 | // If there are methods directly on this trait object, render them here. |
54a0048b | 2797 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?; |
9346a6ac | 2798 | |
1a4d82fc | 2799 | let cache = cache(); |
ea8adc8c | 2800 | let impl_header = " |
3b2f2976 XL |
2801 | <h2 id='implementors' class='small-section-header'> |
2802 | Implementors<a href='#implementors' class='anchor'></a> | |
2803 | </h2> | |
1a4d82fc | 2804 | <ul class='item-list' id='implementors-list'> |
ea8adc8c | 2805 | "; |
0531ce1d XL |
2806 | |
2807 | let synthetic_impl_header = " | |
2808 | <h2 id='synthetic-implementors' class='small-section-header'> | |
2809 | Auto implementors<a href='#synthetic-implementors' class='anchor'></a> | |
2810 | </h2> | |
2811 | <ul class='item-list' id='synthetic-implementors-list'> | |
2812 | "; | |
2813 | ||
2814 | let mut synthetic_types = Vec::new(); | |
2815 | ||
3157f602 | 2816 | if let Some(implementors) = cache.implementors.get(&it.def_id) { |
8bb4bdeb XL |
2817 | // The DefId is for the first Type found with that name. The bool is |
2818 | // if any Types with the same name but different DefId have been found. | |
2819 | let mut implementor_dups: FxHashMap<&str, (DefId, bool)> = FxHashMap(); | |
32a655c1 | 2820 | for implementor in implementors { |
2c00a5a8 | 2821 | match implementor.inner_impl().for_ { |
8bb4bdeb XL |
2822 | clean::ResolvedPath { ref path, did, is_generic: false, .. } | |
2823 | clean::BorrowedRef { | |
2824 | type_: box clean::ResolvedPath { ref path, did, is_generic: false, .. }, | |
2825 | .. | |
2826 | } => { | |
2827 | let &mut (prev_did, ref mut has_duplicates) = | |
2828 | implementor_dups.entry(path.last_name()).or_insert((did, false)); | |
2829 | if prev_did != did { | |
2830 | *has_duplicates = true; | |
2831 | } | |
2832 | } | |
2833 | _ => {} | |
32a655c1 SL |
2834 | } |
2835 | } | |
2836 | ||
ea8adc8c | 2837 | let (local, foreign) = implementors.iter() |
2c00a5a8 | 2838 | .partition::<Vec<_>, _>(|i| i.inner_impl().for_.def_id() |
ea8adc8c XL |
2839 | .map_or(true, |d| cache.paths.contains_key(&d))); |
2840 | ||
0531ce1d XL |
2841 | |
2842 | let (synthetic, concrete) = local.iter() | |
2843 | .partition::<Vec<_>, _>(|i| i.inner_impl().synthetic); | |
2844 | ||
2845 | ||
ea8adc8c XL |
2846 | if !foreign.is_empty() { |
2847 | write!(w, " | |
abe05a73 | 2848 | <h2 id='foreign-impls' class='small-section-header'> |
ea8adc8c XL |
2849 | Implementations on Foreign Types<a href='#foreign-impls' class='anchor'></a> |
2850 | </h2> | |
2851 | ")?; | |
2852 | ||
2853 | for implementor in foreign { | |
2c00a5a8 XL |
2854 | let assoc_link = AssocItemLink::GotoSource( |
2855 | implementor.impl_item.def_id, &implementor.inner_impl().provided_trait_methods | |
2856 | ); | |
2857 | render_impl(w, cx, &implementor, assoc_link, | |
2858 | RenderMode::Normal, implementor.impl_item.stable_since(), false)?; | |
ea8adc8c XL |
2859 | } |
2860 | } | |
2861 | ||
2862 | write!(w, "{}", impl_header)?; | |
0531ce1d XL |
2863 | for implementor in concrete { |
2864 | render_implementor(cx, implementor, w, &implementor_dups)?; | |
2865 | } | |
2866 | write!(w, "</ul>")?; | |
ea8adc8c | 2867 | |
0531ce1d XL |
2868 | if t.auto { |
2869 | write!(w, "{}", synthetic_impl_header)?; | |
2870 | for implementor in synthetic { | |
2871 | synthetic_types.extend( | |
2872 | collect_paths_for_type(implementor.inner_impl().for_.clone()) | |
2873 | ); | |
2874 | render_implementor(cx, implementor, w, &implementor_dups)?; | |
3b2f2976 | 2875 | } |
0531ce1d | 2876 | write!(w, "</ul>")?; |
1a4d82fc | 2877 | } |
ea8adc8c XL |
2878 | } else { |
2879 | // even without any implementations to write in, we still want the heading and list, so the | |
2880 | // implementors javascript file pulled in below has somewhere to write the impls into | |
2881 | write!(w, "{}", impl_header)?; | |
0531ce1d XL |
2882 | write!(w, "</ul>")?; |
2883 | ||
2884 | if t.auto { | |
2885 | write!(w, "{}", synthetic_impl_header)?; | |
2886 | write!(w, "</ul>")?; | |
2887 | } | |
1a4d82fc | 2888 | } |
0531ce1d XL |
2889 | write!(w, r#"<script type="text/javascript">window.inlined_types=new Set({});</script>"#, |
2890 | as_json(&synthetic_types))?; | |
2891 | ||
54a0048b SL |
2892 | write!(w, r#"<script type="text/javascript" async |
2893 | src="{root_path}/implementors/{path}/{ty}.{name}.js"> | |
2894 | </script>"#, | |
2895 | root_path = vec![".."; cx.current.len()].join("/"), | |
2896 | path = if it.def_id.is_local() { | |
2897 | cx.current.join("/") | |
2898 | } else { | |
5bcae85e | 2899 | let (ref path, _) = cache.external_paths[&it.def_id]; |
54a0048b SL |
2900 | path[..path.len() - 1].join("/") |
2901 | }, | |
c30ab7b3 | 2902 | ty = it.type_().css_class(), |
54a0048b | 2903 | name = *it.name.as_ref().unwrap())?; |
1a4d82fc JJ |
2904 | Ok(()) |
2905 | } | |
2906 | ||
54a0048b SL |
2907 | fn naive_assoc_href(it: &clean::Item, link: AssocItemLink) -> String { |
2908 | use html::item_type::ItemType::*; | |
2909 | ||
2910 | let name = it.name.as_ref().unwrap(); | |
c30ab7b3 | 2911 | let ty = match it.type_() { |
54a0048b SL |
2912 | Typedef | AssociatedType => AssociatedType, |
2913 | s@_ => s, | |
2914 | }; | |
2915 | ||
2916 | let anchor = format!("#{}.{}", ty, name); | |
2917 | match link { | |
a7813a04 XL |
2918 | AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), |
2919 | AssocItemLink::Anchor(None) => anchor, | |
54a0048b SL |
2920 | AssocItemLink::GotoSource(did, _) => { |
2921 | href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) | |
2922 | } | |
2923 | } | |
2924 | } | |
2925 | ||
2926 | fn assoc_const(w: &mut fmt::Formatter, | |
2927 | it: &clean::Item, | |
2928 | ty: &clean::Type, | |
8bb4bdeb | 2929 | _default: Option<&String>, |
54a0048b | 2930 | link: AssocItemLink) -> fmt::Result { |
ff7c6d11 XL |
2931 | write!(w, "{}const <a href='{}' class=\"constant\"><b>{}</b></a>: {}", |
2932 | VisSpace(&it.visibility), | |
54a0048b | 2933 | naive_assoc_href(it, link), |
8bb4bdeb XL |
2934 | it.name.as_ref().unwrap(), |
2935 | ty)?; | |
d9579d0f AL |
2936 | Ok(()) |
2937 | } | |
2938 | ||
ff7c6d11 XL |
2939 | fn assoc_type<W: fmt::Write>(w: &mut W, it: &clean::Item, |
2940 | bounds: &Vec<clean::TyParamBound>, | |
2941 | default: Option<&clean::Type>, | |
2942 | link: AssocItemLink) -> fmt::Result { | |
8bb4bdeb | 2943 | write!(w, "type <a href='{}' class=\"type\">{}</a>", |
54a0048b SL |
2944 | naive_assoc_href(it, link), |
2945 | it.name.as_ref().unwrap())?; | |
9346a6ac | 2946 | if !bounds.is_empty() { |
54a0048b | 2947 | write!(w, ": {}", TyParamBounds(bounds))? |
1a4d82fc | 2948 | } |
54a0048b SL |
2949 | if let Some(default) = default { |
2950 | write!(w, " = {}", default)?; | |
1a4d82fc JJ |
2951 | } |
2952 | Ok(()) | |
2953 | } | |
2954 | ||
7453a54e SL |
2955 | fn render_stability_since_raw<'a>(w: &mut fmt::Formatter, |
2956 | ver: Option<&'a str>, | |
2957 | containing_ver: Option<&'a str>) -> fmt::Result { | |
54a0048b SL |
2958 | if let Some(v) = ver { |
2959 | if containing_ver != ver && v.len() > 0 { | |
a7813a04 | 2960 | write!(w, "<div class='since' title='Stable since Rust version {0}'>{0}</div>", |
54a0048b | 2961 | v)? |
7453a54e SL |
2962 | } |
2963 | } | |
7453a54e SL |
2964 | Ok(()) |
2965 | } | |
2966 | ||
2967 | fn render_stability_since(w: &mut fmt::Formatter, | |
2968 | item: &clean::Item, | |
2969 | containing_item: &clean::Item) -> fmt::Result { | |
2970 | render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) | |
2971 | } | |
2972 | ||
54a0048b SL |
2973 | fn render_assoc_item(w: &mut fmt::Formatter, |
2974 | item: &clean::Item, | |
476ff2be SL |
2975 | link: AssocItemLink, |
2976 | parent: ItemType) -> fmt::Result { | |
62682a34 | 2977 | fn method(w: &mut fmt::Formatter, |
54a0048b | 2978 | meth: &clean::Item, |
e9174d1e SL |
2979 | unsafety: hir::Unsafety, |
2980 | constness: hir::Constness, | |
62682a34 SL |
2981 | abi: abi::Abi, |
2982 | g: &clean::Generics, | |
62682a34 | 2983 | d: &clean::FnDecl, |
476ff2be SL |
2984 | link: AssocItemLink, |
2985 | parent: ItemType) | |
62682a34 | 2986 | -> fmt::Result { |
54a0048b | 2987 | let name = meth.name.as_ref().unwrap(); |
c30ab7b3 | 2988 | let anchor = format!("#{}.{}", meth.type_(), name); |
9346a6ac | 2989 | let href = match link { |
a7813a04 XL |
2990 | AssocItemLink::Anchor(Some(ref id)) => format!("#{}", id), |
2991 | AssocItemLink::Anchor(None) => anchor, | |
54a0048b SL |
2992 | AssocItemLink::GotoSource(did, provided_methods) => { |
2993 | // We're creating a link from an impl-item to the corresponding | |
2994 | // trait-item and need to map the anchored type accordingly. | |
2995 | let ty = if provided_methods.contains(name) { | |
2996 | ItemType::Method | |
2997 | } else { | |
2998 | ItemType::TyMethod | |
2999 | }; | |
3000 | ||
3001 | href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) | |
9346a6ac AL |
3002 | } |
3003 | }; | |
ea8adc8c XL |
3004 | let mut head_len = format!("{}{}{}{:#}fn {}{:#}", |
3005 | VisSpace(&meth.visibility), | |
3006 | ConstnessSpace(constness), | |
cc61c64b XL |
3007 | UnsafetySpace(unsafety), |
3008 | AbiSpace(abi), | |
3009 | name, | |
3010 | *g).len(); | |
3011 | let (indent, end_newline) = if parent == ItemType::Trait { | |
3012 | head_len += 4; | |
3013 | (4, false) | |
476ff2be | 3014 | } else { |
cc61c64b | 3015 | (0, true) |
476ff2be | 3016 | }; |
ea8adc8c | 3017 | write!(w, "{}{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\ |
1a4d82fc | 3018 | {generics}{decl}{where_clause}", |
ea8adc8c XL |
3019 | VisSpace(&meth.visibility), |
3020 | ConstnessSpace(constness), | |
92a42be0 | 3021 | UnsafetySpace(unsafety), |
a7813a04 | 3022 | AbiSpace(abi), |
9346a6ac AL |
3023 | href = href, |
3024 | name = name, | |
1a4d82fc | 3025 | generics = *g, |
cc61c64b XL |
3026 | decl = Method { |
3027 | decl: d, | |
3028 | name_len: head_len, | |
3b2f2976 | 3029 | indent, |
cc61c64b XL |
3030 | }, |
3031 | where_clause = WhereClause { | |
3032 | gens: g, | |
3b2f2976 XL |
3033 | indent, |
3034 | end_newline, | |
cc61c64b | 3035 | }) |
1a4d82fc | 3036 | } |
54a0048b SL |
3037 | match item.inner { |
3038 | clean::StrippedItem(..) => Ok(()), | |
1a4d82fc | 3039 | clean::TyMethodItem(ref m) => { |
54a0048b | 3040 | method(w, item, m.unsafety, hir::Constness::NotConst, |
476ff2be | 3041 | m.abi, &m.generics, &m.decl, link, parent) |
1a4d82fc JJ |
3042 | } |
3043 | clean::MethodItem(ref m) => { | |
54a0048b | 3044 | method(w, item, m.unsafety, m.constness, |
476ff2be | 3045 | m.abi, &m.generics, &m.decl, link, parent) |
1a4d82fc | 3046 | } |
d9579d0f | 3047 | clean::AssociatedConstItem(ref ty, ref default) => { |
54a0048b | 3048 | assoc_const(w, item, ty, default.as_ref(), link) |
d9579d0f | 3049 | } |
c34b1796 | 3050 | clean::AssociatedTypeItem(ref bounds, ref default) => { |
54a0048b | 3051 | assoc_type(w, item, bounds, default.as_ref(), link) |
1a4d82fc | 3052 | } |
d9579d0f | 3053 | _ => panic!("render_assoc_item called on non-associated-item") |
1a4d82fc JJ |
3054 | } |
3055 | } | |
3056 | ||
e9174d1e | 3057 | fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 3058 | s: &clean::Struct) -> fmt::Result { |
0531ce1d XL |
3059 | wrap_into_docblock(w, |w| { |
3060 | write!(w, "<pre class='rust struct'>")?; | |
3061 | render_attributes(w, it)?; | |
3062 | render_struct(w, | |
3063 | it, | |
3064 | Some(&s.generics), | |
3065 | s.struct_type, | |
3066 | &s.fields, | |
3067 | "", | |
3068 | true)?; | |
3069 | write!(w, "</pre>") | |
3070 | })?; | |
54a0048b SL |
3071 | |
3072 | document(w, cx, it)?; | |
3157f602 | 3073 | let mut fields = s.fields.iter().filter_map(|f| { |
1a4d82fc | 3074 | match f.inner { |
3157f602 XL |
3075 | clean::StructFieldItem(ref ty) => Some((f, ty)), |
3076 | _ => None, | |
1a4d82fc JJ |
3077 | } |
3078 | }).peekable(); | |
3079 | if let doctree::Plain = s.struct_type { | |
3080 | if fields.peek().is_some() { | |
3b2f2976 XL |
3081 | write!(w, "<h2 id='fields' class='fields small-section-header'> |
3082 | Fields<a href='#fields' class='anchor'></a></h2>")?; | |
3157f602 | 3083 | for (field, ty) in fields { |
9e0c209e SL |
3084 | let id = derive_id(format!("{}.{}", |
3085 | ItemType::StructField, | |
3086 | field.name.as_ref().unwrap())); | |
3087 | let ns_id = derive_id(format!("{}.{}", | |
3088 | field.name.as_ref().unwrap(), | |
3089 | ItemType::StructField.name_space())); | |
ea8adc8c XL |
3090 | write!(w, "<span id=\"{id}\" class=\"{item_type} small-section-header\"> |
3091 | <a href=\"#{id}\" class=\"anchor field\"></a> | |
3092 | <span id=\"{ns_id}\" class='invisible'> | |
9e0c209e | 3093 | <code>{name}: {ty}</code> |
8bb4bdeb | 3094 | </span></span>", |
9e0c209e SL |
3095 | item_type = ItemType::StructField, |
3096 | id = id, | |
3097 | ns_id = ns_id, | |
3157f602 XL |
3098 | name = field.name.as_ref().unwrap(), |
3099 | ty = ty)?; | |
8bb4bdeb XL |
3100 | if let Some(stability_class) = field.stability_class() { |
3101 | write!(w, "<span class='stab {stab}'></span>", | |
3102 | stab = stability_class)?; | |
3103 | } | |
54a0048b | 3104 | document(w, cx, field)?; |
1a4d82fc | 3105 | } |
1a4d82fc JJ |
3106 | } |
3107 | } | |
7453a54e | 3108 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) |
1a4d82fc JJ |
3109 | } |
3110 | ||
9e0c209e SL |
3111 | fn item_union(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
3112 | s: &clean::Union) -> fmt::Result { | |
0531ce1d XL |
3113 | wrap_into_docblock(w, |w| { |
3114 | write!(w, "<pre class='rust union'>")?; | |
3115 | render_attributes(w, it)?; | |
3116 | render_union(w, | |
3117 | it, | |
3118 | Some(&s.generics), | |
3119 | &s.fields, | |
3120 | "", | |
3121 | true)?; | |
3122 | write!(w, "</pre>") | |
3123 | })?; | |
9e0c209e SL |
3124 | |
3125 | document(w, cx, it)?; | |
3126 | let mut fields = s.fields.iter().filter_map(|f| { | |
3127 | match f.inner { | |
3128 | clean::StructFieldItem(ref ty) => Some((f, ty)), | |
3129 | _ => None, | |
3130 | } | |
3131 | }).peekable(); | |
3132 | if fields.peek().is_some() { | |
3b2f2976 XL |
3133 | write!(w, "<h2 id='fields' class='fields small-section-header'> |
3134 | Fields<a href='#fields' class='anchor'></a></h2>")?; | |
9e0c209e | 3135 | for (field, ty) in fields { |
83c7162d XL |
3136 | let name = field.name.as_ref().expect("union field name"); |
3137 | let id = format!("{}.{}", ItemType::StructField, name); | |
3138 | write!(w, "<span id=\"{id}\" class=\"{shortty} small-section-header\">\ | |
3139 | <a href=\"#{id}\" class=\"anchor field\"></a>\ | |
3140 | <span class='invisible'><code>{name}: {ty}</code></span>\ | |
8bb4bdeb | 3141 | </span>", |
83c7162d XL |
3142 | id = id, |
3143 | name = name, | |
9e0c209e | 3144 | shortty = ItemType::StructField, |
9e0c209e | 3145 | ty = ty)?; |
8bb4bdeb XL |
3146 | if let Some(stability_class) = field.stability_class() { |
3147 | write!(w, "<span class='stab {stab}'></span>", | |
3148 | stab = stability_class)?; | |
3149 | } | |
9e0c209e SL |
3150 | document(w, cx, field)?; |
3151 | } | |
3152 | } | |
3153 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) | |
3154 | } | |
3155 | ||
e9174d1e | 3156 | fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 3157 | e: &clean::Enum) -> fmt::Result { |
0531ce1d XL |
3158 | wrap_into_docblock(w, |w| { |
3159 | write!(w, "<pre class='rust enum'>")?; | |
3160 | render_attributes(w, it)?; | |
3161 | write!(w, "{}enum {}{}{}", | |
3162 | VisSpace(&it.visibility), | |
3163 | it.name.as_ref().unwrap(), | |
3164 | e.generics, | |
3165 | WhereClause { gens: &e.generics, indent: 0, end_newline: true })?; | |
3166 | if e.variants.is_empty() && !e.variants_stripped { | |
3167 | write!(w, " {{}}")?; | |
3168 | } else { | |
3169 | write!(w, " {{\n")?; | |
3170 | for v in &e.variants { | |
3171 | write!(w, " ")?; | |
3172 | let name = v.name.as_ref().unwrap(); | |
3173 | match v.inner { | |
3174 | clean::VariantItem(ref var) => { | |
3175 | match var.kind { | |
3176 | clean::VariantKind::CLike => write!(w, "{}", name)?, | |
3177 | clean::VariantKind::Tuple(ref tys) => { | |
3178 | write!(w, "{}(", name)?; | |
3179 | for (i, ty) in tys.iter().enumerate() { | |
3180 | if i > 0 { | |
3181 | write!(w, ", ")? | |
3182 | } | |
3183 | write!(w, "{}", *ty)?; | |
1a4d82fc | 3184 | } |
0531ce1d XL |
3185 | write!(w, ")")?; |
3186 | } | |
3187 | clean::VariantKind::Struct(ref s) => { | |
3188 | render_struct(w, | |
3189 | v, | |
3190 | None, | |
3191 | s.struct_type, | |
3192 | &s.fields, | |
3193 | " ", | |
3194 | false)?; | |
1a4d82fc | 3195 | } |
1a4d82fc JJ |
3196 | } |
3197 | } | |
0531ce1d | 3198 | _ => unreachable!() |
1a4d82fc | 3199 | } |
0531ce1d | 3200 | write!(w, ",\n")?; |
1a4d82fc | 3201 | } |
1a4d82fc | 3202 | |
0531ce1d XL |
3203 | if e.variants_stripped { |
3204 | write!(w, " // some variants omitted\n")?; | |
3205 | } | |
3206 | write!(w, "}}")?; | |
1a4d82fc | 3207 | } |
0531ce1d XL |
3208 | write!(w, "</pre>") |
3209 | })?; | |
1a4d82fc | 3210 | |
54a0048b | 3211 | document(w, cx, it)?; |
9346a6ac | 3212 | if !e.variants.is_empty() { |
3b2f2976 XL |
3213 | write!(w, "<h2 id='variants' class='variants small-section-header'> |
3214 | Variants<a href='#variants' class='anchor'></a></h2>\n")?; | |
85aaf69f | 3215 | for variant in &e.variants { |
9e0c209e SL |
3216 | let id = derive_id(format!("{}.{}", |
3217 | ItemType::Variant, | |
3218 | variant.name.as_ref().unwrap())); | |
3219 | let ns_id = derive_id(format!("{}.{}", | |
3220 | variant.name.as_ref().unwrap(), | |
3221 | ItemType::Variant.name_space())); | |
ea8adc8c XL |
3222 | write!(w, "<span id=\"{id}\" class=\"variant small-section-header\">\ |
3223 | <a href=\"#{id}\" class=\"anchor field\"></a>\ | |
9e0c209e SL |
3224 | <span id='{ns_id}' class='invisible'><code>{name}", |
3225 | id = id, | |
3226 | ns_id = ns_id, | |
54a0048b | 3227 | name = variant.name.as_ref().unwrap())?; |
3157f602 | 3228 | if let clean::VariantItem(ref var) = variant.inner { |
c30ab7b3 | 3229 | if let clean::VariantKind::Tuple(ref tys) = var.kind { |
3157f602 XL |
3230 | write!(w, "(")?; |
3231 | for (i, ty) in tys.iter().enumerate() { | |
3232 | if i > 0 { | |
3233 | write!(w, ", ")?; | |
3234 | } | |
3235 | write!(w, "{}", *ty)?; | |
3236 | } | |
3237 | write!(w, ")")?; | |
3238 | } | |
3239 | } | |
9e0c209e | 3240 | write!(w, "</code></span></span>")?; |
54a0048b SL |
3241 | document(w, cx, variant)?; |
3242 | ||
c30ab7b3 SL |
3243 | use clean::{Variant, VariantKind}; |
3244 | if let clean::VariantItem(Variant { | |
3245 | kind: VariantKind::Struct(ref s) | |
3246 | }) = variant.inner { | |
476ff2be SL |
3247 | let variant_id = derive_id(format!("{}.{}.fields", |
3248 | ItemType::Variant, | |
3249 | variant.name.as_ref().unwrap())); | |
3250 | write!(w, "<span class='docblock autohide sub-variant' id='{id}'>", | |
3251 | id = variant_id)?; | |
3252 | write!(w, "<h3 class='fields'>Fields of <code>{name}</code></h3>\n | |
3253 | <table>", name = variant.name.as_ref().unwrap())?; | |
3157f602 XL |
3254 | for field in &s.fields { |
3255 | use clean::StructFieldItem; | |
3256 | if let StructFieldItem(ref ty) = field.inner { | |
9e0c209e SL |
3257 | let id = derive_id(format!("variant.{}.field.{}", |
3258 | variant.name.as_ref().unwrap(), | |
3259 | field.name.as_ref().unwrap())); | |
3260 | let ns_id = derive_id(format!("{}.{}.{}.{}", | |
3261 | variant.name.as_ref().unwrap(), | |
3262 | ItemType::Variant.name_space(), | |
3263 | field.name.as_ref().unwrap(), | |
3264 | ItemType::StructField.name_space())); | |
3157f602 | 3265 | write!(w, "<tr><td \ |
9e0c209e SL |
3266 | id='{id}'>\ |
3267 | <span id='{ns_id}' class='invisible'>\ | |
3268 | <code>{f}: {t}</code></span></td><td>", | |
3269 | id = id, | |
3270 | ns_id = ns_id, | |
3157f602 XL |
3271 | f = field.name.as_ref().unwrap(), |
3272 | t = *ty)?; | |
3273 | document(w, cx, field)?; | |
3274 | write!(w, "</td></tr>")?; | |
3275 | } | |
1a4d82fc | 3276 | } |
476ff2be | 3277 | write!(w, "</table></span>")?; |
1a4d82fc | 3278 | } |
54a0048b | 3279 | render_stability_since(w, variant, it)?; |
1a4d82fc | 3280 | } |
1a4d82fc | 3281 | } |
54a0048b | 3282 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?; |
1a4d82fc JJ |
3283 | Ok(()) |
3284 | } | |
3285 | ||
476ff2be SL |
3286 | fn render_attribute(attr: &ast::MetaItem) -> Option<String> { |
3287 | let name = attr.name(); | |
3288 | ||
3289 | if attr.is_word() { | |
3290 | Some(format!("{}", name)) | |
3291 | } else if let Some(v) = attr.value_str() { | |
cc61c64b | 3292 | Some(format!("{} = {:?}", name, v.as_str())) |
476ff2be SL |
3293 | } else if let Some(values) = attr.meta_item_list() { |
3294 | let display: Vec<_> = values.iter().filter_map(|attr| { | |
3295 | attr.meta_item().and_then(|mi| render_attribute(mi)) | |
3296 | }).collect(); | |
3297 | ||
3298 | if display.len() > 0 { | |
3299 | Some(format!("{}({})", name, display.join(", "))) | |
3300 | } else { | |
3301 | None | |
3302 | } | |
3303 | } else { | |
3304 | None | |
3305 | } | |
3306 | } | |
3307 | ||
3308 | const ATTRIBUTE_WHITELIST: &'static [&'static str] = &[ | |
3309 | "export_name", | |
3310 | "lang", | |
3311 | "link_section", | |
3312 | "must_use", | |
3313 | "no_mangle", | |
3314 | "repr", | |
3315 | "unsafe_destructor_blind_to_params" | |
3316 | ]; | |
3317 | ||
85aaf69f | 3318 | fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { |
476ff2be SL |
3319 | let mut attrs = String::new(); |
3320 | ||
3321 | for attr in &it.attrs.other_attrs { | |
83c7162d | 3322 | let name = attr.name(); |
cc61c64b | 3323 | if !ATTRIBUTE_WHITELIST.contains(&&*name.as_str()) { |
476ff2be | 3324 | continue; |
85aaf69f | 3325 | } |
cc61c64b | 3326 | if let Some(s) = render_attribute(&attr.meta().unwrap()) { |
476ff2be SL |
3327 | attrs.push_str(&format!("#[{}]\n", s)); |
3328 | } | |
3329 | } | |
3330 | if attrs.len() > 0 { | |
3331 | write!(w, "<div class=\"docblock attributes\">{}</div>", &attrs)?; | |
85aaf69f SL |
3332 | } |
3333 | Ok(()) | |
3334 | } | |
3335 | ||
1a4d82fc JJ |
3336 | fn render_struct(w: &mut fmt::Formatter, it: &clean::Item, |
3337 | g: Option<&clean::Generics>, | |
3338 | ty: doctree::StructType, | |
3339 | fields: &[clean::Item], | |
3340 | tab: &str, | |
3341 | structhead: bool) -> fmt::Result { | |
54a0048b SL |
3342 | write!(w, "{}{}{}", |
3343 | VisSpace(&it.visibility), | |
3344 | if structhead {"struct "} else {""}, | |
3345 | it.name.as_ref().unwrap())?; | |
3346 | if let Some(g) = g { | |
5bcae85e | 3347 | write!(w, "{}", g)? |
1a4d82fc JJ |
3348 | } |
3349 | match ty { | |
3350 | doctree::Plain => { | |
5bcae85e | 3351 | if let Some(g) = g { |
cc61c64b | 3352 | write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true })? |
5bcae85e | 3353 | } |
9e0c209e SL |
3354 | let mut has_visible_fields = false; |
3355 | write!(w, " {{")?; | |
85aaf69f | 3356 | for field in fields { |
54a0048b | 3357 | if let clean::StructFieldItem(ref ty) = field.inner { |
9e0c209e SL |
3358 | write!(w, "\n{} {}{}: {},", |
3359 | tab, | |
54a0048b SL |
3360 | VisSpace(&field.visibility), |
3361 | field.name.as_ref().unwrap(), | |
9e0c209e SL |
3362 | *ty)?; |
3363 | has_visible_fields = true; | |
54a0048b | 3364 | } |
1a4d82fc JJ |
3365 | } |
3366 | ||
9e0c209e SL |
3367 | if has_visible_fields { |
3368 | if it.has_stripped_fields().unwrap() { | |
3369 | write!(w, "\n{} // some fields omitted", tab)?; | |
3370 | } | |
3371 | write!(w, "\n{}", tab)?; | |
3372 | } else if it.has_stripped_fields().unwrap() { | |
3373 | // If there are no visible fields we can just display | |
3374 | // `{ /* fields omitted */ }` to save space. | |
3375 | write!(w, " /* fields omitted */ ")?; | |
1a4d82fc | 3376 | } |
54a0048b | 3377 | write!(w, "}}")?; |
1a4d82fc | 3378 | } |
9e0c209e | 3379 | doctree::Tuple => { |
54a0048b | 3380 | write!(w, "(")?; |
1a4d82fc JJ |
3381 | for (i, field) in fields.iter().enumerate() { |
3382 | if i > 0 { | |
54a0048b | 3383 | write!(w, ", ")?; |
1a4d82fc JJ |
3384 | } |
3385 | match field.inner { | |
54a0048b SL |
3386 | clean::StrippedItem(box clean::StructFieldItem(..)) => { |
3387 | write!(w, "_")? | |
1a4d82fc | 3388 | } |
54a0048b SL |
3389 | clean::StructFieldItem(ref ty) => { |
3390 | write!(w, "{}{}", VisSpace(&field.visibility), *ty)? | |
1a4d82fc JJ |
3391 | } |
3392 | _ => unreachable!() | |
3393 | } | |
3394 | } | |
5bcae85e SL |
3395 | write!(w, ")")?; |
3396 | if let Some(g) = g { | |
cc61c64b | 3397 | write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false })? |
5bcae85e SL |
3398 | } |
3399 | write!(w, ";")?; | |
1a4d82fc JJ |
3400 | } |
3401 | doctree::Unit => { | |
5bcae85e SL |
3402 | // Needed for PhantomData. |
3403 | if let Some(g) = g { | |
cc61c64b | 3404 | write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: false })? |
5bcae85e | 3405 | } |
54a0048b | 3406 | write!(w, ";")?; |
1a4d82fc JJ |
3407 | } |
3408 | } | |
3409 | Ok(()) | |
3410 | } | |
3411 | ||
9e0c209e SL |
3412 | fn render_union(w: &mut fmt::Formatter, it: &clean::Item, |
3413 | g: Option<&clean::Generics>, | |
3414 | fields: &[clean::Item], | |
3415 | tab: &str, | |
3416 | structhead: bool) -> fmt::Result { | |
3417 | write!(w, "{}{}{}", | |
3418 | VisSpace(&it.visibility), | |
3419 | if structhead {"union "} else {""}, | |
3420 | it.name.as_ref().unwrap())?; | |
3421 | if let Some(g) = g { | |
3422 | write!(w, "{}", g)?; | |
cc61c64b | 3423 | write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true })?; |
9e0c209e SL |
3424 | } |
3425 | ||
3426 | write!(w, " {{\n{}", tab)?; | |
3427 | for field in fields { | |
3428 | if let clean::StructFieldItem(ref ty) = field.inner { | |
3429 | write!(w, " {}{}: {},\n{}", | |
3430 | VisSpace(&field.visibility), | |
3431 | field.name.as_ref().unwrap(), | |
3432 | *ty, | |
3433 | tab)?; | |
3434 | } | |
3435 | } | |
3436 | ||
3437 | if it.has_stripped_fields().unwrap() { | |
3438 | write!(w, " // some fields omitted\n{}", tab)?; | |
3439 | } | |
3440 | write!(w, "}}")?; | |
3441 | Ok(()) | |
3442 | } | |
3443 | ||
9346a6ac | 3444 | #[derive(Copy, Clone)] |
54a0048b | 3445 | enum AssocItemLink<'a> { |
a7813a04 | 3446 | Anchor(Option<&'a str>), |
476ff2be | 3447 | GotoSource(DefId, &'a FxHashSet<String>), |
9346a6ac AL |
3448 | } |
3449 | ||
a7813a04 XL |
3450 | impl<'a> AssocItemLink<'a> { |
3451 | fn anchor(&self, id: &'a String) -> Self { | |
3452 | match *self { | |
3453 | AssocItemLink::Anchor(_) => { AssocItemLink::Anchor(Some(&id)) }, | |
3454 | ref other => *other, | |
3455 | } | |
3456 | } | |
3457 | } | |
3458 | ||
d9579d0f AL |
3459 | enum AssocItemRender<'a> { |
3460 | All, | |
9e0c209e SL |
3461 | DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type, deref_mut_: bool } |
3462 | } | |
3463 | ||
3464 | #[derive(Copy, Clone, PartialEq)] | |
3465 | enum RenderMode { | |
3466 | Normal, | |
3467 | ForDeref { mut_: bool }, | |
d9579d0f AL |
3468 | } |
3469 | ||
3470 | fn render_assoc_items(w: &mut fmt::Formatter, | |
e9174d1e | 3471 | cx: &Context, |
7453a54e | 3472 | containing_item: &clean::Item, |
e9174d1e | 3473 | it: DefId, |
d9579d0f AL |
3474 | what: AssocItemRender) -> fmt::Result { |
3475 | let c = cache(); | |
3476 | let v = match c.impls.get(&it) { | |
3477 | Some(v) => v, | |
9346a6ac AL |
3478 | None => return Ok(()), |
3479 | }; | |
d9579d0f | 3480 | let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| { |
a7813a04 | 3481 | i.inner_impl().trait_.is_none() |
d9579d0f | 3482 | }); |
9346a6ac | 3483 | if !non_trait.is_empty() { |
9e0c209e | 3484 | let render_mode = match what { |
d9579d0f | 3485 | AssocItemRender::All => { |
3b2f2976 XL |
3486 | write!(w, " |
3487 | <h2 id='methods' class='small-section-header'> | |
3488 | Methods<a href='#methods' class='anchor'></a> | |
3489 | </h2> | |
3490 | ")?; | |
9e0c209e | 3491 | RenderMode::Normal |
d9579d0f | 3492 | } |
9e0c209e | 3493 | AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { |
3b2f2976 XL |
3494 | write!(w, " |
3495 | <h2 id='deref-methods' class='small-section-header'> | |
3496 | Methods from {}<Target = {}><a href='#deref-methods' class='anchor'></a> | |
3497 | </h2> | |
3498 | ", trait_, type_)?; | |
9e0c209e | 3499 | RenderMode::ForDeref { mut_: deref_mut_ } |
d9579d0f AL |
3500 | } |
3501 | }; | |
9346a6ac | 3502 | for i in &non_trait { |
9e0c209e | 3503 | render_impl(w, cx, i, AssocItemLink::Anchor(None), render_mode, |
ea8adc8c | 3504 | containing_item.stable_since(), true)?; |
9346a6ac AL |
3505 | } |
3506 | } | |
d9579d0f | 3507 | if let AssocItemRender::DerefFor { .. } = what { |
54a0048b | 3508 | return Ok(()); |
d9579d0f | 3509 | } |
9346a6ac | 3510 | if !traits.is_empty() { |
d9579d0f | 3511 | let deref_impl = traits.iter().find(|t| { |
a7813a04 | 3512 | t.inner_impl().trait_.def_id() == c.deref_trait_did |
d9579d0f AL |
3513 | }); |
3514 | if let Some(impl_) = deref_impl { | |
9e0c209e SL |
3515 | let has_deref_mut = traits.iter().find(|t| { |
3516 | t.inner_impl().trait_.def_id() == c.deref_mut_trait_did | |
3517 | }).is_some(); | |
3518 | render_deref_methods(w, cx, impl_, containing_item, has_deref_mut)?; | |
d9579d0f | 3519 | } |
0531ce1d XL |
3520 | |
3521 | let (synthetic, concrete) = traits | |
3522 | .iter() | |
3523 | .partition::<Vec<_>, _>(|t| t.inner_impl().synthetic); | |
3524 | ||
83c7162d XL |
3525 | struct RendererStruct<'a, 'b, 'c>(&'a Context, Vec<&'b &'b Impl>, &'c clean::Item); |
3526 | ||
3527 | impl<'a, 'b, 'c> fmt::Display for RendererStruct<'a, 'b, 'c> { | |
3528 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
3529 | render_impls(self.0, fmt, &self.1, self.2) | |
3530 | } | |
3531 | } | |
3532 | ||
3533 | let impls = format!("{}", RendererStruct(cx, concrete, containing_item)); | |
3534 | if !impls.is_empty() { | |
3535 | write!(w, " | |
3536 | <h2 id='implementations' class='small-section-header'> | |
3537 | Trait Implementations<a href='#implementations' class='anchor'></a> | |
3538 | </h2> | |
3539 | <div id='implementations-list'>{}</div>", impls)?; | |
3540 | } | |
0531ce1d XL |
3541 | |
3542 | if !synthetic.is_empty() { | |
3543 | write!(w, " | |
3544 | <h2 id='synthetic-implementations' class='small-section-header'> | |
3545 | Auto Trait Implementations<a href='#synthetic-implementations' class='anchor'></a> | |
3546 | </h2> | |
3547 | <div id='synthetic-implementations-list'> | |
3548 | ")?; | |
83c7162d | 3549 | render_impls(cx, w, &synthetic, containing_item)?; |
0531ce1d | 3550 | write!(w, "</div>")?; |
9346a6ac | 3551 | } |
1a4d82fc JJ |
3552 | } |
3553 | Ok(()) | |
3554 | } | |
3555 | ||
7453a54e | 3556 | fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl, |
9e0c209e | 3557 | container_item: &clean::Item, deref_mut: bool) -> fmt::Result { |
a7813a04 XL |
3558 | let deref_type = impl_.inner_impl().trait_.as_ref().unwrap(); |
3559 | let target = impl_.inner_impl().items.iter().filter_map(|item| { | |
d9579d0f | 3560 | match item.inner { |
62682a34 | 3561 | clean::TypedefItem(ref t, true) => Some(&t.type_), |
d9579d0f AL |
3562 | _ => None, |
3563 | } | |
62682a34 | 3564 | }).next().expect("Expected associated type binding"); |
9e0c209e SL |
3565 | let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target, |
3566 | deref_mut_: deref_mut }; | |
54a0048b SL |
3567 | if let Some(did) = target.def_id() { |
3568 | render_assoc_items(w, cx, container_item, did, what) | |
3569 | } else { | |
3570 | if let Some(prim) = target.primitive_type() { | |
476ff2be | 3571 | if let Some(&did) = cache().primitive_locations.get(&prim) { |
54a0048b | 3572 | render_assoc_items(w, cx, container_item, did, what)?; |
d9579d0f | 3573 | } |
d9579d0f | 3574 | } |
54a0048b | 3575 | Ok(()) |
1a4d82fc | 3576 | } |
d9579d0f AL |
3577 | } |
3578 | ||
abe05a73 XL |
3579 | fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool { |
3580 | let self_type_opt = match item.inner { | |
3581 | clean::MethodItem(ref method) => method.decl.self_type(), | |
3582 | clean::TyMethodItem(ref method) => method.decl.self_type(), | |
3583 | _ => None | |
3584 | }; | |
3585 | ||
3586 | if let Some(self_ty) = self_type_opt { | |
3587 | let (by_mut_ref, by_box, by_value) = match self_ty { | |
3588 | SelfTy::SelfBorrowed(_, mutability) | | |
3589 | SelfTy::SelfExplicit(clean::BorrowedRef { mutability, .. }) => { | |
3590 | (mutability == Mutability::Mutable, false, false) | |
3591 | }, | |
3592 | SelfTy::SelfExplicit(clean::ResolvedPath { did, .. }) => { | |
3593 | (false, Some(did) == cache().owned_box_did, false) | |
3594 | }, | |
3595 | SelfTy::SelfValue => (false, false, true), | |
3596 | _ => (false, false, false), | |
3597 | }; | |
3598 | ||
3599 | (deref_mut_ || !by_mut_ref) && !by_box && !by_value | |
3600 | } else { | |
3601 | false | |
3602 | } | |
3603 | } | |
3604 | ||
ff7c6d11 XL |
3605 | fn render_spotlight_traits(item: &clean::Item) -> Result<String, fmt::Error> { |
3606 | let mut out = String::new(); | |
3607 | ||
3608 | match item.inner { | |
3609 | clean::FunctionItem(clean::Function { ref decl, .. }) | | |
3610 | clean::TyMethodItem(clean::TyMethod { ref decl, .. }) | | |
3611 | clean::MethodItem(clean::Method { ref decl, .. }) | | |
3612 | clean::ForeignFunctionItem(clean::Function { ref decl, .. }) => { | |
3613 | out = spotlight_decl(decl)?; | |
3614 | } | |
3615 | _ => {} | |
3616 | } | |
3617 | ||
3618 | Ok(out) | |
3619 | } | |
3620 | ||
3621 | fn spotlight_decl(decl: &clean::FnDecl) -> Result<String, fmt::Error> { | |
3622 | let mut out = String::new(); | |
3623 | let mut trait_ = String::new(); | |
3624 | ||
3625 | if let Some(did) = decl.output.def_id() { | |
3626 | let c = cache(); | |
3627 | if let Some(impls) = c.impls.get(&did) { | |
3628 | for i in impls { | |
3629 | let impl_ = i.inner_impl(); | |
2c00a5a8 | 3630 | if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) { |
ff7c6d11 XL |
3631 | if out.is_empty() { |
3632 | out.push_str( | |
3633 | &format!("<h3 class=\"important\">Important traits for {}</h3>\ | |
3634 | <code class=\"content\">", | |
3635 | impl_.for_)); | |
3636 | trait_.push_str(&format!("{}", impl_.for_)); | |
3637 | } | |
3638 | ||
3639 | //use the "where" class here to make it small | |
3640 | out.push_str(&format!("<span class=\"where fmt-newline\">{}</span>", impl_)); | |
3641 | let t_did = impl_.trait_.def_id().unwrap(); | |
3642 | for it in &impl_.items { | |
3643 | if let clean::TypedefItem(ref tydef, _) = it.inner { | |
3644 | out.push_str("<span class=\"where fmt-newline\"> "); | |
3645 | assoc_type(&mut out, it, &vec![], | |
3646 | Some(&tydef.type_), | |
3647 | AssocItemLink::GotoSource(t_did, &FxHashSet()))?; | |
3648 | out.push_str(";</span>"); | |
3649 | } | |
3650 | } | |
3651 | } | |
3652 | } | |
3653 | } | |
3654 | } | |
3655 | ||
3656 | if !out.is_empty() { | |
3657 | out.insert_str(0, &format!("<div class=\"important-traits\"><div class='tooltip'>ⓘ\ | |
3658 | <span class='tooltiptext'>Important traits for {}</span></div>\ | |
3659 | <div class=\"content hidden\">", | |
3660 | trait_)); | |
3661 | out.push_str("</code></div></div>"); | |
3662 | } | |
3663 | ||
3664 | Ok(out) | |
3665 | } | |
3666 | ||
e9174d1e | 3667 | fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink, |
ea8adc8c XL |
3668 | render_mode: RenderMode, outer_version: Option<&str>, |
3669 | show_def_docs: bool) -> fmt::Result { | |
9e0c209e | 3670 | if render_mode == RenderMode::Normal { |
3b2f2976 | 3671 | let id = derive_id(match i.inner_impl().trait_ { |
abe05a73 | 3672 | Some(ref t) => format!("impl-{}", small_url_encode(&format!("{:#}", t))), |
3b2f2976 XL |
3673 | None => "impl".to_string(), |
3674 | }); | |
0531ce1d XL |
3675 | write!(w, "<h3 id='{}' class='impl'><span class='in-band'><table class='table-display'>\ |
3676 | <tbody><tr><td><code>{}</code>", | |
3b2f2976 XL |
3677 | id, i.inner_impl())?; |
3678 | write!(w, "<a href='#{}' class='anchor'></a>", id)?; | |
0531ce1d | 3679 | write!(w, "</span></td><td><span class='out-of-band'>")?; |
a7813a04 | 3680 | let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]); |
476ff2be | 3681 | if let Some(l) = (Item { item: &i.impl_item, cx: cx }).src_href() { |
a7813a04 XL |
3682 | write!(w, "<div class='ghost'></div>")?; |
3683 | render_stability_since_raw(w, since, outer_version)?; | |
476ff2be SL |
3684 | write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", |
3685 | l, "goto source code")?; | |
a7813a04 XL |
3686 | } else { |
3687 | render_stability_since_raw(w, since, outer_version)?; | |
3688 | } | |
0531ce1d | 3689 | write!(w, "</span></td></tr></tbody></table></h3>")?; |
ff7c6d11 | 3690 | if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) { |
2c00a5a8 | 3691 | write!(w, "<div class='docblock'>{}</div>", |
0531ce1d | 3692 | Markdown(&*dox, &i.impl_item.links()))?; |
d9579d0f | 3693 | } |
1a4d82fc JJ |
3694 | } |
3695 | ||
9e0c209e SL |
3696 | fn doc_impl_item(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item, |
3697 | link: AssocItemLink, render_mode: RenderMode, | |
3698 | is_default_item: bool, outer_version: Option<&str>, | |
ea8adc8c | 3699 | trait_: Option<&clean::Trait>, show_def_docs: bool) -> fmt::Result { |
c30ab7b3 | 3700 | let item_type = item.type_(); |
92a42be0 | 3701 | let name = item.name.as_ref().unwrap(); |
54a0048b | 3702 | |
9e0c209e SL |
3703 | let render_method_item: bool = match render_mode { |
3704 | RenderMode::Normal => true, | |
abe05a73 | 3705 | RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_), |
54a0048b SL |
3706 | }; |
3707 | ||
1a4d82fc | 3708 | match item.inner { |
ff7c6d11 XL |
3709 | clean::MethodItem(clean::Method { ref decl, .. }) | |
3710 | clean::TyMethodItem(clean::TyMethod{ ref decl, .. }) => { | |
62682a34 | 3711 | // Only render when the method is not static or we allow static methods |
9e0c209e SL |
3712 | if render_method_item { |
3713 | let id = derive_id(format!("{}.{}", item_type, name)); | |
3714 | let ns_id = derive_id(format!("{}.{}", name, item_type.name_space())); | |
8bb4bdeb | 3715 | write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?; |
ff7c6d11 | 3716 | write!(w, "{}", spotlight_decl(decl)?)?; |
9e0c209e | 3717 | write!(w, "<span id='{}' class='invisible'>", ns_id)?; |
0531ce1d | 3718 | write!(w, "<table class='table-display'><tbody><tr><td><code>")?; |
476ff2be | 3719 | render_assoc_item(w, item, link.anchor(&id), ItemType::Impl)?; |
a7813a04 | 3720 | write!(w, "</code>")?; |
3b2f2976 | 3721 | if let Some(l) = (Item { cx, item }).src_href() { |
0531ce1d | 3722 | write!(w, "</span></td><td><span class='out-of-band'>")?; |
3b2f2976 XL |
3723 | write!(w, "<div class='ghost'></div>")?; |
3724 | render_stability_since_raw(w, item.stable_since(), outer_version)?; | |
3725 | write!(w, "<a class='srclink' href='{}' title='{}'>[src]</a>", | |
3726 | l, "goto source code")?; | |
3727 | } else { | |
0531ce1d | 3728 | write!(w, "</td><td>")?; |
3b2f2976 XL |
3729 | render_stability_since_raw(w, item.stable_since(), outer_version)?; |
3730 | } | |
0531ce1d | 3731 | write!(w, "</td></tr></tbody></table></span></h4>")?; |
62682a34 | 3732 | } |
1a4d82fc | 3733 | } |
62682a34 | 3734 | clean::TypedefItem(ref tydef, _) => { |
54a0048b | 3735 | let id = derive_id(format!("{}.{}", ItemType::AssociatedType, name)); |
9e0c209e | 3736 | let ns_id = derive_id(format!("{}.{}", name, item_type.name_space())); |
8bb4bdeb | 3737 | write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?; |
9e0c209e | 3738 | write!(w, "<span id='{}' class='invisible'><code>", ns_id)?; |
a7813a04 | 3739 | assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id))?; |
9e0c209e | 3740 | write!(w, "</code></span></h4>\n")?; |
1a4d82fc | 3741 | } |
d9579d0f | 3742 | clean::AssociatedConstItem(ref ty, ref default) => { |
9e0c209e SL |
3743 | let id = derive_id(format!("{}.{}", item_type, name)); |
3744 | let ns_id = derive_id(format!("{}.{}", name, item_type.name_space())); | |
8bb4bdeb | 3745 | write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?; |
9e0c209e | 3746 | write!(w, "<span id='{}' class='invisible'><code>", ns_id)?; |
a7813a04 | 3747 | assoc_const(w, item, ty, default.as_ref(), link.anchor(&id))?; |
9e0c209e | 3748 | write!(w, "</code></span></h4>\n")?; |
d9579d0f | 3749 | } |
c34b1796 | 3750 | clean::AssociatedTypeItem(ref bounds, ref default) => { |
9e0c209e SL |
3751 | let id = derive_id(format!("{}.{}", item_type, name)); |
3752 | let ns_id = derive_id(format!("{}.{}", name, item_type.name_space())); | |
8bb4bdeb | 3753 | write!(w, "<h4 id='{}' class=\"{}\">", id, item_type)?; |
9e0c209e | 3754 | write!(w, "<span id='{}' class='invisible'><code>", ns_id)?; |
a7813a04 | 3755 | assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id))?; |
9e0c209e | 3756 | write!(w, "</code></span></h4>\n")?; |
1a4d82fc | 3757 | } |
54a0048b | 3758 | clean::StrippedItem(..) => return Ok(()), |
1a4d82fc JJ |
3759 | _ => panic!("can't make docs for trait item with name {:?}", item.name) |
3760 | } | |
62682a34 | 3761 | |
9e0c209e | 3762 | if render_method_item || render_mode == RenderMode::Normal { |
041b39d2 | 3763 | let prefix = render_assoc_const_value(item); |
ff7c6d11 | 3764 | |
a7813a04 | 3765 | if !is_default_item { |
3157f602 XL |
3766 | if let Some(t) = trait_ { |
3767 | // The trait item may have been stripped so we might not | |
3768 | // find any documentation or stability for it. | |
3769 | if let Some(it) = t.items.iter().find(|i| i.name == item.name) { | |
3770 | // We need the stability of the item from the trait | |
3771 | // because impls can't have a stability. | |
3772 | document_stability(w, cx, it)?; | |
041b39d2 | 3773 | if item.doc_value().is_some() { |
ea8adc8c XL |
3774 | document_full(w, item, cx, &prefix)?; |
3775 | } else if show_def_docs { | |
3157f602 XL |
3776 | // In case the item isn't documented, |
3777 | // provide short documentation from the trait. | |
0531ce1d | 3778 | document_short(w, it, link, &prefix)?; |
a7813a04 XL |
3779 | } |
3780 | } | |
3157f602 | 3781 | } else { |
041b39d2 | 3782 | document_stability(w, cx, item)?; |
ea8adc8c XL |
3783 | if show_def_docs { |
3784 | document_full(w, item, cx, &prefix)?; | |
3785 | } | |
a7813a04 XL |
3786 | } |
3787 | } else { | |
3157f602 | 3788 | document_stability(w, cx, item)?; |
ea8adc8c | 3789 | if show_def_docs { |
0531ce1d | 3790 | document_short(w, item, link, &prefix)?; |
ea8adc8c | 3791 | } |
a7813a04 | 3792 | } |
1a4d82fc | 3793 | } |
a7813a04 | 3794 | Ok(()) |
1a4d82fc JJ |
3795 | } |
3796 | ||
a7813a04 | 3797 | let traits = &cache().traits; |
2c00a5a8 | 3798 | let trait_ = i.trait_did().map(|did| &traits[&did]); |
a7813a04 | 3799 | |
ea8adc8c XL |
3800 | if !show_def_docs { |
3801 | write!(w, "<span class='docblock autohide'>")?; | |
3802 | } | |
3803 | ||
54a0048b | 3804 | write!(w, "<div class='impl-items'>")?; |
a7813a04 | 3805 | for trait_item in &i.inner_impl().items { |
9e0c209e | 3806 | doc_impl_item(w, cx, trait_item, link, render_mode, |
ea8adc8c | 3807 | false, outer_version, trait_, show_def_docs)?; |
1a4d82fc JJ |
3808 | } |
3809 | ||
d9579d0f | 3810 | fn render_default_items(w: &mut fmt::Formatter, |
e9174d1e | 3811 | cx: &Context, |
d9579d0f | 3812 | t: &clean::Trait, |
54a0048b | 3813 | i: &clean::Impl, |
9e0c209e | 3814 | render_mode: RenderMode, |
ea8adc8c XL |
3815 | outer_version: Option<&str>, |
3816 | show_def_docs: bool) -> fmt::Result { | |
85aaf69f | 3817 | for trait_item in &t.items { |
c34b1796 | 3818 | let n = trait_item.name.clone(); |
54a0048b SL |
3819 | if i.items.iter().find(|m| m.name == n).is_some() { |
3820 | continue; | |
1a4d82fc | 3821 | } |
54a0048b SL |
3822 | let did = i.trait_.as_ref().unwrap().def_id().unwrap(); |
3823 | let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods); | |
1a4d82fc | 3824 | |
9e0c209e | 3825 | doc_impl_item(w, cx, trait_item, assoc_link, render_mode, true, |
ea8adc8c | 3826 | outer_version, None, show_def_docs)?; |
1a4d82fc JJ |
3827 | } |
3828 | Ok(()) | |
3829 | } | |
3830 | ||
3831 | // If we've implemented a trait, then also emit documentation for all | |
54a0048b | 3832 | // default items which weren't overridden in the implementation block. |
a7813a04 | 3833 | if let Some(t) = trait_ { |
ea8adc8c XL |
3834 | render_default_items(w, cx, t, &i.inner_impl(), |
3835 | render_mode, outer_version, show_def_docs)?; | |
1a4d82fc | 3836 | } |
54a0048b | 3837 | write!(w, "</div>")?; |
ea8adc8c XL |
3838 | |
3839 | if !show_def_docs { | |
3840 | write!(w, "</span>")?; | |
3841 | } | |
3842 | ||
1a4d82fc JJ |
3843 | Ok(()) |
3844 | } | |
3845 | ||
e9174d1e | 3846 | fn item_typedef(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 3847 | t: &clean::Typedef) -> fmt::Result { |
8bb4bdeb XL |
3848 | write!(w, "<pre class='rust typedef'>")?; |
3849 | render_attributes(w, it)?; | |
3850 | write!(w, "type {}{}{where_clause} = {type_};</pre>", | |
54a0048b SL |
3851 | it.name.as_ref().unwrap(), |
3852 | t.generics, | |
cc61c64b | 3853 | where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true }, |
54a0048b | 3854 | type_ = t.type_)?; |
1a4d82fc | 3855 | |
041b39d2 XL |
3856 | document(w, cx, it)?; |
3857 | ||
3858 | // Render any items associated directly to this alias, as otherwise they | |
3859 | // won't be visible anywhere in the docs. It would be nice to also show | |
3860 | // associated items from the aliased type (see discussion in #32077), but | |
3861 | // we need #14072 to make sense of the generics. | |
3862 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) | |
1a4d82fc JJ |
3863 | } |
3864 | ||
abe05a73 XL |
3865 | fn item_foreign_type(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item) -> fmt::Result { |
3866 | writeln!(w, "<pre class='rust foreigntype'>extern {{")?; | |
3867 | render_attributes(w, it)?; | |
3868 | write!( | |
3869 | w, | |
3870 | " {}type {};\n}}</pre>", | |
3871 | VisSpace(&it.visibility), | |
3872 | it.name.as_ref().unwrap(), | |
3873 | )?; | |
3874 | ||
3875 | document(w, cx, it)?; | |
3876 | ||
3877 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) | |
3878 | } | |
3879 | ||
85aaf69f | 3880 | impl<'a> fmt::Display for Sidebar<'a> { |
1a4d82fc JJ |
3881 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
3882 | let cx = self.cx; | |
3883 | let it = self.item; | |
c34b1796 AL |
3884 | let parentlen = cx.current.len() - if it.is_mod() {1} else {0}; |
3885 | ||
cc61c64b | 3886 | if it.is_struct() || it.is_trait() || it.is_primitive() || it.is_union() |
0531ce1d | 3887 | || it.is_enum() || it.is_mod() || it.is_typedef() { |
83c7162d XL |
3888 | write!(fmt, "<p class='location'>{}{}</p>", |
3889 | match it.inner { | |
3890 | clean::StructItem(..) => "Struct ", | |
3891 | clean::TraitItem(..) => "Trait ", | |
3892 | clean::PrimitiveItem(..) => "Primitive Type ", | |
3893 | clean::UnionItem(..) => "Union ", | |
3894 | clean::EnumItem(..) => "Enum ", | |
3895 | clean::TypedefItem(..) => "Type Definition ", | |
3896 | clean::ForeignTypeItem => "Foreign Type ", | |
3897 | clean::ModuleItem(..) => if it.is_crate() { | |
3898 | "Crate " | |
3899 | } else { | |
3900 | "Module " | |
3901 | }, | |
3902 | _ => "", | |
cc61c64b | 3903 | }, |
83c7162d | 3904 | it.name.as_ref().unwrap())?; |
0531ce1d | 3905 | } |
cc61c64b | 3906 | |
0531ce1d XL |
3907 | if it.is_crate() { |
3908 | if let Some(ref version) = cache().crate_version { | |
3909 | write!(fmt, | |
3910 | "<div class='block version'>\ | |
3911 | <p>Version {}</p>\ | |
83c7162d XL |
3912 | </div> |
3913 | <a id='all-types' href='all.html'><p>See all {}'s items</p></a>", | |
3914 | version, | |
3915 | it.name.as_ref().unwrap())?; | |
abe05a73 | 3916 | } |
0531ce1d | 3917 | } |
abe05a73 | 3918 | |
0531ce1d XL |
3919 | write!(fmt, "<div class=\"sidebar-elems\">")?; |
3920 | match it.inner { | |
3921 | clean::StructItem(ref s) => sidebar_struct(fmt, it, s)?, | |
3922 | clean::TraitItem(ref t) => sidebar_trait(fmt, it, t)?, | |
3923 | clean::PrimitiveItem(ref p) => sidebar_primitive(fmt, it, p)?, | |
3924 | clean::UnionItem(ref u) => sidebar_union(fmt, it, u)?, | |
3925 | clean::EnumItem(ref e) => sidebar_enum(fmt, it, e)?, | |
3926 | clean::TypedefItem(ref t, _) => sidebar_typedef(fmt, it, t)?, | |
3927 | clean::ModuleItem(ref m) => sidebar_module(fmt, it, &m.items)?, | |
3928 | clean::ForeignTypeItem => sidebar_foreign_type(fmt, it)?, | |
3929 | _ => (), | |
cc61c64b XL |
3930 | } |
3931 | ||
476ff2be | 3932 | // The sidebar is designed to display sibling functions, modules and |
5bcae85e | 3933 | // other miscellaneous information. since there are lots of sibling |
c34b1796 AL |
3934 | // items (and that causes quadratic growth in large modules), |
3935 | // we refactor common parts into a shared JavaScript file per module. | |
3936 | // still, we don't move everything into JS because we want to preserve | |
3937 | // as much HTML as possible in order to allow non-JS-enabled browsers | |
3938 | // to navigate the documentation (though slightly inefficiently). | |
3939 | ||
54a0048b | 3940 | write!(fmt, "<p class='location'>")?; |
c34b1796 | 3941 | for (i, name) in cx.current.iter().take(parentlen).enumerate() { |
1a4d82fc | 3942 | if i > 0 { |
54a0048b | 3943 | write!(fmt, "::<wbr>")?; |
1a4d82fc | 3944 | } |
54a0048b | 3945 | write!(fmt, "<a href='{}index.html'>{}</a>", |
c30ab7b3 | 3946 | &cx.root_path()[..(cx.current.len() - i - 1) * 3], |
54a0048b | 3947 | *name)?; |
1a4d82fc | 3948 | } |
54a0048b | 3949 | write!(fmt, "</p>")?; |
1a4d82fc | 3950 | |
476ff2be | 3951 | // Sidebar refers to the enclosing module, not this module. |
54a0048b SL |
3952 | let relpath = if it.is_mod() { "../" } else { "" }; |
3953 | write!(fmt, | |
3954 | "<script>window.sidebarCurrent = {{\ | |
3955 | name: '{name}', \ | |
3956 | ty: '{ty}', \ | |
3957 | relpath: '{path}'\ | |
3958 | }};</script>", | |
3959 | name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""), | |
c30ab7b3 | 3960 | ty = it.type_().css_class(), |
54a0048b | 3961 | path = relpath)?; |
c34b1796 | 3962 | if parentlen == 0 { |
476ff2be | 3963 | // There is no sidebar-items.js beyond the crate root path |
c34b1796 AL |
3964 | // FIXME maybe dynamic crate loading can be merged here |
3965 | } else { | |
54a0048b SL |
3966 | write!(fmt, "<script defer src=\"{path}sidebar-items.js\"></script>", |
3967 | path = relpath)?; | |
1a4d82fc | 3968 | } |
0531ce1d XL |
3969 | // Closes sidebar-elems div. |
3970 | write!(fmt, "</div>")?; | |
1a4d82fc | 3971 | |
1a4d82fc JJ |
3972 | Ok(()) |
3973 | } | |
3974 | } | |
3975 | ||
abe05a73 XL |
3976 | fn get_methods(i: &clean::Impl, for_deref: bool) -> Vec<String> { |
3977 | i.items.iter().filter_map(|item| { | |
3978 | match item.name { | |
3979 | // Maybe check with clean::Visibility::Public as well? | |
3980 | Some(ref name) if !name.is_empty() && item.visibility.is_some() && item.is_method() => { | |
3981 | if !for_deref || should_render_item(item, false) { | |
3982 | Some(format!("<a href=\"#method.{name}\">{name}</a>", name = name)) | |
3983 | } else { | |
3984 | None | |
3985 | } | |
3986 | } | |
3987 | _ => None, | |
3988 | } | |
3989 | }).collect::<Vec<_>>() | |
3990 | } | |
3991 | ||
3992 | // The point is to url encode any potential character from a type with genericity. | |
3993 | fn small_url_encode(s: &str) -> String { | |
3994 | s.replace("<", "%3C") | |
3995 | .replace(">", "%3E") | |
3996 | .replace(" ", "%20") | |
3997 | .replace("?", "%3F") | |
3998 | .replace("'", "%27") | |
3999 | .replace("&", "%26") | |
4000 | .replace(",", "%2C") | |
4001 | .replace(":", "%3A") | |
4002 | .replace(";", "%3B") | |
4003 | .replace("[", "%5B") | |
4004 | .replace("]", "%5D") | |
ff7c6d11 | 4005 | .replace("\"", "%22") |
abe05a73 XL |
4006 | } |
4007 | ||
cc61c64b XL |
4008 | fn sidebar_assoc_items(it: &clean::Item) -> String { |
4009 | let mut out = String::new(); | |
4010 | let c = cache(); | |
4011 | if let Some(v) = c.impls.get(&it.def_id) { | |
abe05a73 XL |
4012 | let ret = v.iter() |
4013 | .filter(|i| i.inner_impl().trait_.is_none()) | |
4014 | .flat_map(|i| get_methods(i.inner_impl(), false)) | |
4015 | .collect::<String>(); | |
4016 | if !ret.is_empty() { | |
4017 | out.push_str(&format!("<a class=\"sidebar-title\" href=\"#methods\">Methods\ | |
4018 | </a><div class=\"sidebar-links\">{}</div>", ret)); | |
cc61c64b XL |
4019 | } |
4020 | ||
4021 | if v.iter().any(|i| i.inner_impl().trait_.is_some()) { | |
4022 | if let Some(impl_) = v.iter() | |
4023 | .filter(|i| i.inner_impl().trait_.is_some()) | |
4024 | .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) { | |
4025 | if let Some(target) = impl_.inner_impl().items.iter().filter_map(|item| { | |
4026 | match item.inner { | |
4027 | clean::TypedefItem(ref t, true) => Some(&t.type_), | |
4028 | _ => None, | |
4029 | } | |
4030 | }).next() { | |
4031 | let inner_impl = target.def_id().or(target.primitive_type().and_then(|prim| { | |
4032 | c.primitive_locations.get(&prim).cloned() | |
4033 | })).and_then(|did| c.impls.get(&did)); | |
abe05a73 XL |
4034 | if let Some(impls) = inner_impl { |
4035 | out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">"); | |
ff7c6d11 XL |
4036 | out.push_str(&format!("Methods from {}<Target={}>", |
4037 | Escape(&format!("{:#}", | |
4038 | impl_.inner_impl().trait_.as_ref().unwrap())), | |
4039 | Escape(&format!("{:#}", target)))); | |
abe05a73 XL |
4040 | out.push_str("</a>"); |
4041 | let ret = impls.iter() | |
4042 | .filter(|i| i.inner_impl().trait_.is_none()) | |
4043 | .flat_map(|i| get_methods(i.inner_impl(), true)) | |
4044 | .collect::<String>(); | |
4045 | out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret)); | |
cc61c64b XL |
4046 | } |
4047 | } | |
4048 | } | |
0531ce1d XL |
4049 | let format_impls = |impls: Vec<&Impl>| { |
4050 | let mut links = HashSet::new(); | |
4051 | impls.iter() | |
4052 | .filter_map(|i| { | |
4053 | let is_negative_impl = is_negative_impl(i.inner_impl()); | |
4054 | if let Some(ref i) = i.inner_impl().trait_ { | |
4055 | let i_display = format!("{:#}", i); | |
4056 | let out = Escape(&i_display); | |
4057 | let encoded = small_url_encode(&format!("{:#}", i)); | |
4058 | let generated = format!("<a href=\"#impl-{}\">{}{}</a>", | |
4059 | encoded, | |
4060 | if is_negative_impl { "!" } else { "" }, | |
4061 | out); | |
4062 | if links.insert(generated.clone()) { | |
4063 | Some(generated) | |
4064 | } else { | |
4065 | None | |
4066 | } | |
ff7c6d11 XL |
4067 | } else { |
4068 | None | |
4069 | } | |
0531ce1d XL |
4070 | }) |
4071 | .collect::<String>() | |
4072 | }; | |
4073 | ||
4074 | let (synthetic, concrete) = v | |
4075 | .iter() | |
4076 | .partition::<Vec<_>, _>(|i| i.inner_impl().synthetic); | |
4077 | ||
4078 | let concrete_format = format_impls(concrete); | |
4079 | let synthetic_format = format_impls(synthetic); | |
4080 | ||
4081 | if !concrete_format.is_empty() { | |
abe05a73 XL |
4082 | out.push_str("<a class=\"sidebar-title\" href=\"#implementations\">\ |
4083 | Trait Implementations</a>"); | |
0531ce1d XL |
4084 | out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", concrete_format)); |
4085 | } | |
4086 | ||
4087 | if !synthetic_format.is_empty() { | |
4088 | out.push_str("<a class=\"sidebar-title\" href=\"#synthetic-implementations\">\ | |
4089 | Auto Trait Implementations</a>"); | |
4090 | out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", synthetic_format)); | |
abe05a73 | 4091 | } |
cc61c64b XL |
4092 | } |
4093 | } | |
4094 | ||
4095 | out | |
4096 | } | |
4097 | ||
4098 | fn sidebar_struct(fmt: &mut fmt::Formatter, it: &clean::Item, | |
4099 | s: &clean::Struct) -> fmt::Result { | |
4100 | let mut sidebar = String::new(); | |
abe05a73 | 4101 | let fields = get_struct_fields_name(&s.fields); |
cc61c64b | 4102 | |
abe05a73 | 4103 | if !fields.is_empty() { |
cc61c64b | 4104 | if let doctree::Plain = s.struct_type { |
abe05a73 XL |
4105 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\ |
4106 | <div class=\"sidebar-links\">{}</div>", fields)); | |
cc61c64b XL |
4107 | } |
4108 | } | |
4109 | ||
4110 | sidebar.push_str(&sidebar_assoc_items(it)); | |
4111 | ||
4112 | if !sidebar.is_empty() { | |
abe05a73 | 4113 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?; |
cc61c64b XL |
4114 | } |
4115 | Ok(()) | |
4116 | } | |
4117 | ||
abe05a73 XL |
4118 | fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> { |
4119 | match item.inner { | |
4120 | clean::ItemEnum::ImplItem(ref i) => { | |
4121 | if let Some(ref trait_) = i.trait_ { | |
4122 | Some((format!("{:#}", i.for_), format!("{:#}", trait_))) | |
4123 | } else { | |
4124 | None | |
4125 | } | |
4126 | }, | |
4127 | _ => None, | |
4128 | } | |
4129 | } | |
4130 | ||
ff7c6d11 XL |
4131 | fn is_negative_impl(i: &clean::Impl) -> bool { |
4132 | i.polarity == Some(clean::ImplPolarity::Negative) | |
4133 | } | |
4134 | ||
cc61c64b XL |
4135 | fn sidebar_trait(fmt: &mut fmt::Formatter, it: &clean::Item, |
4136 | t: &clean::Trait) -> fmt::Result { | |
4137 | let mut sidebar = String::new(); | |
4138 | ||
abe05a73 XL |
4139 | let types = t.items |
4140 | .iter() | |
4141 | .filter_map(|m| { | |
4142 | match m.name { | |
4143 | Some(ref name) if m.is_associated_type() => { | |
4144 | Some(format!("<a href=\"#associatedtype.{name}\">{name}</a>", | |
4145 | name=name)) | |
4146 | } | |
4147 | _ => None, | |
4148 | } | |
4149 | }) | |
4150 | .collect::<String>(); | |
4151 | let consts = t.items | |
4152 | .iter() | |
4153 | .filter_map(|m| { | |
4154 | match m.name { | |
4155 | Some(ref name) if m.is_associated_const() => { | |
4156 | Some(format!("<a href=\"#associatedconstant.{name}\">{name}</a>", | |
4157 | name=name)) | |
4158 | } | |
4159 | _ => None, | |
4160 | } | |
4161 | }) | |
4162 | .collect::<String>(); | |
4163 | let required = t.items | |
4164 | .iter() | |
4165 | .filter_map(|m| { | |
4166 | match m.name { | |
4167 | Some(ref name) if m.is_ty_method() => { | |
4168 | Some(format!("<a href=\"#tymethod.{name}\">{name}</a>", | |
4169 | name=name)) | |
4170 | } | |
4171 | _ => None, | |
4172 | } | |
4173 | }) | |
4174 | .collect::<String>(); | |
4175 | let provided = t.items | |
4176 | .iter() | |
4177 | .filter_map(|m| { | |
4178 | match m.name { | |
4179 | Some(ref name) if m.is_method() => { | |
4180 | Some(format!("<a href=\"#method.{name}\">{name}</a>", name=name)) | |
4181 | } | |
4182 | _ => None, | |
4183 | } | |
4184 | }) | |
4185 | .collect::<String>(); | |
cc61c64b | 4186 | |
abe05a73 XL |
4187 | if !types.is_empty() { |
4188 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#associated-types\">\ | |
4189 | Associated Types</a><div class=\"sidebar-links\">{}</div>", | |
4190 | types)); | |
cc61c64b | 4191 | } |
abe05a73 XL |
4192 | if !consts.is_empty() { |
4193 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#associated-const\">\ | |
4194 | Associated Constants</a><div class=\"sidebar-links\">{}</div>", | |
4195 | consts)); | |
cc61c64b | 4196 | } |
abe05a73 XL |
4197 | if !required.is_empty() { |
4198 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#required-methods\">\ | |
4199 | Required Methods</a><div class=\"sidebar-links\">{}</div>", | |
4200 | required)); | |
cc61c64b | 4201 | } |
abe05a73 XL |
4202 | if !provided.is_empty() { |
4203 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#provided-methods\">\ | |
4204 | Provided Methods</a><div class=\"sidebar-links\">{}</div>", | |
4205 | provided)); | |
cc61c64b XL |
4206 | } |
4207 | ||
ea8adc8c XL |
4208 | let c = cache(); |
4209 | ||
4210 | if let Some(implementors) = c.implementors.get(&it.def_id) { | |
abe05a73 | 4211 | let res = implementors.iter() |
2c00a5a8 | 4212 | .filter(|i| i.inner_impl().for_.def_id() |
0531ce1d | 4213 | .map_or(false, |d| !c.paths.contains_key(&d))) |
abe05a73 | 4214 | .filter_map(|i| { |
2c00a5a8 XL |
4215 | match extract_for_impl_name(&i.impl_item) { |
4216 | Some((ref name, ref url)) => { | |
4217 | Some(format!("<a href=\"#impl-{}\">{}</a>", | |
4218 | small_url_encode(url), | |
4219 | Escape(name))) | |
abe05a73 | 4220 | } |
2c00a5a8 | 4221 | _ => None, |
abe05a73 XL |
4222 | } |
4223 | }) | |
4224 | .collect::<String>(); | |
4225 | if !res.is_empty() { | |
4226 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#foreign-impls\">\ | |
4227 | Implementations on Foreign Types</a><div \ | |
4228 | class=\"sidebar-links\">{}</div>", | |
4229 | res)); | |
4230 | } | |
4231 | } | |
4232 | ||
4233 | sidebar.push_str("<a class=\"sidebar-title\" href=\"#implementors\">Implementors</a>"); | |
0531ce1d XL |
4234 | if t.auto { |
4235 | sidebar.push_str("<a class=\"sidebar-title\" \ | |
4236 | href=\"#synthetic-implementors\">Auto Implementors</a>"); | |
4237 | } | |
ea8adc8c | 4238 | |
abe05a73 | 4239 | sidebar.push_str(&sidebar_assoc_items(it)); |
cc61c64b | 4240 | |
abe05a73 | 4241 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar) |
cc61c64b XL |
4242 | } |
4243 | ||
4244 | fn sidebar_primitive(fmt: &mut fmt::Formatter, it: &clean::Item, | |
4245 | _p: &clean::PrimitiveType) -> fmt::Result { | |
4246 | let sidebar = sidebar_assoc_items(it); | |
4247 | ||
4248 | if !sidebar.is_empty() { | |
abe05a73 | 4249 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?; |
cc61c64b XL |
4250 | } |
4251 | Ok(()) | |
4252 | } | |
4253 | ||
041b39d2 XL |
4254 | fn sidebar_typedef(fmt: &mut fmt::Formatter, it: &clean::Item, |
4255 | _t: &clean::Typedef) -> fmt::Result { | |
4256 | let sidebar = sidebar_assoc_items(it); | |
4257 | ||
4258 | if !sidebar.is_empty() { | |
abe05a73 | 4259 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?; |
041b39d2 XL |
4260 | } |
4261 | Ok(()) | |
4262 | } | |
4263 | ||
abe05a73 XL |
4264 | fn get_struct_fields_name(fields: &[clean::Item]) -> String { |
4265 | fields.iter() | |
4266 | .filter(|f| if let clean::StructFieldItem(..) = f.inner { | |
4267 | true | |
4268 | } else { | |
4269 | false | |
4270 | }) | |
4271 | .filter_map(|f| match f.name { | |
4272 | Some(ref name) => Some(format!("<a href=\"#structfield.{name}\">\ | |
4273 | {name}</a>", name=name)), | |
4274 | _ => None, | |
4275 | }) | |
4276 | .collect() | |
4277 | } | |
4278 | ||
cc61c64b XL |
4279 | fn sidebar_union(fmt: &mut fmt::Formatter, it: &clean::Item, |
4280 | u: &clean::Union) -> fmt::Result { | |
4281 | let mut sidebar = String::new(); | |
abe05a73 | 4282 | let fields = get_struct_fields_name(&u.fields); |
cc61c64b | 4283 | |
abe05a73 XL |
4284 | if !fields.is_empty() { |
4285 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#fields\">Fields</a>\ | |
4286 | <div class=\"sidebar-links\">{}</div>", fields)); | |
cc61c64b XL |
4287 | } |
4288 | ||
4289 | sidebar.push_str(&sidebar_assoc_items(it)); | |
4290 | ||
4291 | if !sidebar.is_empty() { | |
abe05a73 | 4292 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?; |
cc61c64b XL |
4293 | } |
4294 | Ok(()) | |
4295 | } | |
4296 | ||
4297 | fn sidebar_enum(fmt: &mut fmt::Formatter, it: &clean::Item, | |
4298 | e: &clean::Enum) -> fmt::Result { | |
4299 | let mut sidebar = String::new(); | |
4300 | ||
abe05a73 XL |
4301 | let variants = e.variants.iter() |
4302 | .filter_map(|v| match v.name { | |
4303 | Some(ref name) => Some(format!("<a href=\"#variant.{name}\">{name}\ | |
4304 | </a>", name = name)), | |
4305 | _ => None, | |
4306 | }) | |
4307 | .collect::<String>(); | |
4308 | if !variants.is_empty() { | |
4309 | sidebar.push_str(&format!("<a class=\"sidebar-title\" href=\"#variants\">Variants</a>\ | |
4310 | <div class=\"sidebar-links\">{}</div>", variants)); | |
cc61c64b XL |
4311 | } |
4312 | ||
4313 | sidebar.push_str(&sidebar_assoc_items(it)); | |
4314 | ||
4315 | if !sidebar.is_empty() { | |
abe05a73 | 4316 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?; |
cc61c64b XL |
4317 | } |
4318 | Ok(()) | |
4319 | } | |
4320 | ||
4321 | fn sidebar_module(fmt: &mut fmt::Formatter, _it: &clean::Item, | |
4322 | items: &[clean::Item]) -> fmt::Result { | |
4323 | let mut sidebar = String::new(); | |
4324 | ||
4325 | if items.iter().any(|it| it.type_() == ItemType::ExternCrate || | |
4326 | it.type_() == ItemType::Import) { | |
4327 | sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>", | |
4328 | id = "reexports", | |
2c00a5a8 | 4329 | name = "Re-exports")); |
cc61c64b XL |
4330 | } |
4331 | ||
4332 | // ordering taken from item_module, reorder, where it prioritized elements in a certain order | |
4333 | // to print its headings | |
4334 | for &myty in &[ItemType::Primitive, ItemType::Module, ItemType::Macro, ItemType::Struct, | |
4335 | ItemType::Enum, ItemType::Constant, ItemType::Static, ItemType::Trait, | |
4336 | ItemType::Function, ItemType::Typedef, ItemType::Union, ItemType::Impl, | |
4337 | ItemType::TyMethod, ItemType::Method, ItemType::StructField, ItemType::Variant, | |
abe05a73 | 4338 | ItemType::AssociatedType, ItemType::AssociatedConst, ItemType::ForeignType] { |
2c00a5a8 | 4339 | if items.iter().any(|it| !it.is_stripped() && it.type_() == myty) { |
cc61c64b XL |
4340 | let (short, name) = match myty { |
4341 | ItemType::ExternCrate | | |
2c00a5a8 | 4342 | ItemType::Import => ("reexports", "Re-exports"), |
cc61c64b XL |
4343 | ItemType::Module => ("modules", "Modules"), |
4344 | ItemType::Struct => ("structs", "Structs"), | |
4345 | ItemType::Union => ("unions", "Unions"), | |
4346 | ItemType::Enum => ("enums", "Enums"), | |
4347 | ItemType::Function => ("functions", "Functions"), | |
4348 | ItemType::Typedef => ("types", "Type Definitions"), | |
4349 | ItemType::Static => ("statics", "Statics"), | |
4350 | ItemType::Constant => ("constants", "Constants"), | |
4351 | ItemType::Trait => ("traits", "Traits"), | |
4352 | ItemType::Impl => ("impls", "Implementations"), | |
4353 | ItemType::TyMethod => ("tymethods", "Type Methods"), | |
4354 | ItemType::Method => ("methods", "Methods"), | |
4355 | ItemType::StructField => ("fields", "Struct Fields"), | |
4356 | ItemType::Variant => ("variants", "Variants"), | |
4357 | ItemType::Macro => ("macros", "Macros"), | |
4358 | ItemType::Primitive => ("primitives", "Primitive Types"), | |
4359 | ItemType::AssociatedType => ("associated-types", "Associated Types"), | |
4360 | ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), | |
abe05a73 | 4361 | ItemType::ForeignType => ("foreign-types", "Foreign Types"), |
cc61c64b XL |
4362 | }; |
4363 | sidebar.push_str(&format!("<li><a href=\"#{id}\">{name}</a></li>", | |
4364 | id = short, | |
4365 | name = name)); | |
4366 | } | |
4367 | } | |
4368 | ||
4369 | if !sidebar.is_empty() { | |
4370 | write!(fmt, "<div class=\"block items\"><ul>{}</ul></div>", sidebar)?; | |
4371 | } | |
4372 | Ok(()) | |
4373 | } | |
4374 | ||
abe05a73 XL |
4375 | fn sidebar_foreign_type(fmt: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { |
4376 | let sidebar = sidebar_assoc_items(it); | |
4377 | if !sidebar.is_empty() { | |
4378 | write!(fmt, "<div class=\"block items\">{}</div>", sidebar)?; | |
4379 | } | |
4380 | Ok(()) | |
4381 | } | |
4382 | ||
85aaf69f | 4383 | impl<'a> fmt::Display for Source<'a> { |
1a4d82fc JJ |
4384 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
4385 | let Source(s) = *self; | |
4386 | let lines = s.lines().count(); | |
4387 | let mut cols = 0; | |
4388 | let mut tmp = lines; | |
4389 | while tmp > 0 { | |
4390 | cols += 1; | |
4391 | tmp /= 10; | |
4392 | } | |
54a0048b | 4393 | write!(fmt, "<pre class=\"line-numbers\">")?; |
85aaf69f | 4394 | for i in 1..lines + 1 { |
54a0048b | 4395 | write!(fmt, "<span id=\"{0}\">{0:1$}</span>\n", i, cols)?; |
1a4d82fc | 4396 | } |
54a0048b | 4397 | write!(fmt, "</pre>")?; |
ea8adc8c XL |
4398 | write!(fmt, "{}", |
4399 | highlight::render_with_highlighting(s, None, None, None, None))?; | |
1a4d82fc JJ |
4400 | Ok(()) |
4401 | } | |
4402 | } | |
4403 | ||
e9174d1e | 4404 | fn item_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 4405 | t: &clean::Macro) -> fmt::Result { |
0531ce1d XL |
4406 | wrap_into_docblock(w, |w| { |
4407 | w.write_str(&highlight::render_with_highlighting(&t.source, | |
4408 | Some("macro"), | |
4409 | None, | |
4410 | None, | |
4411 | None)) | |
4412 | })?; | |
e9174d1e | 4413 | document(w, cx, it) |
1a4d82fc JJ |
4414 | } |
4415 | ||
e9174d1e | 4416 | fn item_primitive(w: &mut fmt::Formatter, cx: &Context, |
1a4d82fc JJ |
4417 | it: &clean::Item, |
4418 | _p: &clean::PrimitiveType) -> fmt::Result { | |
54a0048b | 4419 | document(w, cx, it)?; |
7453a54e | 4420 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) |
1a4d82fc JJ |
4421 | } |
4422 | ||
54a0048b | 4423 | const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang"; |
1a4d82fc JJ |
4424 | |
4425 | fn make_item_keywords(it: &clean::Item) -> String { | |
54a0048b | 4426 | format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) |
1a4d82fc JJ |
4427 | } |
4428 | ||
a7813a04 XL |
4429 | fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> { |
4430 | let decl = match item.inner { | |
4431 | clean::FunctionItem(ref f) => &f.decl, | |
4432 | clean::MethodItem(ref m) => &m.decl, | |
4433 | clean::TyMethodItem(ref m) => &m.decl, | |
c34b1796 AL |
4434 | _ => return None |
4435 | }; | |
4436 | ||
a7813a04 | 4437 | let inputs = decl.inputs.values.iter().map(|arg| get_index_type(&arg.type_)).collect(); |
c34b1796 AL |
4438 | let output = match decl.output { |
4439 | clean::FunctionRetTy::Return(ref return_type) => Some(get_index_type(return_type)), | |
4440 | _ => None | |
4441 | }; | |
4442 | ||
4443 | Some(IndexItemFunctionType { inputs: inputs, output: output }) | |
4444 | } | |
4445 | ||
4446 | fn get_index_type(clean_type: &clean::Type) -> Type { | |
abe05a73 XL |
4447 | let t = Type { |
4448 | name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()), | |
4449 | generics: get_generics(clean_type), | |
4450 | }; | |
4451 | t | |
c34b1796 AL |
4452 | } |
4453 | ||
0531ce1d XL |
4454 | /// Returns a list of all paths used in the type. |
4455 | /// This is used to help deduplicate imported impls | |
4456 | /// for reexported types. If any of the contained | |
4457 | /// types are re-exported, we don't use the corresponding | |
4458 | /// entry from the js file, as inlining will have already | |
4459 | /// picked up the impl | |
4460 | fn collect_paths_for_type(first_ty: clean::Type) -> Vec<String> { | |
4461 | let mut out = Vec::new(); | |
4462 | let mut visited = FxHashSet(); | |
4463 | let mut work = VecDeque::new(); | |
4464 | let cache = cache(); | |
4465 | ||
4466 | work.push_back(first_ty); | |
4467 | ||
4468 | while let Some(ty) = work.pop_front() { | |
4469 | if !visited.insert(ty.clone()) { | |
4470 | continue; | |
4471 | } | |
4472 | ||
4473 | match ty { | |
4474 | clean::Type::ResolvedPath { did, .. } => { | |
4475 | let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone()); | |
4476 | let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern); | |
4477 | ||
4478 | match fqp { | |
4479 | Some(path) => { | |
4480 | out.push(path.join("::")); | |
4481 | }, | |
4482 | _ => {} | |
4483 | }; | |
4484 | ||
4485 | }, | |
4486 | clean::Type::Tuple(tys) => { | |
4487 | work.extend(tys.into_iter()); | |
4488 | }, | |
4489 | clean::Type::Slice(ty) => { | |
4490 | work.push_back(*ty); | |
4491 | } | |
4492 | clean::Type::Array(ty, _) => { | |
4493 | work.push_back(*ty); | |
4494 | }, | |
4495 | clean::Type::Unique(ty) => { | |
4496 | work.push_back(*ty); | |
4497 | }, | |
4498 | clean::Type::RawPointer(_, ty) => { | |
4499 | work.push_back(*ty); | |
4500 | }, | |
4501 | clean::Type::BorrowedRef { type_, .. } => { | |
4502 | work.push_back(*type_); | |
4503 | }, | |
4504 | clean::Type::QPath { self_type, trait_, .. } => { | |
4505 | work.push_back(*self_type); | |
4506 | work.push_back(*trait_); | |
4507 | }, | |
4508 | _ => {} | |
4509 | } | |
4510 | }; | |
4511 | out | |
4512 | } | |
4513 | ||
abe05a73 | 4514 | fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> { |
c34b1796 AL |
4515 | match *clean_type { |
4516 | clean::ResolvedPath { ref path, .. } => { | |
4517 | let segments = &path.segments; | |
2c00a5a8 XL |
4518 | let path_segment = segments.into_iter().last().unwrap_or_else(|| panic!( |
4519 | "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path", | |
4520 | clean_type, accept_generic | |
4521 | )); | |
4522 | Some(path_segment.name.clone()) | |
abe05a73 XL |
4523 | } |
4524 | clean::Generic(ref s) if accept_generic => Some(s.clone()), | |
c34b1796 | 4525 | clean::Primitive(ref p) => Some(format!("{:?}", p)), |
abe05a73 | 4526 | clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic), |
c34b1796 AL |
4527 | // FIXME: add all from clean::Type. |
4528 | _ => None | |
4529 | } | |
4530 | } | |
4531 | ||
abe05a73 XL |
4532 | fn get_generics(clean_type: &clean::Type) -> Option<Vec<String>> { |
4533 | clean_type.generics() | |
4534 | .and_then(|types| { | |
4535 | let r = types.iter() | |
4536 | .filter_map(|t| get_index_type_name(t, false)) | |
4537 | .map(|s| s.to_ascii_lowercase()) | |
4538 | .collect::<Vec<_>>(); | |
4539 | if r.is_empty() { | |
4540 | None | |
4541 | } else { | |
4542 | Some(r) | |
4543 | } | |
4544 | }) | |
4545 | } | |
4546 | ||
1a4d82fc JJ |
4547 | pub fn cache() -> Arc<Cache> { |
4548 | CACHE_KEY.with(|c| c.borrow().clone()) | |
4549 | } | |
92a42be0 SL |
4550 | |
4551 | #[cfg(test)] | |
4552 | #[test] | |
4553 | fn test_unique_id() { | |
4554 | let input = ["foo", "examples", "examples", "method.into_iter","examples", | |
4555 | "method.into_iter", "foo", "main", "search", "methods", | |
4556 | "examples", "method.into_iter", "assoc_type.Item", "assoc_type.Item"]; | |
4557 | let expected = ["foo", "examples", "examples-1", "method.into_iter", "examples-2", | |
4558 | "method.into_iter-1", "foo-1", "main-1", "search-1", "methods-1", | |
4559 | "examples-3", "method.into_iter-2", "assoc_type.Item", "assoc_type.Item-1"]; | |
4560 | ||
4561 | let test = || { | |
4562 | let actual: Vec<String> = input.iter().map(|s| derive_id(s.to_string())).collect(); | |
4563 | assert_eq!(&actual[..], expected); | |
4564 | }; | |
4565 | test(); | |
54a0048b | 4566 | reset_ids(true); |
92a42be0 SL |
4567 | test(); |
4568 | } | |
cc61c64b XL |
4569 | |
4570 | #[cfg(test)] | |
4571 | #[test] | |
4572 | fn test_name_key() { | |
4573 | assert_eq!(name_key("0"), ("", 0, 1)); | |
4574 | assert_eq!(name_key("123"), ("", 123, 0)); | |
4575 | assert_eq!(name_key("Fruit"), ("Fruit", 0, 0)); | |
4576 | assert_eq!(name_key("Fruit0"), ("Fruit", 0, 1)); | |
4577 | assert_eq!(name_key("Fruit0000"), ("Fruit", 0, 4)); | |
4578 | assert_eq!(name_key("Fruit01"), ("Fruit", 1, 1)); | |
4579 | assert_eq!(name_key("Fruit10"), ("Fruit", 10, 0)); | |
4580 | assert_eq!(name_key("Fruit123"), ("Fruit", 123, 0)); | |
4581 | } | |
4582 | ||
4583 | #[cfg(test)] | |
4584 | #[test] | |
4585 | fn test_name_sorting() { | |
4586 | let names = ["Apple", | |
4587 | "Banana", | |
4588 | "Fruit", "Fruit0", "Fruit00", | |
4589 | "Fruit1", "Fruit01", | |
4590 | "Fruit2", "Fruit02", | |
4591 | "Fruit20", | |
4592 | "Fruit100", | |
4593 | "Pear"]; | |
4594 | let mut sorted = names.to_owned(); | |
4595 | sorted.sort_by_key(|&s| name_key(s)); | |
4596 | assert_eq!(names, sorted); | |
4597 | } |