]>
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 JJ |
34 | //! both occur before the crate is rendered. |
35 | pub use self::ExternalLocation::*; | |
36 | ||
c1a9b12d | 37 | use std::ascii::AsciiExt; |
1a4d82fc | 38 | use std::cell::RefCell; |
85aaf69f | 39 | use std::cmp::Ordering; |
d9579d0f | 40 | use std::collections::{BTreeMap, HashMap, HashSet}; |
1a4d82fc | 41 | use std::default::Default; |
b039eaaf SL |
42 | use std::error; |
43 | use std::fmt::{self, Display, Formatter}; | |
c34b1796 AL |
44 | use std::fs::{self, File}; |
45 | use std::io::prelude::*; | |
46 | use std::io::{self, BufWriter, BufReader}; | |
1a4d82fc | 47 | use std::iter::repeat; |
9346a6ac | 48 | use std::mem; |
7453a54e | 49 | use std::path::{PathBuf, Path, Component}; |
1a4d82fc JJ |
50 | use std::str; |
51 | use std::sync::Arc; | |
52 | ||
53 | use externalfiles::ExternalHtml; | |
54 | ||
7453a54e | 55 | use serialize::json::{ToJson, Json, as_json}; |
e9174d1e | 56 | use syntax::{abi, ast}; |
7453a54e | 57 | use syntax::feature_gate::UnstableFeatures; |
92a42be0 | 58 | use rustc::middle::cstore::LOCAL_CRATE; |
54a0048b | 59 | use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; |
92a42be0 | 60 | use rustc::middle::privacy::AccessLevels; |
b039eaaf | 61 | use rustc::middle::stability; |
7453a54e | 62 | use rustc::session::config::get_unstable_features_setting; |
54a0048b | 63 | use rustc::hir; |
1a4d82fc | 64 | |
54a0048b | 65 | use clean::{self, SelfTy, Attributes, GetDefId}; |
1a4d82fc JJ |
66 | use doctree; |
67 | use fold::DocFolder; | |
d9579d0f | 68 | use html::escape::Escape; |
62682a34 | 69 | use html::format::{ConstnessSpace}; |
d9579d0f AL |
70 | use html::format::{TyParamBounds, WhereClause, href, AbiSpace}; |
71 | use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace}; | |
1a4d82fc | 72 | use html::item_type::ItemType; |
62682a34 SL |
73 | use html::markdown::{self, Markdown}; |
74 | use html::{highlight, layout}; | |
1a4d82fc | 75 | |
85aaf69f | 76 | /// A pair of name and its optional document. |
c34b1796 | 77 | pub type NameDoc = (String, Option<String>); |
85aaf69f | 78 | |
1a4d82fc JJ |
79 | /// Major driving force in all rustdoc rendering. This contains information |
80 | /// about where in the tree-like hierarchy rendering is occurring and controls | |
81 | /// how the current page is being rendered. | |
82 | /// | |
83 | /// It is intended that this context is a lightweight object which can be fairly | |
84 | /// easily cloned because it is cloned per work-job (about once per item in the | |
85 | /// rustdoc tree). | |
86 | #[derive(Clone)] | |
87 | pub struct Context { | |
88 | /// Current hierarchy of components leading down to what's currently being | |
89 | /// rendered | |
90 | pub current: Vec<String>, | |
91 | /// String representation of how to get back to the root path of the 'doc/' | |
92 | /// folder in terms of a relative URL. | |
93 | pub root_path: 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, | |
1a4d82fc JJ |
111 | /// This flag indicates whether [src] links should be generated or not. If |
112 | /// the source files are present in the html rendering, then this will be | |
113 | /// `true`. | |
114 | pub include_sources: bool, | |
54a0048b SL |
115 | /// The local file sources we've emitted and their respective url-paths. |
116 | pub local_sources: HashMap<PathBuf, String>, | |
1a4d82fc JJ |
117 | /// All the passes that were run on this crate. |
118 | pub passes: HashSet<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>, | |
1a4d82fc JJ |
125 | } |
126 | ||
127 | /// Indicates where an external crate can be found. | |
128 | pub enum ExternalLocation { | |
129 | /// Remote URL root of the external crate | |
130 | Remote(String), | |
131 | /// This external crate can be found in the local doc/ folder | |
132 | Local, | |
133 | /// The external crate could not be found. | |
134 | Unknown, | |
135 | } | |
136 | ||
137 | /// Metadata about an implementor of a trait. | |
138 | pub struct Implementor { | |
e9174d1e | 139 | pub def_id: DefId, |
1a4d82fc | 140 | pub stability: Option<clean::Stability>, |
c1a9b12d | 141 | pub impl_: clean::Impl, |
1a4d82fc JJ |
142 | } |
143 | ||
144 | /// Metadata about implementations for a type. | |
145 | #[derive(Clone)] | |
146 | pub struct Impl { | |
147 | pub impl_: clean::Impl, | |
148 | pub dox: Option<String>, | |
149 | pub stability: Option<clean::Stability>, | |
150 | } | |
151 | ||
9346a6ac | 152 | impl Impl { |
e9174d1e | 153 | fn trait_did(&self) -> Option<DefId> { |
54a0048b | 154 | self.impl_.trait_.def_id() |
9346a6ac AL |
155 | } |
156 | } | |
157 | ||
b039eaaf SL |
158 | #[derive(Debug)] |
159 | pub struct Error { | |
160 | file: PathBuf, | |
161 | error: io::Error, | |
162 | } | |
163 | ||
164 | impl error::Error for Error { | |
165 | fn description(&self) -> &str { | |
166 | self.error.description() | |
167 | } | |
168 | } | |
169 | ||
170 | impl Display for Error { | |
171 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { | |
172 | write!(f, "\"{}\": {}", self.file.display(), self.error) | |
173 | } | |
174 | } | |
175 | ||
176 | impl Error { | |
177 | pub fn new(e: io::Error, file: &Path) -> Error { | |
178 | Error { | |
179 | file: file.to_path_buf(), | |
180 | error: e, | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | macro_rules! try_err { | |
186 | ($e:expr, $file:expr) => ({ | |
187 | match $e { | |
188 | Ok(e) => e, | |
189 | Err(e) => return Err(Error::new(e, $file)), | |
190 | } | |
191 | }) | |
192 | } | |
193 | ||
1a4d82fc JJ |
194 | /// This cache is used to store information about the `clean::Crate` being |
195 | /// rendered in order to provide more useful documentation. This contains | |
196 | /// information like all implementors of a trait, all traits a type implements, | |
197 | /// documentation for all known traits, etc. | |
198 | /// | |
199 | /// This structure purposefully does not implement `Clone` because it's intended | |
200 | /// to be a fairly large and expensive structure to clone. Instead this adheres | |
201 | /// to `Send` so it may be stored in a `Arc` instance and shared among the various | |
bd371182 | 202 | /// rendering threads. |
1a4d82fc JJ |
203 | #[derive(Default)] |
204 | pub struct Cache { | |
205 | /// Mapping of typaram ids to the name of the type parameter. This is used | |
206 | /// when pretty-printing a type (so pretty printing doesn't have to | |
207 | /// painfully maintain a context like this) | |
e9174d1e | 208 | pub typarams: HashMap<DefId, String>, |
1a4d82fc JJ |
209 | |
210 | /// Maps a type id to all known implementations for that type. This is only | |
211 | /// recognized for intra-crate `ResolvedPath` types, and is used to print | |
212 | /// out extra documentation on the page of an enum/struct. | |
213 | /// | |
214 | /// The values of the map are a list of implementations and documentation | |
215 | /// found on that implementation. | |
e9174d1e | 216 | pub impls: HashMap<DefId, Vec<Impl>>, |
1a4d82fc JJ |
217 | |
218 | /// Maintains a mapping of local crate node ids to the fully qualified name | |
219 | /// and "short type description" of that node. This is used when generating | |
220 | /// URLs when a type is being linked to. External paths are not located in | |
221 | /// this map because the `External` type itself has all the information | |
222 | /// necessary. | |
e9174d1e | 223 | pub paths: HashMap<DefId, (Vec<String>, ItemType)>, |
1a4d82fc JJ |
224 | |
225 | /// Similar to `paths`, but only holds external paths. This is only used for | |
226 | /// generating explicit hyperlinks to other crates. | |
e9174d1e | 227 | pub external_paths: HashMap<DefId, Vec<String>>, |
1a4d82fc JJ |
228 | |
229 | /// This map contains information about all known traits of this crate. | |
230 | /// Implementations of a crate should inherit the documentation of the | |
231 | /// parent trait if no extra documentation is specified, and default methods | |
232 | /// should show up in documentation about trait implementations. | |
e9174d1e | 233 | pub traits: HashMap<DefId, clean::Trait>, |
1a4d82fc JJ |
234 | |
235 | /// When rendering traits, it's often useful to be able to list all | |
236 | /// implementors of the trait, and this mapping is exactly, that: a mapping | |
237 | /// of trait ids to the list of known implementors of the trait | |
e9174d1e | 238 | pub implementors: HashMap<DefId, Vec<Implementor>>, |
1a4d82fc JJ |
239 | |
240 | /// Cache of where external crate documentation can be found. | |
d9579d0f | 241 | pub extern_locations: HashMap<ast::CrateNum, (String, ExternalLocation)>, |
1a4d82fc JJ |
242 | |
243 | /// Cache of where documentation for primitives can be found. | |
244 | pub primitive_locations: HashMap<clean::PrimitiveType, ast::CrateNum>, | |
245 | ||
246 | /// Set of definitions which have been inlined from external crates. | |
e9174d1e | 247 | pub inlined: HashSet<DefId>, |
1a4d82fc JJ |
248 | |
249 | // Private fields only used when initially crawling a crate to build a cache | |
250 | ||
251 | stack: Vec<String>, | |
e9174d1e | 252 | parent_stack: Vec<DefId>, |
7453a54e | 253 | parent_is_trait_impl: bool, |
1a4d82fc | 254 | search_index: Vec<IndexItem>, |
54a0048b | 255 | stripped_mod: bool, |
92a42be0 | 256 | access_levels: AccessLevels<DefId>, |
e9174d1e | 257 | deref_trait_did: Option<DefId>, |
1a4d82fc JJ |
258 | |
259 | // In rare case where a structure is defined in one module but implemented | |
260 | // in another, if the implementing module is parsed before defining module, | |
261 | // then the fully qualified name of the structure isn't presented in `paths` | |
262 | // yet when its implementation methods are being indexed. Caches such methods | |
263 | // and their parent id here and indexes them at the end of crate parsing. | |
b039eaaf | 264 | orphan_methods: Vec<(DefId, clean::Item)>, |
1a4d82fc JJ |
265 | } |
266 | ||
267 | /// Helper struct to render all source code to HTML pages | |
268 | struct SourceCollector<'a> { | |
54a0048b | 269 | scx: &'a mut SharedContext, |
1a4d82fc | 270 | |
1a4d82fc | 271 | /// Root destination to place all HTML output into |
c34b1796 | 272 | dst: PathBuf, |
1a4d82fc JJ |
273 | } |
274 | ||
275 | /// Wrapper struct to render the source code of a file. This will do things like | |
276 | /// adding line numbers to the left-hand side. | |
277 | struct Source<'a>(&'a str); | |
278 | ||
279 | // Helper structs for rendering items/sidebars and carrying along contextual | |
280 | // information | |
281 | ||
c34b1796 | 282 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
283 | struct Item<'a> { |
284 | cx: &'a Context, | |
285 | item: &'a clean::Item, | |
286 | } | |
287 | ||
288 | struct Sidebar<'a> { cx: &'a Context, item: &'a clean::Item, } | |
289 | ||
290 | /// Struct representing one entry in the JS search index. These are all emitted | |
291 | /// by hand to a large JS file at the end of cache-creation. | |
292 | struct IndexItem { | |
293 | ty: ItemType, | |
294 | name: String, | |
295 | path: String, | |
296 | desc: String, | |
e9174d1e | 297 | parent: Option<DefId>, |
7453a54e | 298 | parent_idx: Option<usize>, |
c34b1796 AL |
299 | search_type: Option<IndexItemFunctionType>, |
300 | } | |
301 | ||
7453a54e SL |
302 | impl ToJson for IndexItem { |
303 | fn to_json(&self) -> Json { | |
304 | assert_eq!(self.parent.is_some(), self.parent_idx.is_some()); | |
305 | ||
306 | let mut data = Vec::with_capacity(6); | |
307 | data.push((self.ty as usize).to_json()); | |
308 | data.push(self.name.to_json()); | |
309 | data.push(self.path.to_json()); | |
310 | data.push(self.desc.to_json()); | |
311 | data.push(self.parent_idx.to_json()); | |
312 | data.push(self.search_type.to_json()); | |
313 | ||
314 | Json::Array(data) | |
315 | } | |
316 | } | |
317 | ||
c34b1796 AL |
318 | /// A type used for the search index. |
319 | struct Type { | |
320 | name: Option<String>, | |
321 | } | |
322 | ||
7453a54e SL |
323 | impl ToJson for Type { |
324 | fn to_json(&self) -> Json { | |
c34b1796 | 325 | match self.name { |
7453a54e SL |
326 | Some(ref name) => { |
327 | let mut data = BTreeMap::new(); | |
328 | data.insert("name".to_owned(), name.to_json()); | |
329 | Json::Object(data) | |
330 | }, | |
331 | None => Json::Null | |
c34b1796 AL |
332 | } |
333 | } | |
334 | } | |
335 | ||
336 | /// Full type of functions/methods in the search index. | |
337 | struct IndexItemFunctionType { | |
338 | inputs: Vec<Type>, | |
339 | output: Option<Type> | |
340 | } | |
341 | ||
7453a54e SL |
342 | impl ToJson for IndexItemFunctionType { |
343 | fn to_json(&self) -> Json { | |
c34b1796 | 344 | // If we couldn't figure out a type, just write `null`. |
7453a54e SL |
345 | if self.inputs.iter().chain(self.output.iter()).any(|ref i| i.name.is_none()) { |
346 | Json::Null | |
347 | } else { | |
348 | let mut data = BTreeMap::new(); | |
349 | data.insert("inputs".to_owned(), self.inputs.to_json()); | |
350 | data.insert("output".to_owned(), self.output.to_json()); | |
351 | Json::Object(data) | |
c34b1796 | 352 | } |
c34b1796 | 353 | } |
1a4d82fc JJ |
354 | } |
355 | ||
356 | // TLS keys used to carry information around during rendering. | |
357 | ||
358 | thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default()); | |
359 | thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> = | |
360 | RefCell::new(Vec::new())); | |
92a42be0 SL |
361 | thread_local!(static USED_ID_MAP: RefCell<HashMap<String, usize>> = |
362 | RefCell::new(init_ids())); | |
363 | ||
364 | fn init_ids() -> HashMap<String, usize> { | |
365 | [ | |
366 | "main", | |
367 | "search", | |
368 | "help", | |
369 | "TOC", | |
370 | "render-detail", | |
371 | "associated-types", | |
372 | "associated-const", | |
373 | "required-methods", | |
374 | "provided-methods", | |
375 | "implementors", | |
376 | "implementors-list", | |
377 | "methods", | |
378 | "deref-methods", | |
379 | "implementations", | |
380 | "derived_implementations" | |
54a0048b | 381 | ].into_iter().map(|id| (String::from(*id), 1)).collect() |
92a42be0 SL |
382 | } |
383 | ||
384 | /// This method resets the local table of used ID attributes. This is typically | |
385 | /// used at the beginning of rendering an entire HTML page to reset from the | |
386 | /// previous state (if any). | |
54a0048b SL |
387 | pub fn reset_ids(embedded: bool) { |
388 | USED_ID_MAP.with(|s| { | |
389 | *s.borrow_mut() = if embedded { | |
390 | init_ids() | |
391 | } else { | |
392 | HashMap::new() | |
393 | }; | |
394 | }); | |
92a42be0 SL |
395 | } |
396 | ||
397 | pub fn derive_id(candidate: String) -> String { | |
398 | USED_ID_MAP.with(|map| { | |
399 | let id = match map.borrow_mut().get_mut(&candidate) { | |
400 | None => candidate, | |
401 | Some(a) => { | |
402 | let id = format!("{}-{}", candidate, *a); | |
403 | *a += 1; | |
404 | id | |
405 | } | |
406 | }; | |
407 | ||
408 | map.borrow_mut().insert(id.clone(), 1); | |
409 | id | |
410 | }) | |
411 | } | |
1a4d82fc JJ |
412 | |
413 | /// Generates the documentation for `crate` into the directory `dst` | |
414 | pub fn run(mut krate: clean::Crate, | |
415 | external_html: &ExternalHtml, | |
c34b1796 | 416 | dst: PathBuf, |
54a0048b SL |
417 | passes: HashSet<String>, |
418 | css_file_extension: Option<PathBuf>) -> Result<(), Error> { | |
c34b1796 AL |
419 | let src_root = match krate.src.parent() { |
420 | Some(p) => p.to_path_buf(), | |
421 | None => PathBuf::new(), | |
422 | }; | |
54a0048b | 423 | let mut scx = SharedContext { |
c34b1796 | 424 | src_root: src_root, |
1a4d82fc | 425 | passes: passes, |
54a0048b SL |
426 | include_sources: true, |
427 | local_sources: HashMap::new(), | |
428 | issue_tracker_base_url: None, | |
1a4d82fc JJ |
429 | layout: layout::Layout { |
430 | logo: "".to_string(), | |
431 | favicon: "".to_string(), | |
432 | external_html: external_html.clone(), | |
433 | krate: krate.name.clone(), | |
434 | playground_url: "".to_string(), | |
435 | }, | |
54a0048b | 436 | css_file_extension: css_file_extension.clone(), |
1a4d82fc JJ |
437 | }; |
438 | ||
1a4d82fc JJ |
439 | // Crawl the crate attributes looking for attributes which control how we're |
440 | // going to emit HTML | |
54a0048b SL |
441 | if let Some(attrs) = krate.module.as_ref().map(|m| m.attrs.list("doc")) { |
442 | for attr in attrs { | |
443 | match *attr { | |
444 | clean::NameValue(ref x, ref s) | |
445 | if "html_favicon_url" == *x => { | |
446 | scx.layout.favicon = s.to_string(); | |
447 | } | |
448 | clean::NameValue(ref x, ref s) | |
449 | if "html_logo_url" == *x => { | |
450 | scx.layout.logo = s.to_string(); | |
451 | } | |
452 | clean::NameValue(ref x, ref s) | |
453 | if "html_playground_url" == *x => { | |
454 | scx.layout.playground_url = s.to_string(); | |
455 | markdown::PLAYGROUND_KRATE.with(|slot| { | |
456 | if slot.borrow().is_none() { | |
457 | let name = krate.name.clone(); | |
458 | *slot.borrow_mut() = Some(Some(name)); | |
459 | } | |
460 | }); | |
1a4d82fc | 461 | } |
54a0048b SL |
462 | clean::NameValue(ref x, ref s) |
463 | if "issue_tracker_base_url" == *x => { | |
464 | scx.issue_tracker_base_url = Some(s.to_string()); | |
465 | } | |
466 | clean::Word(ref x) | |
467 | if "html_no_source" == *x => { | |
468 | scx.include_sources = false; | |
469 | } | |
470 | _ => {} | |
1a4d82fc JJ |
471 | } |
472 | } | |
1a4d82fc | 473 | } |
54a0048b SL |
474 | try_err!(mkdir(&dst), &dst); |
475 | krate = render_sources(&dst, &mut scx, krate)?; | |
476 | let cx = Context { | |
477 | current: Vec::new(), | |
478 | root_path: String::new(), | |
479 | dst: dst, | |
480 | render_redirect_pages: false, | |
481 | shared: Arc::new(scx), | |
482 | }; | |
1a4d82fc JJ |
483 | |
484 | // Crawl the crate to build various caches used for the output | |
485 | let analysis = ::ANALYSISKEY.with(|a| a.clone()); | |
486 | let analysis = analysis.borrow(); | |
92a42be0 SL |
487 | let access_levels = analysis.as_ref().map(|a| a.access_levels.clone()); |
488 | let access_levels = access_levels.unwrap_or(Default::default()); | |
e9174d1e | 489 | let paths: HashMap<DefId, (Vec<String>, ItemType)> = |
1a4d82fc JJ |
490 | analysis.as_ref().map(|a| { |
491 | let paths = a.external_paths.borrow_mut().take().unwrap(); | |
492 | paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from_type_kind(t)))).collect() | |
493 | }).unwrap_or(HashMap::new()); | |
494 | let mut cache = Cache { | |
495 | impls: HashMap::new(), | |
496 | external_paths: paths.iter().map(|(&k, v)| (k, v.0.clone())) | |
497 | .collect(), | |
498 | paths: paths, | |
499 | implementors: HashMap::new(), | |
500 | stack: Vec::new(), | |
501 | parent_stack: Vec::new(), | |
502 | search_index: Vec::new(), | |
7453a54e | 503 | parent_is_trait_impl: false, |
1a4d82fc JJ |
504 | extern_locations: HashMap::new(), |
505 | primitive_locations: HashMap::new(), | |
54a0048b | 506 | stripped_mod: false, |
92a42be0 | 507 | access_levels: access_levels, |
1a4d82fc | 508 | orphan_methods: Vec::new(), |
9346a6ac | 509 | traits: mem::replace(&mut krate.external_traits, HashMap::new()), |
d9579d0f | 510 | deref_trait_did: analysis.as_ref().and_then(|a| a.deref_trait_did), |
1a4d82fc JJ |
511 | typarams: analysis.as_ref().map(|a| { |
512 | a.external_typarams.borrow_mut().take().unwrap() | |
513 | }).unwrap_or(HashMap::new()), | |
514 | inlined: analysis.as_ref().map(|a| { | |
515 | a.inlined.borrow_mut().take().unwrap() | |
516 | }).unwrap_or(HashSet::new()), | |
517 | }; | |
1a4d82fc JJ |
518 | |
519 | // Cache where all our extern crates are located | |
85aaf69f | 520 | for &(n, ref e) in &krate.externs { |
d9579d0f AL |
521 | cache.extern_locations.insert(n, (e.name.clone(), |
522 | extern_location(e, &cx.dst))); | |
b039eaaf | 523 | let did = DefId { krate: n, index: CRATE_DEF_INDEX }; |
1a4d82fc JJ |
524 | cache.paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); |
525 | } | |
526 | ||
527 | // Cache where all known primitives have their documentation located. | |
528 | // | |
529 | // Favor linking to as local extern as possible, so iterate all crates in | |
530 | // reverse topological order. | |
531 | for &(n, ref e) in krate.externs.iter().rev() { | |
85aaf69f | 532 | for &prim in &e.primitives { |
1a4d82fc JJ |
533 | cache.primitive_locations.insert(prim, n); |
534 | } | |
535 | } | |
85aaf69f | 536 | for &prim in &krate.primitives { |
e9174d1e | 537 | cache.primitive_locations.insert(prim, LOCAL_CRATE); |
1a4d82fc JJ |
538 | } |
539 | ||
d9579d0f AL |
540 | cache.stack.push(krate.name.clone()); |
541 | krate = cache.fold_crate(krate); | |
542 | ||
1a4d82fc | 543 | // Build our search index |
b039eaaf | 544 | let index = build_index(&krate, &mut cache); |
1a4d82fc JJ |
545 | |
546 | // Freeze the cache now that the index has been built. Put an Arc into TLS | |
547 | // for future parallelization opportunities | |
548 | let cache = Arc::new(cache); | |
549 | CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone()); | |
550 | CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear()); | |
551 | ||
54a0048b | 552 | write_shared(&cx, &krate, &*cache, index)?; |
1a4d82fc | 553 | |
1a4d82fc | 554 | // And finally render the whole crate's documentation |
d9579d0f | 555 | cx.krate(krate) |
1a4d82fc JJ |
556 | } |
557 | ||
7453a54e | 558 | /// Build the search index from the collected metadata |
b039eaaf | 559 | fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String { |
1a4d82fc | 560 | let mut nodeid_to_pathid = HashMap::new(); |
7453a54e SL |
561 | let mut crate_items = Vec::with_capacity(cache.search_index.len()); |
562 | let mut crate_paths = Vec::<Json>::new(); | |
563 | ||
564 | let Cache { ref mut search_index, | |
565 | ref orphan_methods, | |
566 | ref mut paths, .. } = *cache; | |
567 | ||
568 | // Attach all orphan methods to the type's definition if the type | |
569 | // has since been learned. | |
570 | for &(did, ref item) in orphan_methods { | |
571 | match paths.get(&did) { | |
572 | Some(&(ref fqp, _)) => { | |
573 | // Needed to determine `self` type. | |
574 | let parent_basename = Some(fqp[fqp.len() - 1].clone()); | |
575 | search_index.push(IndexItem { | |
576 | ty: shortty(item), | |
577 | name: item.name.clone().unwrap(), | |
578 | path: fqp[..fqp.len() - 1].join("::"), | |
579 | desc: Escape(&shorter(item.doc_value())).to_string(), | |
580 | parent: Some(did), | |
581 | parent_idx: None, | |
582 | search_type: get_index_search_type(&item, parent_basename), | |
583 | }); | |
584 | }, | |
585 | None => {} | |
586 | } | |
587 | } | |
588 | ||
589 | // Reduce `NodeId` in paths into smaller sequential numbers, | |
590 | // and prune the paths that do not appear in the index. | |
591 | let mut lastpath = String::new(); | |
592 | let mut lastpathid = 0usize; | |
593 | ||
594 | for item in search_index { | |
595 | item.parent_idx = item.parent.map(|nodeid| { | |
596 | if nodeid_to_pathid.contains_key(&nodeid) { | |
597 | *nodeid_to_pathid.get(&nodeid).unwrap() | |
598 | } else { | |
599 | let pathid = lastpathid; | |
600 | nodeid_to_pathid.insert(nodeid, pathid); | |
601 | lastpathid += 1; | |
1a4d82fc | 602 | |
7453a54e SL |
603 | let &(ref fqp, short) = paths.get(&nodeid).unwrap(); |
604 | crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json()); | |
605 | pathid | |
1a4d82fc | 606 | } |
7453a54e | 607 | }); |
1a4d82fc | 608 | |
7453a54e | 609 | // Omit the parent path if it is same to that of the prior item. |
1a4d82fc | 610 | if lastpath == item.path { |
7453a54e | 611 | item.path.clear(); |
1a4d82fc | 612 | } else { |
7453a54e | 613 | lastpath = item.path.clone(); |
1a4d82fc | 614 | } |
7453a54e | 615 | crate_items.push(item.to_json()); |
1a4d82fc JJ |
616 | } |
617 | ||
7453a54e SL |
618 | let crate_doc = krate.module.as_ref().map(|module| { |
619 | Escape(&shorter(module.doc_value())).to_string() | |
620 | }).unwrap_or(String::new()); | |
1a4d82fc | 621 | |
7453a54e SL |
622 | let mut crate_data = BTreeMap::new(); |
623 | crate_data.insert("doc".to_owned(), Json::String(crate_doc)); | |
624 | crate_data.insert("items".to_owned(), Json::Array(crate_items)); | |
625 | crate_data.insert("paths".to_owned(), Json::Array(crate_paths)); | |
1a4d82fc | 626 | |
7453a54e SL |
627 | // Collect the index into a string |
628 | format!("searchIndex[{}] = {};", | |
629 | as_json(&krate.name), | |
630 | Json::Object(crate_data)) | |
1a4d82fc JJ |
631 | } |
632 | ||
633 | fn write_shared(cx: &Context, | |
634 | krate: &clean::Crate, | |
635 | cache: &Cache, | |
b039eaaf | 636 | search_index: String) -> Result<(), Error> { |
1a4d82fc JJ |
637 | // Write out the shared files. Note that these are shared among all rustdoc |
638 | // docs placed in the output directory, so this needs to be a synchronized | |
639 | // operation with respect to all other rustdocs running around. | |
b039eaaf | 640 | try_err!(mkdir(&cx.dst), &cx.dst); |
1a4d82fc JJ |
641 | let _lock = ::flock::Lock::new(&cx.dst.join(".lock")); |
642 | ||
643 | // Add all the static files. These may already exist, but we just | |
644 | // overwrite them anyway to make sure that they're fresh and up-to-date. | |
54a0048b SL |
645 | |
646 | write(cx.dst.join("jquery.js"), | |
647 | include_bytes!("static/jquery-2.1.4.min.js"))?; | |
648 | write(cx.dst.join("main.js"), | |
649 | include_bytes!("static/main.js"))?; | |
650 | write(cx.dst.join("playpen.js"), | |
651 | include_bytes!("static/playpen.js"))?; | |
652 | write(cx.dst.join("rustdoc.css"), | |
653 | include_bytes!("static/rustdoc.css"))?; | |
654 | write(cx.dst.join("main.css"), | |
655 | include_bytes!("static/styles/main.css"))?; | |
656 | if let Some(ref css) = cx.shared.css_file_extension { | |
657 | let mut content = String::new(); | |
658 | let css = css.as_path(); | |
659 | let mut f = try_err!(File::open(css), css); | |
660 | ||
661 | try_err!(f.read_to_string(&mut content), css); | |
662 | let css = cx.dst.join("theme.css"); | |
663 | let css = css.as_path(); | |
664 | let mut f = try_err!(File::create(css), css); | |
665 | try_err!(write!(f, "{}", &content), css); | |
666 | } | |
667 | write(cx.dst.join("normalize.css"), | |
668 | include_bytes!("static/normalize.css"))?; | |
669 | write(cx.dst.join("FiraSans-Regular.woff"), | |
670 | include_bytes!("static/FiraSans-Regular.woff"))?; | |
671 | write(cx.dst.join("FiraSans-Medium.woff"), | |
672 | include_bytes!("static/FiraSans-Medium.woff"))?; | |
673 | write(cx.dst.join("FiraSans-LICENSE.txt"), | |
674 | include_bytes!("static/FiraSans-LICENSE.txt"))?; | |
675 | write(cx.dst.join("Heuristica-Italic.woff"), | |
676 | include_bytes!("static/Heuristica-Italic.woff"))?; | |
677 | write(cx.dst.join("Heuristica-LICENSE.txt"), | |
678 | include_bytes!("static/Heuristica-LICENSE.txt"))?; | |
679 | write(cx.dst.join("SourceSerifPro-Regular.woff"), | |
680 | include_bytes!("static/SourceSerifPro-Regular.woff"))?; | |
681 | write(cx.dst.join("SourceSerifPro-Bold.woff"), | |
682 | include_bytes!("static/SourceSerifPro-Bold.woff"))?; | |
683 | write(cx.dst.join("SourceSerifPro-LICENSE.txt"), | |
684 | include_bytes!("static/SourceSerifPro-LICENSE.txt"))?; | |
685 | write(cx.dst.join("SourceCodePro-Regular.woff"), | |
686 | include_bytes!("static/SourceCodePro-Regular.woff"))?; | |
687 | write(cx.dst.join("SourceCodePro-Semibold.woff"), | |
688 | include_bytes!("static/SourceCodePro-Semibold.woff"))?; | |
689 | write(cx.dst.join("SourceCodePro-LICENSE.txt"), | |
690 | include_bytes!("static/SourceCodePro-LICENSE.txt"))?; | |
691 | write(cx.dst.join("LICENSE-MIT.txt"), | |
692 | include_bytes!("static/LICENSE-MIT.txt"))?; | |
693 | write(cx.dst.join("LICENSE-APACHE.txt"), | |
694 | include_bytes!("static/LICENSE-APACHE.txt"))?; | |
695 | write(cx.dst.join("COPYRIGHT.txt"), | |
696 | include_bytes!("static/COPYRIGHT.txt"))?; | |
1a4d82fc JJ |
697 | |
698 | fn collect(path: &Path, krate: &str, | |
c34b1796 | 699 | key: &str) -> io::Result<Vec<String>> { |
1a4d82fc JJ |
700 | let mut ret = Vec::new(); |
701 | if path.exists() { | |
54a0048b SL |
702 | for line in BufReader::new(File::open(path)?).lines() { |
703 | let line = line?; | |
1a4d82fc JJ |
704 | if !line.starts_with(key) { |
705 | continue | |
706 | } | |
7453a54e | 707 | if line.starts_with(&format!(r#"{}["{}"]"#, key, krate)) { |
1a4d82fc JJ |
708 | continue |
709 | } | |
710 | ret.push(line.to_string()); | |
711 | } | |
712 | } | |
713 | return Ok(ret); | |
714 | } | |
715 | ||
716 | // Update the search index | |
717 | let dst = cx.dst.join("search-index.js"); | |
b039eaaf SL |
718 | let all_indexes = try_err!(collect(&dst, &krate.name, "searchIndex"), &dst); |
719 | let mut w = try_err!(File::create(&dst), &dst); | |
720 | try_err!(writeln!(&mut w, "var searchIndex = {{}};"), &dst); | |
721 | try_err!(writeln!(&mut w, "{}", search_index), &dst); | |
85aaf69f | 722 | for index in &all_indexes { |
b039eaaf | 723 | try_err!(writeln!(&mut w, "{}", *index), &dst); |
1a4d82fc | 724 | } |
b039eaaf | 725 | try_err!(writeln!(&mut w, "initSearch(searchIndex);"), &dst); |
1a4d82fc JJ |
726 | |
727 | // Update the list of all implementors for traits | |
728 | let dst = cx.dst.join("implementors"); | |
b039eaaf | 729 | try_err!(mkdir(&dst), &dst); |
85aaf69f | 730 | for (&did, imps) in &cache.implementors { |
1a4d82fc JJ |
731 | // Private modules can leak through to this phase of rustdoc, which |
732 | // could contain implementations for otherwise private types. In some | |
733 | // rare cases we could find an implementation for an item which wasn't | |
734 | // indexed, so we just skip this step in that case. | |
735 | // | |
736 | // FIXME: this is a vague explanation for why this can't be a `get`, in | |
737 | // theory it should be... | |
738 | let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) { | |
739 | Some(p) => p, | |
740 | None => continue, | |
741 | }; | |
742 | ||
743 | let mut mydst = dst.clone(); | |
85aaf69f SL |
744 | for part in &remote_path[..remote_path.len() - 1] { |
745 | mydst.push(part); | |
b039eaaf | 746 | try_err!(mkdir(&mydst), &mydst); |
1a4d82fc | 747 | } |
c34b1796 AL |
748 | mydst.push(&format!("{}.{}.js", |
749 | remote_item_type.to_static_str(), | |
750 | remote_path[remote_path.len() - 1])); | |
b039eaaf SL |
751 | let all_implementors = try_err!(collect(&mydst, &krate.name, |
752 | "implementors"), | |
753 | &mydst); | |
1a4d82fc | 754 | |
b039eaaf SL |
755 | try_err!(mkdir(mydst.parent().unwrap()), |
756 | &mydst.parent().unwrap().to_path_buf()); | |
757 | let mut f = BufWriter::new(try_err!(File::create(&mydst), &mydst)); | |
758 | try_err!(writeln!(&mut f, "(function() {{var implementors = {{}};"), &mydst); | |
1a4d82fc | 759 | |
85aaf69f | 760 | for implementor in &all_implementors { |
b039eaaf | 761 | try_err!(write!(&mut f, "{}", *implementor), &mydst); |
1a4d82fc JJ |
762 | } |
763 | ||
b039eaaf | 764 | try_err!(write!(&mut f, r"implementors['{}'] = [", krate.name), &mydst); |
85aaf69f | 765 | for imp in imps { |
1a4d82fc JJ |
766 | // If the trait and implementation are in the same crate, then |
767 | // there's no need to emit information about it (there's inlining | |
768 | // going on). If they're in different crates then the crate defining | |
769 | // the trait will be interested in our implementation. | |
770 | if imp.def_id.krate == did.krate { continue } | |
b039eaaf | 771 | try_err!(write!(&mut f, r#""{}","#, imp.impl_), &mydst); |
1a4d82fc | 772 | } |
b039eaaf SL |
773 | try_err!(writeln!(&mut f, r"];"), &mydst); |
774 | try_err!(writeln!(&mut f, "{}", r" | |
1a4d82fc JJ |
775 | if (window.register_implementors) { |
776 | window.register_implementors(implementors); | |
777 | } else { | |
778 | window.pending_implementors = implementors; | |
779 | } | |
b039eaaf SL |
780 | "), &mydst); |
781 | try_err!(writeln!(&mut f, r"}})()"), &mydst); | |
1a4d82fc JJ |
782 | } |
783 | Ok(()) | |
784 | } | |
785 | ||
54a0048b | 786 | fn render_sources(dst: &Path, scx: &mut SharedContext, |
b039eaaf | 787 | krate: clean::Crate) -> Result<clean::Crate, Error> { |
1a4d82fc | 788 | info!("emitting source files"); |
54a0048b | 789 | let dst = dst.join("src"); |
b039eaaf | 790 | try_err!(mkdir(&dst), &dst); |
85aaf69f | 791 | let dst = dst.join(&krate.name); |
b039eaaf | 792 | try_err!(mkdir(&dst), &dst); |
1a4d82fc JJ |
793 | let mut folder = SourceCollector { |
794 | dst: dst, | |
54a0048b | 795 | scx: scx, |
1a4d82fc | 796 | }; |
1a4d82fc JJ |
797 | Ok(folder.fold_crate(krate)) |
798 | } | |
799 | ||
800 | /// Writes the entire contents of a string to a destination, not attempting to | |
801 | /// catch any errors. | |
b039eaaf SL |
802 | fn write(dst: PathBuf, contents: &[u8]) -> Result<(), Error> { |
803 | Ok(try_err!(try_err!(File::create(&dst), &dst).write_all(contents), &dst)) | |
1a4d82fc JJ |
804 | } |
805 | ||
bd371182 | 806 | /// Makes a directory on the filesystem, failing the thread if an error occurs and |
1a4d82fc | 807 | /// skipping if the directory already exists. |
c34b1796 | 808 | fn mkdir(path: &Path) -> io::Result<()> { |
1a4d82fc | 809 | if !path.exists() { |
c34b1796 | 810 | fs::create_dir(path) |
1a4d82fc JJ |
811 | } else { |
812 | Ok(()) | |
813 | } | |
814 | } | |
815 | ||
816 | /// Returns a documentation-level item type from the item. | |
817 | fn shortty(item: &clean::Item) -> ItemType { | |
818 | ItemType::from_item(item) | |
819 | } | |
820 | ||
821 | /// Takes a path to a source file and cleans the path to it. This canonicalizes | |
822 | /// things like ".." to components which preserve the "top down" hierarchy of a | |
c34b1796 AL |
823 | /// static HTML tree. Each component in the cleaned path will be passed as an |
824 | /// argument to `f`. The very last component of the path (ie the file name) will | |
825 | /// be passed to `f` if `keep_filename` is true, and ignored otherwise. | |
1a4d82fc JJ |
826 | // FIXME (#9639): The closure should deal with &[u8] instead of &str |
827 | // FIXME (#9639): This is too conservative, rejecting non-UTF-8 paths | |
c34b1796 | 828 | fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) where |
1a4d82fc JJ |
829 | F: FnMut(&str), |
830 | { | |
1a4d82fc | 831 | // make it relative, if possible |
9cc50fc6 | 832 | let p = p.strip_prefix(src_root).unwrap_or(p); |
1a4d82fc | 833 | |
7453a54e SL |
834 | let mut iter = p.components().peekable(); |
835 | ||
c34b1796 AL |
836 | while let Some(c) = iter.next() { |
837 | if !keep_filename && iter.peek().is_none() { | |
838 | break; | |
839 | } | |
840 | ||
7453a54e SL |
841 | match c { |
842 | Component::ParentDir => f("up"), | |
843 | Component::Normal(c) => f(c.to_str().unwrap()), | |
844 | _ => continue, | |
1a4d82fc JJ |
845 | } |
846 | } | |
847 | } | |
848 | ||
849 | /// Attempts to find where an external crate is located, given that we're | |
850 | /// rendering in to the specified source destination. | |
851 | fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation { | |
852 | // See if there's documentation generated into the local directory | |
85aaf69f | 853 | let local_location = dst.join(&e.name); |
1a4d82fc JJ |
854 | if local_location.is_dir() { |
855 | return Local; | |
856 | } | |
857 | ||
858 | // Failing that, see if there's an attribute specifying where to find this | |
859 | // external crate | |
54a0048b SL |
860 | e.attrs.list("doc").value("html_root_url").map(|url| { |
861 | let mut url = url.to_owned(); | |
862 | if !url.ends_with("/") { | |
863 | url.push('/') | |
1a4d82fc | 864 | } |
54a0048b SL |
865 | Remote(url) |
866 | }).unwrap_or(Unknown) // Well, at least we tried. | |
1a4d82fc JJ |
867 | } |
868 | ||
869 | impl<'a> DocFolder for SourceCollector<'a> { | |
870 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | |
871 | // If we're including source files, and we haven't seen this file yet, | |
872 | // then we need to render it out to the filesystem | |
54a0048b SL |
873 | if self.scx.include_sources |
874 | // skip all invalid spans | |
875 | && item.source.filename != "" | |
876 | // macros from other libraries get special filenames which we can | |
877 | // safely ignore | |
878 | && !(item.source.filename.starts_with("<") | |
879 | && item.source.filename.ends_with("macros>")) { | |
1a4d82fc JJ |
880 | |
881 | // If it turns out that we couldn't read this file, then we probably | |
882 | // can't read any of the files (generating html output from json or | |
883 | // something like that), so just don't include sources for the | |
884 | // entire crate. The other option is maintaining this mapping on a | |
885 | // per-file basis, but that's probably not worth it... | |
54a0048b | 886 | self.scx |
7453a54e | 887 | .include_sources = match self.emit_source(&item.source.filename) { |
1a4d82fc JJ |
888 | Ok(()) => true, |
889 | Err(e) => { | |
890 | println!("warning: source code was requested to be rendered, \ | |
891 | but processing `{}` had an error: {}", | |
892 | item.source.filename, e); | |
893 | println!(" skipping rendering of source code"); | |
894 | false | |
895 | } | |
896 | }; | |
1a4d82fc | 897 | } |
1a4d82fc JJ |
898 | self.fold_item_recur(item) |
899 | } | |
900 | } | |
901 | ||
902 | impl<'a> SourceCollector<'a> { | |
903 | /// Renders the given filename into its corresponding HTML source file. | |
c34b1796 AL |
904 | fn emit_source(&mut self, filename: &str) -> io::Result<()> { |
905 | let p = PathBuf::from(filename); | |
54a0048b SL |
906 | if self.scx.local_sources.contains_key(&p) { |
907 | // We've already emitted this source | |
908 | return Ok(()); | |
909 | } | |
1a4d82fc | 910 | |
c34b1796 | 911 | let mut contents = Vec::new(); |
54a0048b SL |
912 | File::open(&p).and_then(|mut f| f.read_to_end(&mut contents))?; |
913 | ||
85aaf69f | 914 | let contents = str::from_utf8(&contents).unwrap(); |
1a4d82fc JJ |
915 | |
916 | // Remove the utf-8 BOM if any | |
917 | let contents = if contents.starts_with("\u{feff}") { | |
85aaf69f | 918 | &contents[3..] |
1a4d82fc JJ |
919 | } else { |
920 | contents | |
921 | }; | |
922 | ||
923 | // Create the intermediate directories | |
924 | let mut cur = self.dst.clone(); | |
62682a34 | 925 | let mut root_path = String::from("../../"); |
54a0048b SL |
926 | let mut href = String::new(); |
927 | clean_srcpath(&self.scx.src_root, &p, false, |component| { | |
1a4d82fc JJ |
928 | cur.push(component); |
929 | mkdir(&cur).unwrap(); | |
930 | root_path.push_str("../"); | |
54a0048b SL |
931 | href.push_str(component); |
932 | href.push('/'); | |
1a4d82fc | 933 | }); |
c34b1796 AL |
934 | let mut fname = p.file_name().expect("source has no filename") |
935 | .to_os_string(); | |
936 | fname.push(".html"); | |
937 | cur.push(&fname[..]); | |
54a0048b SL |
938 | href.push_str(&fname.to_string_lossy()); |
939 | ||
940 | let mut w = BufWriter::new(File::create(&cur)?); | |
c34b1796 AL |
941 | let title = format!("{} -- source", cur.file_name().unwrap() |
942 | .to_string_lossy()); | |
1a4d82fc JJ |
943 | let desc = format!("Source to the Rust file `{}`.", filename); |
944 | let page = layout::Page { | |
85aaf69f | 945 | title: &title, |
1a4d82fc | 946 | ty: "source", |
85aaf69f SL |
947 | root_path: &root_path, |
948 | description: &desc, | |
54a0048b | 949 | keywords: BASIC_KEYWORDS, |
1a4d82fc | 950 | }; |
54a0048b SL |
951 | layout::render(&mut w, &self.scx.layout, |
952 | &page, &(""), &Source(contents), | |
953 | self.scx.css_file_extension.is_some())?; | |
954 | w.flush()?; | |
955 | self.scx.local_sources.insert(p, href); | |
956 | Ok(()) | |
1a4d82fc JJ |
957 | } |
958 | } | |
959 | ||
960 | impl DocFolder for Cache { | |
961 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | |
54a0048b SL |
962 | // If this is a stripped module, |
963 | // we don't want it or its children in the search index. | |
964 | let orig_stripped_mod = match item.inner { | |
965 | clean::StrippedItem(box clean::ModuleItem(..)) => { | |
966 | let prev = self.stripped_mod; | |
967 | self.stripped_mod = true; | |
1a4d82fc JJ |
968 | prev |
969 | } | |
54a0048b | 970 | _ => self.stripped_mod, |
1a4d82fc JJ |
971 | }; |
972 | ||
973 | // Register any generics to their corresponding string. This is used | |
974 | // when pretty-printing types | |
975 | match item.inner { | |
976 | clean::StructItem(ref s) => self.generics(&s.generics), | |
977 | clean::EnumItem(ref e) => self.generics(&e.generics), | |
978 | clean::FunctionItem(ref f) => self.generics(&f.generics), | |
62682a34 | 979 | clean::TypedefItem(ref t, _) => self.generics(&t.generics), |
1a4d82fc JJ |
980 | clean::TraitItem(ref t) => self.generics(&t.generics), |
981 | clean::ImplItem(ref i) => self.generics(&i.generics), | |
982 | clean::TyMethodItem(ref i) => self.generics(&i.generics), | |
983 | clean::MethodItem(ref i) => self.generics(&i.generics), | |
984 | clean::ForeignFunctionItem(ref f) => self.generics(&f.generics), | |
985 | _ => {} | |
986 | } | |
987 | ||
988 | // Propagate a trait methods' documentation to all implementors of the | |
989 | // trait | |
990 | if let clean::TraitItem(ref t) = item.inner { | |
991 | self.traits.insert(item.def_id, t.clone()); | |
992 | } | |
993 | ||
994 | // Collect all the implementors of traits. | |
995 | if let clean::ImplItem(ref i) = item.inner { | |
54a0048b SL |
996 | if let Some(did) = i.trait_.def_id() { |
997 | self.implementors.entry(did).or_insert(vec![]).push(Implementor { | |
998 | def_id: item.def_id, | |
999 | stability: item.stability.clone(), | |
1000 | impl_: i.clone(), | |
1001 | }); | |
1a4d82fc JJ |
1002 | } |
1003 | } | |
1004 | ||
1005 | // Index this method for searching later on | |
1006 | if let Some(ref s) = item.name { | |
1007 | let (parent, is_method) = match item.inner { | |
54a0048b | 1008 | clean::StrippedItem(..) => ((None, None), false), |
7453a54e SL |
1009 | clean::AssociatedConstItem(..) | |
1010 | clean::TypedefItem(_, true) if self.parent_is_trait_impl => { | |
1011 | // skip associated items in trait impls | |
1012 | ((None, None), false) | |
1013 | } | |
d9579d0f AL |
1014 | clean::AssociatedTypeItem(..) | |
1015 | clean::AssociatedConstItem(..) | | |
1a4d82fc JJ |
1016 | clean::TyMethodItem(..) | |
1017 | clean::StructFieldItem(..) | | |
1018 | clean::VariantItem(..) => { | |
1019 | ((Some(*self.parent_stack.last().unwrap()), | |
85aaf69f | 1020 | Some(&self.stack[..self.stack.len() - 1])), |
1a4d82fc JJ |
1021 | false) |
1022 | } | |
1023 | clean::MethodItem(..) => { | |
9346a6ac | 1024 | if self.parent_stack.is_empty() { |
1a4d82fc JJ |
1025 | ((None, None), false) |
1026 | } else { | |
1027 | let last = self.parent_stack.last().unwrap(); | |
1028 | let did = *last; | |
1029 | let path = match self.paths.get(&did) { | |
1030 | Some(&(_, ItemType::Trait)) => | |
85aaf69f | 1031 | Some(&self.stack[..self.stack.len() - 1]), |
9346a6ac AL |
1032 | // The current stack not necessarily has correlation |
1033 | // for where the type was defined. On the other | |
1034 | // hand, `paths` always has the right | |
1035 | // information if present. | |
1a4d82fc JJ |
1036 | Some(&(ref fqp, ItemType::Struct)) | |
1037 | Some(&(ref fqp, ItemType::Enum)) => | |
85aaf69f SL |
1038 | Some(&fqp[..fqp.len() - 1]), |
1039 | Some(..) => Some(&*self.stack), | |
1a4d82fc JJ |
1040 | None => None |
1041 | }; | |
1042 | ((Some(*last), path), true) | |
1043 | } | |
1044 | } | |
85aaf69f | 1045 | _ => ((None, Some(&*self.stack)), false) |
1a4d82fc | 1046 | }; |
1a4d82fc JJ |
1047 | |
1048 | match parent { | |
54a0048b | 1049 | (parent, Some(path)) if is_method || (!self.stripped_mod) => { |
c34b1796 AL |
1050 | // Needed to determine `self` type. |
1051 | let parent_basename = self.parent_stack.first().and_then(|parent| { | |
1052 | match self.paths.get(parent) { | |
1053 | Some(&(ref fqp, _)) => Some(fqp[fqp.len() - 1].clone()), | |
1054 | _ => None | |
1055 | } | |
1056 | }); | |
54a0048b | 1057 | debug_assert!(!item.is_stripped()); |
c34b1796 | 1058 | |
54a0048b SL |
1059 | // A crate has a module at its root, containing all items, |
1060 | // which should not be indexed. The crate-item itself is | |
1061 | // inserted later on when serializing the search-index. | |
9cc50fc6 SL |
1062 | if item.def_id.index != CRATE_DEF_INDEX { |
1063 | self.search_index.push(IndexItem { | |
1064 | ty: shortty(&item), | |
1065 | name: s.to_string(), | |
1066 | path: path.join("::").to_string(), | |
7453a54e | 1067 | desc: Escape(&shorter(item.doc_value())).to_string(), |
9cc50fc6 | 1068 | parent: parent, |
7453a54e | 1069 | parent_idx: None, |
9cc50fc6 SL |
1070 | search_type: get_index_search_type(&item, parent_basename), |
1071 | }); | |
1072 | } | |
1a4d82fc | 1073 | } |
54a0048b | 1074 | (Some(parent), None) if is_method || (!self.stripped_mod)=> { |
e9174d1e | 1075 | if parent.is_local() { |
1a4d82fc JJ |
1076 | // We have a parent, but we don't know where they're |
1077 | // defined yet. Wait for later to index this item. | |
b039eaaf | 1078 | self.orphan_methods.push((parent, item.clone())) |
1a4d82fc JJ |
1079 | } |
1080 | } | |
1081 | _ => {} | |
1082 | } | |
1083 | } | |
1084 | ||
1085 | // Keep track of the fully qualified path for this item. | |
54a0048b SL |
1086 | let pushed = match item.name { |
1087 | Some(ref n) if !n.is_empty() => { | |
1a4d82fc JJ |
1088 | self.stack.push(n.to_string()); |
1089 | true | |
54a0048b SL |
1090 | } |
1091 | _ => false, | |
1092 | }; | |
1093 | ||
1a4d82fc JJ |
1094 | match item.inner { |
1095 | clean::StructItem(..) | clean::EnumItem(..) | | |
1096 | clean::TypedefItem(..) | clean::TraitItem(..) | | |
1097 | clean::FunctionItem(..) | clean::ModuleItem(..) | | |
54a0048b | 1098 | clean::ForeignFunctionItem(..) if !self.stripped_mod => { |
1a4d82fc JJ |
1099 | // Reexported items mean that the same id can show up twice |
1100 | // in the rustdoc ast that we're looking at. We know, | |
1101 | // however, that a reexported item doesn't show up in the | |
1102 | // `public_items` map, so we can skip inserting into the | |
1103 | // paths map if there was already an entry present and we're | |
1104 | // not a public item. | |
b039eaaf SL |
1105 | if |
1106 | !self.paths.contains_key(&item.def_id) || | |
1107 | !item.def_id.is_local() || | |
92a42be0 | 1108 | self.access_levels.is_public(item.def_id) |
b039eaaf | 1109 | { |
1a4d82fc JJ |
1110 | self.paths.insert(item.def_id, |
1111 | (self.stack.clone(), shortty(&item))); | |
1112 | } | |
1113 | } | |
1114 | // link variants to their parent enum because pages aren't emitted | |
1115 | // for each variant | |
54a0048b | 1116 | clean::VariantItem(..) if !self.stripped_mod => { |
1a4d82fc JJ |
1117 | let mut stack = self.stack.clone(); |
1118 | stack.pop(); | |
1119 | self.paths.insert(item.def_id, (stack, ItemType::Enum)); | |
1120 | } | |
1121 | ||
1122 | clean::PrimitiveItem(..) if item.visibility.is_some() => { | |
1123 | self.paths.insert(item.def_id, (self.stack.clone(), | |
1124 | shortty(&item))); | |
1125 | } | |
1126 | ||
1127 | _ => {} | |
1128 | } | |
1129 | ||
1130 | // Maintain the parent stack | |
7453a54e | 1131 | let orig_parent_is_trait_impl = self.parent_is_trait_impl; |
1a4d82fc JJ |
1132 | let parent_pushed = match item.inner { |
1133 | clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => { | |
1134 | self.parent_stack.push(item.def_id); | |
7453a54e | 1135 | self.parent_is_trait_impl = false; |
1a4d82fc JJ |
1136 | true |
1137 | } | |
1138 | clean::ImplItem(ref i) => { | |
7453a54e | 1139 | self.parent_is_trait_impl = i.trait_.is_some(); |
1a4d82fc JJ |
1140 | match i.for_ { |
1141 | clean::ResolvedPath{ did, .. } => { | |
1142 | self.parent_stack.push(did); | |
1143 | true | |
1144 | } | |
9346a6ac AL |
1145 | ref t => { |
1146 | match t.primitive_type() { | |
1147 | Some(prim) => { | |
b039eaaf | 1148 | let did = DefId::local(prim.to_def_index()); |
9346a6ac AL |
1149 | self.parent_stack.push(did); |
1150 | true | |
1151 | } | |
1152 | _ => false, | |
1153 | } | |
1154 | } | |
1a4d82fc JJ |
1155 | } |
1156 | } | |
1157 | _ => false | |
1158 | }; | |
1159 | ||
1160 | // Once we've recursively found all the generics, then hoard off all the | |
1161 | // implementations elsewhere | |
54a0048b SL |
1162 | let ret = self.fold_item_recur(item).and_then(|item| { |
1163 | if let clean::Item { attrs, inner: clean::ImplItem(i), .. } = item { | |
1164 | // Figure out the id of this impl. This may map to a | |
1165 | // primitive rather than always to a struct/enum. | |
1166 | let did = match i.for_ { | |
1167 | clean::ResolvedPath { did, .. } | | |
1168 | clean::BorrowedRef { | |
1169 | type_: box clean::ResolvedPath { did, .. }, .. | |
1170 | } => { | |
1171 | Some(did) | |
1172 | } | |
1a4d82fc | 1173 | |
54a0048b SL |
1174 | ref t => { |
1175 | t.primitive_type().and_then(|t| { | |
1176 | self.primitive_locations.get(&t).map(|n| { | |
1177 | let id = t.to_def_index(); | |
1178 | DefId { krate: *n, index: id } | |
1179 | }) | |
1180 | }) | |
1a4d82fc | 1181 | } |
54a0048b | 1182 | }; |
1a4d82fc | 1183 | |
54a0048b SL |
1184 | if let Some(did) = did { |
1185 | self.impls.entry(did).or_insert(vec![]).push(Impl { | |
1186 | impl_: i, | |
1187 | dox: attrs.value("doc").map(|s|s.to_owned()), | |
1188 | stability: item.stability.clone(), | |
1189 | }); | |
1a4d82fc | 1190 | } |
54a0048b SL |
1191 | None |
1192 | } else { | |
1193 | Some(item) | |
1a4d82fc | 1194 | } |
54a0048b | 1195 | }); |
1a4d82fc JJ |
1196 | |
1197 | if pushed { self.stack.pop().unwrap(); } | |
1198 | if parent_pushed { self.parent_stack.pop().unwrap(); } | |
54a0048b | 1199 | self.stripped_mod = orig_stripped_mod; |
7453a54e | 1200 | self.parent_is_trait_impl = orig_parent_is_trait_impl; |
1a4d82fc JJ |
1201 | return ret; |
1202 | } | |
1203 | } | |
1204 | ||
1205 | impl<'a> Cache { | |
1206 | fn generics(&mut self, generics: &clean::Generics) { | |
85aaf69f | 1207 | for typ in &generics.type_params { |
1a4d82fc JJ |
1208 | self.typarams.insert(typ.did, typ.name.clone()); |
1209 | } | |
1210 | } | |
1211 | } | |
1212 | ||
1213 | impl Context { | |
1214 | /// Recurse in the directory structure and change the "root path" to make | |
1215 | /// sure it always points to the top (relatively) | |
1216 | fn recurse<T, F>(&mut self, s: String, f: F) -> T where | |
1217 | F: FnOnce(&mut Context) -> T, | |
1218 | { | |
9346a6ac | 1219 | if s.is_empty() { |
1a4d82fc JJ |
1220 | panic!("Unexpected empty destination: {:?}", self.current); |
1221 | } | |
1222 | let prev = self.dst.clone(); | |
85aaf69f | 1223 | self.dst.push(&s); |
1a4d82fc JJ |
1224 | self.root_path.push_str("../"); |
1225 | self.current.push(s); | |
1226 | ||
1227 | info!("Recursing into {}", self.dst.display()); | |
1228 | ||
1229 | mkdir(&self.dst).unwrap(); | |
1230 | let ret = f(self); | |
1231 | ||
1232 | info!("Recursed; leaving {}", self.dst.display()); | |
1233 | ||
1234 | // Go back to where we were at | |
1235 | self.dst = prev; | |
1236 | let len = self.root_path.len(); | |
1237 | self.root_path.truncate(len - 3); | |
1238 | self.current.pop().unwrap(); | |
1239 | ||
1240 | return ret; | |
1241 | } | |
1242 | ||
1243 | /// Main method for rendering a crate. | |
1244 | /// | |
1245 | /// This currently isn't parallelized, but it'd be pretty easy to add | |
1246 | /// parallelization to this function. | |
b039eaaf | 1247 | fn krate(self, mut krate: clean::Crate) -> Result<(), Error> { |
1a4d82fc JJ |
1248 | let mut item = match krate.module.take() { |
1249 | Some(i) => i, | |
1250 | None => return Ok(()) | |
1251 | }; | |
1252 | item.name = Some(krate.name); | |
1253 | ||
1a4d82fc JJ |
1254 | // render the crate documentation |
1255 | let mut work = vec!((self, item)); | |
1a4d82fc | 1256 | |
54a0048b SL |
1257 | while let Some((mut cx, item)) = work.pop() { |
1258 | cx.item(item, |cx, item| { | |
1259 | work.push((cx.clone(), item)) | |
1260 | })? | |
1261 | } | |
1a4d82fc JJ |
1262 | Ok(()) |
1263 | } | |
1264 | ||
1265 | /// Non-parallelized version of rendering an item. This will take the input | |
1266 | /// item, render its contents, and then invoke the specified closure with | |
1267 | /// all sub-items which need to be rendered. | |
1268 | /// | |
1269 | /// The rendering driver uses this closure to queue up more work. | |
b039eaaf | 1270 | fn item<F>(&mut self, item: clean::Item, mut f: F) -> Result<(), Error> where |
1a4d82fc JJ |
1271 | F: FnMut(&mut Context, clean::Item), |
1272 | { | |
c34b1796 AL |
1273 | fn render(w: File, cx: &Context, it: &clean::Item, |
1274 | pushname: bool) -> io::Result<()> { | |
1a4d82fc JJ |
1275 | // A little unfortunate that this is done like this, but it sure |
1276 | // does make formatting *a lot* nicer. | |
1277 | CURRENT_LOCATION_KEY.with(|slot| { | |
1278 | *slot.borrow_mut() = cx.current.clone(); | |
1279 | }); | |
1280 | ||
c1a9b12d | 1281 | let mut title = cx.current.join("::"); |
1a4d82fc | 1282 | if pushname { |
9346a6ac | 1283 | if !title.is_empty() { |
1a4d82fc JJ |
1284 | title.push_str("::"); |
1285 | } | |
85aaf69f | 1286 | title.push_str(it.name.as_ref().unwrap()); |
1a4d82fc JJ |
1287 | } |
1288 | title.push_str(" - Rust"); | |
1289 | let tyname = shortty(it).to_static_str(); | |
54a0048b | 1290 | let desc = if it.is_crate() { |
1a4d82fc | 1291 | format!("API documentation for the Rust `{}` crate.", |
54a0048b | 1292 | cx.shared.layout.krate) |
1a4d82fc JJ |
1293 | } else { |
1294 | format!("API documentation for the Rust `{}` {} in crate `{}`.", | |
54a0048b | 1295 | it.name.as_ref().unwrap(), tyname, cx.shared.layout.krate) |
1a4d82fc JJ |
1296 | }; |
1297 | let keywords = make_item_keywords(it); | |
1298 | let page = layout::Page { | |
1299 | ty: tyname, | |
85aaf69f SL |
1300 | root_path: &cx.root_path, |
1301 | title: &title, | |
1302 | description: &desc, | |
1303 | keywords: &keywords, | |
1a4d82fc JJ |
1304 | }; |
1305 | ||
54a0048b | 1306 | reset_ids(true); |
1a4d82fc JJ |
1307 | |
1308 | // We have a huge number of calls to write, so try to alleviate some | |
1309 | // of the pain by using a buffered writer instead of invoking the | |
1310 | // write syscall all the time. | |
c34b1796 | 1311 | let mut writer = BufWriter::new(w); |
1a4d82fc | 1312 | if !cx.render_redirect_pages { |
54a0048b SL |
1313 | layout::render(&mut writer, &cx.shared.layout, &page, |
1314 | &Sidebar{ cx: cx, item: it }, | |
1315 | &Item{ cx: cx, item: it }, | |
1316 | cx.shared.css_file_extension.is_some())?; | |
1a4d82fc JJ |
1317 | } else { |
1318 | let mut url = repeat("../").take(cx.current.len()) | |
1319 | .collect::<String>(); | |
54a0048b SL |
1320 | if let Some(&(ref names, _)) = cache().paths.get(&it.def_id) { |
1321 | for name in &names[..names.len() - 1] { | |
1322 | url.push_str(name); | |
1323 | url.push_str("/"); | |
1a4d82fc | 1324 | } |
54a0048b SL |
1325 | url.push_str(&item_path(it)); |
1326 | layout::redirect(&mut writer, &url)?; | |
1a4d82fc JJ |
1327 | } |
1328 | } | |
1329 | writer.flush() | |
1330 | } | |
1331 | ||
54a0048b SL |
1332 | // Stripped modules survive the rustdoc passes (i.e. `strip-private`) |
1333 | // if they contain impls for public types. These modules can also | |
1a4d82fc JJ |
1334 | // contain items such as publicly reexported structures. |
1335 | // | |
1336 | // External crates will provide links to these structures, so | |
54a0048b SL |
1337 | // these modules are recursed into, but not rendered normally |
1338 | // (a flag on the context). | |
1a4d82fc | 1339 | if !self.render_redirect_pages { |
54a0048b | 1340 | self.render_redirect_pages = self.maybe_ignore_item(&item); |
1a4d82fc JJ |
1341 | } |
1342 | ||
54a0048b | 1343 | if item.is_mod() { |
1a4d82fc JJ |
1344 | // modules are special because they add a namespace. We also need to |
1345 | // recurse into the items of the module as well. | |
54a0048b SL |
1346 | let name = item.name.as_ref().unwrap().to_string(); |
1347 | let mut item = Some(item); | |
1348 | self.recurse(name, |this| { | |
1349 | let item = item.take().unwrap(); | |
1350 | let joint_dst = this.dst.join("index.html"); | |
1351 | let dst = try_err!(File::create(&joint_dst), &joint_dst); | |
1352 | try_err!(render(dst, this, &item, false), &joint_dst); | |
c34b1796 | 1353 | |
54a0048b SL |
1354 | let m = match item.inner { |
1355 | clean::StrippedItem(box clean::ModuleItem(m)) | | |
1356 | clean::ModuleItem(m) => m, | |
1357 | _ => unreachable!() | |
1358 | }; | |
1a4d82fc | 1359 | |
54a0048b SL |
1360 | // render sidebar-items.js used throughout this module |
1361 | { | |
1362 | let items = this.build_sidebar_items(&m); | |
1363 | let js_dst = this.dst.join("sidebar-items.js"); | |
1364 | let mut js_out = BufWriter::new(try_err!(File::create(&js_dst), &js_dst)); | |
1365 | try_err!(write!(&mut js_out, "initSidebarItems({});", | |
1366 | as_json(&items)), &js_dst); | |
1367 | } | |
b039eaaf | 1368 | |
54a0048b SL |
1369 | for item in m.items { |
1370 | f(this,item); | |
1371 | } | |
b039eaaf | 1372 | Ok(()) |
54a0048b SL |
1373 | }) |
1374 | } else if item.name.is_some() { | |
1375 | let joint_dst = self.dst.join(&item_path(&item)); | |
1a4d82fc | 1376 | |
54a0048b SL |
1377 | let dst = try_err!(File::create(&joint_dst), &joint_dst); |
1378 | try_err!(render(dst, self, &item, true), &joint_dst); | |
1379 | Ok(()) | |
1380 | } else { | |
1381 | Ok(()) | |
1a4d82fc JJ |
1382 | } |
1383 | } | |
1384 | ||
d9579d0f AL |
1385 | fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> { |
1386 | // BTreeMap instead of HashMap to get a sorted output | |
1387 | let mut map = BTreeMap::new(); | |
85aaf69f | 1388 | for item in &m.items { |
54a0048b | 1389 | if self.maybe_ignore_item(item) { continue } |
1a4d82fc | 1390 | |
1a4d82fc JJ |
1391 | let short = shortty(item).to_static_str(); |
1392 | let myname = match item.name { | |
1393 | None => continue, | |
1394 | Some(ref s) => s.to_string(), | |
1395 | }; | |
1396 | let short = short.to_string(); | |
c34b1796 AL |
1397 | map.entry(short).or_insert(vec![]) |
1398 | .push((myname, Some(plain_summary_line(item.doc_value())))); | |
1a4d82fc JJ |
1399 | } |
1400 | ||
85aaf69f | 1401 | for (_, items) in &mut map { |
1a4d82fc JJ |
1402 | items.sort(); |
1403 | } | |
1404 | return map; | |
1405 | } | |
1406 | ||
54a0048b | 1407 | fn maybe_ignore_item(&self, it: &clean::Item) -> bool { |
1a4d82fc | 1408 | match it.inner { |
54a0048b | 1409 | clean::StrippedItem(..) => true, |
1a4d82fc | 1410 | clean::ModuleItem(ref m) => { |
54a0048b SL |
1411 | it.doc_value().is_none() && m.items.is_empty() && it.visibility != Some(hir::Public) |
1412 | }, | |
1a4d82fc JJ |
1413 | _ => false, |
1414 | } | |
1415 | } | |
1416 | } | |
1417 | ||
1418 | impl<'a> Item<'a> { | |
1a4d82fc JJ |
1419 | /// Generate a url appropriate for an `href` attribute back to the source of |
1420 | /// this item. | |
1421 | /// | |
1422 | /// The url generated, when clicked, will redirect the browser back to the | |
1423 | /// original source code. | |
1424 | /// | |
1425 | /// If `None` is returned, then a source link couldn't be generated. This | |
1426 | /// may happen, for example, with externally inlined items where the source | |
1427 | /// of their crate documentation isn't known. | |
54a0048b | 1428 | fn href(&self) -> Option<String> { |
d9579d0f AL |
1429 | let href = if self.item.source.loline == self.item.source.hiline { |
1430 | format!("{}", self.item.source.loline) | |
1431 | } else { | |
1432 | format!("{}-{}", self.item.source.loline, self.item.source.hiline) | |
1433 | }; | |
1434 | ||
1435 | // First check to see if this is an imported macro source. In this case | |
1436 | // we need to handle it specially as cross-crate inlined macros have... | |
1437 | // odd locations! | |
1438 | let imported_macro_from = match self.item.inner { | |
1439 | clean::MacroItem(ref m) => m.imported_from.as_ref(), | |
1440 | _ => None, | |
1441 | }; | |
1442 | if let Some(krate) = imported_macro_from { | |
1443 | let cache = cache(); | |
1444 | let root = cache.extern_locations.values().find(|&&(ref n, _)| { | |
1445 | *krate == *n | |
1446 | }).map(|l| &l.1); | |
1447 | let root = match root { | |
1448 | Some(&Remote(ref s)) => s.to_string(), | |
1449 | Some(&Local) => self.cx.root_path.clone(), | |
1450 | None | Some(&Unknown) => return None, | |
1451 | }; | |
1452 | Some(format!("{root}/{krate}/macro.{name}.html?gotomacrosrc=1", | |
1453 | root = root, | |
1454 | krate = krate, | |
1455 | name = self.item.name.as_ref().unwrap())) | |
1456 | ||
1a4d82fc JJ |
1457 | // If this item is part of the local crate, then we're guaranteed to |
1458 | // know the span, so we plow forward and generate a proper url. The url | |
1459 | // has anchors for the line numbers that we're linking to. | |
e9174d1e | 1460 | } else if self.item.def_id.is_local() { |
54a0048b SL |
1461 | let path = PathBuf::from(&self.item.source.filename); |
1462 | self.cx.shared.local_sources.get(&path).map(|path| { | |
1463 | format!("{root}src/{krate}/{path}#{href}", | |
1464 | root = self.cx.root_path, | |
1465 | krate = self.cx.shared.layout.krate, | |
1466 | path = path, | |
1467 | href = href) | |
1468 | }) | |
1a4d82fc JJ |
1469 | // If this item is not part of the local crate, then things get a little |
1470 | // trickier. We don't actually know the span of the external item, but | |
1471 | // we know that the documentation on the other end knows the span! | |
1472 | // | |
1473 | // In this case, we generate a link to the *documentation* for this type | |
1474 | // in the original crate. There's an extra URL parameter which says that | |
1475 | // we want to go somewhere else, and the JS on the destination page will | |
1476 | // pick it up and instantly redirect the browser to the source code. | |
1477 | // | |
1478 | // If we don't know where the external documentation for this crate is | |
1479 | // located, then we return `None`. | |
1480 | } else { | |
1481 | let cache = cache(); | |
c34b1796 AL |
1482 | let path = &cache.external_paths[&self.item.def_id]; |
1483 | let root = match cache.extern_locations[&self.item.def_id.krate] { | |
d9579d0f AL |
1484 | (_, Remote(ref s)) => s.to_string(), |
1485 | (_, Local) => self.cx.root_path.clone(), | |
1486 | (_, Unknown) => return None, | |
1a4d82fc JJ |
1487 | }; |
1488 | Some(format!("{root}{path}/{file}?gotosrc={goto}", | |
1489 | root = root, | |
c1a9b12d | 1490 | path = path[..path.len() - 1].join("/"), |
1a4d82fc | 1491 | file = item_path(self.item), |
b039eaaf | 1492 | goto = self.item.def_id.index.as_usize())) |
1a4d82fc JJ |
1493 | } |
1494 | } | |
1495 | } | |
1496 | ||
1497 | ||
85aaf69f | 1498 | impl<'a> fmt::Display for Item<'a> { |
1a4d82fc | 1499 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
54a0048b | 1500 | debug_assert!(!self.item.is_stripped()); |
1a4d82fc | 1501 | // Write the breadcrumb trail header for the top |
54a0048b | 1502 | write!(fmt, "\n<h1 class='fqn'><span class='in-band'>")?; |
1a4d82fc JJ |
1503 | match self.item.inner { |
1504 | clean::ModuleItem(ref m) => if m.is_crate { | |
54a0048b | 1505 | write!(fmt, "Crate ")?; |
1a4d82fc | 1506 | } else { |
54a0048b | 1507 | write!(fmt, "Module ")?; |
1a4d82fc | 1508 | }, |
54a0048b SL |
1509 | clean::FunctionItem(..) => write!(fmt, "Function ")?, |
1510 | clean::TraitItem(..) => write!(fmt, "Trait ")?, | |
1511 | clean::StructItem(..) => write!(fmt, "Struct ")?, | |
1512 | clean::EnumItem(..) => write!(fmt, "Enum ")?, | |
1513 | clean::PrimitiveItem(..) => write!(fmt, "Primitive Type ")?, | |
1a4d82fc JJ |
1514 | _ => {} |
1515 | } | |
1516 | let is_primitive = match self.item.inner { | |
1517 | clean::PrimitiveItem(..) => true, | |
1518 | _ => false, | |
1519 | }; | |
1520 | if !is_primitive { | |
85aaf69f | 1521 | let cur = &self.cx.current; |
54a0048b | 1522 | let amt = if self.item.is_mod() { cur.len() - 1 } else { cur.len() }; |
1a4d82fc | 1523 | for (i, component) in cur.iter().enumerate().take(amt) { |
54a0048b SL |
1524 | write!(fmt, "<a href='{}index.html'>{}</a>::<wbr>", |
1525 | repeat("../").take(cur.len() - i - 1) | |
1526 | .collect::<String>(), | |
1527 | component)?; | |
1a4d82fc JJ |
1528 | } |
1529 | } | |
54a0048b SL |
1530 | write!(fmt, "<a class='{}' href=''>{}</a>", |
1531 | shortty(self.item), self.item.name.as_ref().unwrap())?; | |
1a4d82fc | 1532 | |
54a0048b SL |
1533 | write!(fmt, "</span>")?; // in-band |
1534 | write!(fmt, "<span class='out-of-band'>")?; | |
1535 | write!(fmt, | |
1536 | r##"<span id='render-detail'> | |
1537 | <a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs"> | |
1538 | [<span class='inner'>−</span>] | |
1539 | </a> | |
1540 | </span>"##)?; | |
1a4d82fc JJ |
1541 | |
1542 | // Write `src` tag | |
1543 | // | |
1544 | // When this item is part of a `pub use` in a downstream crate, the | |
1545 | // [src] link in the downstream documentation will actually come back to | |
1546 | // this page, and this link will be auto-clicked. The `id` attribute is | |
1547 | // used to find the link to auto-click. | |
54a0048b SL |
1548 | if self.cx.shared.include_sources && !is_primitive { |
1549 | if let Some(l) = self.href() { | |
1550 | write!(fmt, "<a id='src-{}' class='srclink' \ | |
1551 | href='{}' title='{}'>[src]</a>", | |
1552 | self.item.def_id.index.as_usize(), l, "goto source code")?; | |
1a4d82fc JJ |
1553 | } |
1554 | } | |
1555 | ||
54a0048b | 1556 | write!(fmt, "</span>")?; // out-of-band |
1a4d82fc | 1557 | |
54a0048b | 1558 | write!(fmt, "</h1>\n")?; |
1a4d82fc JJ |
1559 | |
1560 | match self.item.inner { | |
1561 | clean::ModuleItem(ref m) => { | |
85aaf69f | 1562 | item_module(fmt, self.cx, self.item, &m.items) |
1a4d82fc JJ |
1563 | } |
1564 | clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => | |
e9174d1e | 1565 | item_function(fmt, self.cx, self.item, f), |
1a4d82fc | 1566 | clean::TraitItem(ref t) => item_trait(fmt, self.cx, self.item, t), |
e9174d1e SL |
1567 | clean::StructItem(ref s) => item_struct(fmt, self.cx, self.item, s), |
1568 | clean::EnumItem(ref e) => item_enum(fmt, self.cx, self.item, e), | |
1569 | clean::TypedefItem(ref t, _) => item_typedef(fmt, self.cx, self.item, t), | |
1570 | clean::MacroItem(ref m) => item_macro(fmt, self.cx, self.item, m), | |
1571 | clean::PrimitiveItem(ref p) => item_primitive(fmt, self.cx, self.item, p), | |
1a4d82fc | 1572 | clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) => |
e9174d1e SL |
1573 | item_static(fmt, self.cx, self.item, i), |
1574 | clean::ConstantItem(ref c) => item_constant(fmt, self.cx, self.item, c), | |
1a4d82fc JJ |
1575 | _ => Ok(()) |
1576 | } | |
1577 | } | |
1578 | } | |
1579 | ||
1580 | fn item_path(item: &clean::Item) -> String { | |
54a0048b SL |
1581 | if item.is_mod() { |
1582 | format!("{}/index.html", item.name.as_ref().unwrap()) | |
1583 | } else { | |
1584 | format!("{}.{}.html", | |
1585 | shortty(item).to_static_str(), | |
1586 | *item.name.as_ref().unwrap()) | |
1a4d82fc JJ |
1587 | } |
1588 | } | |
1589 | ||
1590 | fn full_path(cx: &Context, item: &clean::Item) -> String { | |
c1a9b12d | 1591 | let mut s = cx.current.join("::"); |
1a4d82fc | 1592 | s.push_str("::"); |
85aaf69f | 1593 | s.push_str(item.name.as_ref().unwrap()); |
1a4d82fc JJ |
1594 | return s |
1595 | } | |
1596 | ||
c34b1796 | 1597 | fn shorter<'a>(s: Option<&'a str>) -> String { |
1a4d82fc | 1598 | match s { |
c34b1796 AL |
1599 | Some(s) => s.lines().take_while(|line|{ |
1600 | (*line).chars().any(|chr|{ | |
1601 | !chr.is_whitespace() | |
1602 | }) | |
c1a9b12d | 1603 | }).collect::<Vec<_>>().join("\n"), |
c34b1796 | 1604 | None => "".to_string() |
1a4d82fc JJ |
1605 | } |
1606 | } | |
1607 | ||
85aaf69f | 1608 | #[inline] |
c34b1796 AL |
1609 | fn plain_summary_line(s: Option<&str>) -> String { |
1610 | let line = shorter(s).replace("\n", " "); | |
1611 | markdown::plain_summary_line(&line[..]) | |
85aaf69f SL |
1612 | } |
1613 | ||
e9174d1e SL |
1614 | fn document(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item) -> fmt::Result { |
1615 | if let Some(s) = short_stability(item, cx, true) { | |
54a0048b | 1616 | write!(w, "<div class='stability'>{}</div>", s)?; |
d9579d0f AL |
1617 | } |
1618 | if let Some(s) = item.doc_value() { | |
54a0048b | 1619 | write!(w, "<div class='docblock'>{}</div>", Markdown(s))?; |
1a4d82fc JJ |
1620 | } |
1621 | Ok(()) | |
1622 | } | |
1623 | ||
1624 | fn item_module(w: &mut fmt::Formatter, cx: &Context, | |
1625 | item: &clean::Item, items: &[clean::Item]) -> fmt::Result { | |
54a0048b | 1626 | document(w, cx, item)?; |
1a4d82fc | 1627 | |
85aaf69f | 1628 | let mut indices = (0..items.len()).filter(|i| { |
54a0048b | 1629 | !cx.maybe_ignore_item(&items[*i]) |
c34b1796 | 1630 | }).collect::<Vec<usize>>(); |
1a4d82fc JJ |
1631 | |
1632 | // the order of item types in the listing | |
1633 | fn reorder(ty: ItemType) -> u8 { | |
1634 | match ty { | |
85aaf69f SL |
1635 | ItemType::ExternCrate => 0, |
1636 | ItemType::Import => 1, | |
1637 | ItemType::Primitive => 2, | |
1638 | ItemType::Module => 3, | |
1639 | ItemType::Macro => 4, | |
1640 | ItemType::Struct => 5, | |
1641 | ItemType::Enum => 6, | |
1642 | ItemType::Constant => 7, | |
1643 | ItemType::Static => 8, | |
1644 | ItemType::Trait => 9, | |
1645 | ItemType::Function => 10, | |
1646 | ItemType::Typedef => 12, | |
1647 | _ => 13 + ty as u8, | |
1a4d82fc JJ |
1648 | } |
1649 | } | |
1650 | ||
c34b1796 | 1651 | fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering { |
1a4d82fc JJ |
1652 | let ty1 = shortty(i1); |
1653 | let ty2 = shortty(i2); | |
d9579d0f AL |
1654 | if ty1 != ty2 { |
1655 | return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2)) | |
1656 | } | |
1657 | let s1 = i1.stability.as_ref().map(|s| s.level); | |
1658 | let s2 = i2.stability.as_ref().map(|s| s.level); | |
1659 | match (s1, s2) { | |
b039eaaf SL |
1660 | (Some(stability::Unstable), Some(stability::Stable)) => return Ordering::Greater, |
1661 | (Some(stability::Stable), Some(stability::Unstable)) => return Ordering::Less, | |
d9579d0f | 1662 | _ => {} |
1a4d82fc | 1663 | } |
d9579d0f | 1664 | i1.name.cmp(&i2.name) |
1a4d82fc JJ |
1665 | } |
1666 | ||
1667 | indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); | |
1668 | ||
1669 | debug!("{:?}", indices); | |
1670 | let mut curty = None; | |
85aaf69f | 1671 | for &idx in &indices { |
1a4d82fc | 1672 | let myitem = &items[idx]; |
54a0048b SL |
1673 | if myitem.is_stripped() { |
1674 | continue; | |
1675 | } | |
1a4d82fc JJ |
1676 | |
1677 | let myty = Some(shortty(myitem)); | |
85aaf69f SL |
1678 | if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) { |
1679 | // Put `extern crate` and `use` re-exports in the same section. | |
1680 | curty = myty; | |
1681 | } else if myty != curty { | |
1a4d82fc | 1682 | if curty.is_some() { |
54a0048b | 1683 | write!(w, "</table>")?; |
1a4d82fc JJ |
1684 | } |
1685 | curty = myty; | |
1686 | let (short, name) = match myty.unwrap() { | |
85aaf69f SL |
1687 | ItemType::ExternCrate | |
1688 | ItemType::Import => ("reexports", "Reexports"), | |
1a4d82fc JJ |
1689 | ItemType::Module => ("modules", "Modules"), |
1690 | ItemType::Struct => ("structs", "Structs"), | |
1691 | ItemType::Enum => ("enums", "Enums"), | |
1692 | ItemType::Function => ("functions", "Functions"), | |
1693 | ItemType::Typedef => ("types", "Type Definitions"), | |
1694 | ItemType::Static => ("statics", "Statics"), | |
1695 | ItemType::Constant => ("constants", "Constants"), | |
1696 | ItemType::Trait => ("traits", "Traits"), | |
1697 | ItemType::Impl => ("impls", "Implementations"), | |
1a4d82fc JJ |
1698 | ItemType::TyMethod => ("tymethods", "Type Methods"), |
1699 | ItemType::Method => ("methods", "Methods"), | |
1700 | ItemType::StructField => ("fields", "Struct Fields"), | |
1701 | ItemType::Variant => ("variants", "Variants"), | |
1702 | ItemType::Macro => ("macros", "Macros"), | |
1703 | ItemType::Primitive => ("primitives", "Primitive Types"), | |
1704 | ItemType::AssociatedType => ("associated-types", "Associated Types"), | |
d9579d0f | 1705 | ItemType::AssociatedConst => ("associated-consts", "Associated Constants"), |
1a4d82fc | 1706 | }; |
54a0048b SL |
1707 | write!(w, "<h2 id='{id}' class='section-header'>\ |
1708 | <a href=\"#{id}\">{name}</a></h2>\n<table>", | |
1709 | id = derive_id(short.to_owned()), name = name)?; | |
1a4d82fc JJ |
1710 | } |
1711 | ||
1712 | match myitem.inner { | |
85aaf69f SL |
1713 | clean::ExternCrateItem(ref name, ref src) => { |
1714 | match *src { | |
1715 | Some(ref src) => { | |
54a0048b SL |
1716 | write!(w, "<tr><td><code>{}extern crate {} as {};", |
1717 | VisSpace(&myitem.visibility), | |
1718 | src, | |
1719 | name)? | |
1a4d82fc | 1720 | } |
85aaf69f | 1721 | None => { |
54a0048b SL |
1722 | write!(w, "<tr><td><code>{}extern crate {};", |
1723 | VisSpace(&myitem.visibility), name)? | |
1a4d82fc JJ |
1724 | } |
1725 | } | |
54a0048b | 1726 | write!(w, "</code></td></tr>")?; |
85aaf69f | 1727 | } |
1a4d82fc | 1728 | |
85aaf69f | 1729 | clean::ImportItem(ref import) => { |
54a0048b SL |
1730 | write!(w, "<tr><td><code>{}{}</code></td></tr>", |
1731 | VisSpace(&myitem.visibility), *import)?; | |
1a4d82fc JJ |
1732 | } |
1733 | ||
1734 | _ => { | |
1735 | if myitem.name.is_none() { continue } | |
e9174d1e | 1736 | let stab_docs = if let Some(s) = short_stability(myitem, cx, false) { |
d9579d0f AL |
1737 | format!("[{}]", s) |
1738 | } else { | |
1739 | String::new() | |
1740 | }; | |
7453a54e | 1741 | let doc_value = myitem.doc_value().unwrap_or(""); |
54a0048b SL |
1742 | write!(w, " |
1743 | <tr class='{stab} module-item'> | |
1744 | <td><a class='{class}' href='{href}' | |
1745 | title='{title}'>{name}</a></td> | |
1746 | <td class='docblock short'> | |
1747 | {stab_docs} {docs} | |
1748 | </td> | |
1749 | </tr>", | |
1750 | name = *myitem.name.as_ref().unwrap(), | |
1751 | stab_docs = stab_docs, | |
1752 | docs = shorter(Some(&Markdown(doc_value).to_string())), | |
1753 | class = shortty(myitem), | |
1754 | stab = myitem.stability_class(), | |
1755 | href = item_path(myitem), | |
1756 | title = full_path(cx, myitem))?; | |
1a4d82fc JJ |
1757 | } |
1758 | } | |
1759 | } | |
1760 | ||
1761 | write!(w, "</table>") | |
1762 | } | |
1763 | ||
e9174d1e | 1764 | fn short_stability(item: &clean::Item, cx: &Context, show_reason: bool) -> Option<String> { |
54a0048b | 1765 | item.stability.as_ref().and_then(|stab| { |
d9579d0f AL |
1766 | let reason = if show_reason && !stab.reason.is_empty() { |
1767 | format!(": {}", stab.reason) | |
1768 | } else { | |
1769 | String::new() | |
1770 | }; | |
1771 | let text = if !stab.deprecated_since.is_empty() { | |
1772 | let since = if show_reason { | |
1773 | format!(" since {}", Escape(&stab.deprecated_since)) | |
1774 | } else { | |
1775 | String::new() | |
1776 | }; | |
1777 | format!("Deprecated{}{}", since, Markdown(&reason)) | |
b039eaaf | 1778 | } else if stab.level == stability::Unstable { |
e9174d1e | 1779 | let unstable_extra = if show_reason { |
54a0048b | 1780 | match (!stab.feature.is_empty(), &cx.shared.issue_tracker_base_url, stab.issue) { |
9cc50fc6 | 1781 | (true, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => |
e9174d1e SL |
1782 | format!(" (<code>{}</code> <a href=\"{}{}\">#{}</a>)", |
1783 | Escape(&stab.feature), tracker_url, issue_no, issue_no), | |
9cc50fc6 | 1784 | (false, &Some(ref tracker_url), Some(issue_no)) if issue_no > 0 => |
e9174d1e SL |
1785 | format!(" (<a href=\"{}{}\">#{}</a>)", Escape(&tracker_url), issue_no, |
1786 | issue_no), | |
1787 | (true, _, _) => | |
1788 | format!(" (<code>{}</code>)", Escape(&stab.feature)), | |
1789 | _ => String::new(), | |
1790 | } | |
1791 | } else { | |
1792 | String::new() | |
1793 | }; | |
1794 | format!("Unstable{}{}", unstable_extra, Markdown(&reason)) | |
d9579d0f AL |
1795 | } else { |
1796 | return None | |
1797 | }; | |
1798 | Some(format!("<em class='stab {}'>{}</em>", | |
1799 | item.stability_class(), text)) | |
54a0048b SL |
1800 | }).or_else(|| { |
1801 | item.deprecation.as_ref().and_then(|depr| { | |
9cc50fc6 SL |
1802 | let note = if show_reason && !depr.note.is_empty() { |
1803 | format!(": {}", depr.note) | |
1804 | } else { | |
1805 | String::new() | |
1806 | }; | |
1807 | let since = if show_reason && !depr.since.is_empty() { | |
1808 | format!(" since {}", Escape(&depr.since)) | |
1809 | } else { | |
1810 | String::new() | |
1811 | }; | |
1812 | ||
1813 | let text = format!("Deprecated{}{}", since, Markdown(¬e)); | |
1814 | Some(format!("<em class='stab deprecated'>{}</em>", text)) | |
54a0048b SL |
1815 | }) |
1816 | }) | |
d9579d0f AL |
1817 | } |
1818 | ||
1a4d82fc JJ |
1819 | struct Initializer<'a>(&'a str); |
1820 | ||
85aaf69f | 1821 | impl<'a> fmt::Display for Initializer<'a> { |
1a4d82fc JJ |
1822 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
1823 | let Initializer(s) = *self; | |
9346a6ac | 1824 | if s.is_empty() { return Ok(()); } |
54a0048b | 1825 | write!(f, "<code> = </code>")?; |
85aaf69f | 1826 | write!(f, "<code>{}</code>", s) |
1a4d82fc JJ |
1827 | } |
1828 | } | |
1829 | ||
e9174d1e | 1830 | fn item_constant(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 1831 | c: &clean::Constant) -> fmt::Result { |
54a0048b SL |
1832 | write!(w, "<pre class='rust const'>{vis}const \ |
1833 | {name}: {typ}{init}</pre>", | |
1834 | vis = VisSpace(&it.visibility), | |
85aaf69f | 1835 | name = it.name.as_ref().unwrap(), |
1a4d82fc | 1836 | typ = c.type_, |
54a0048b | 1837 | init = Initializer(&c.expr))?; |
e9174d1e | 1838 | document(w, cx, it) |
1a4d82fc JJ |
1839 | } |
1840 | ||
e9174d1e | 1841 | fn item_static(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 1842 | s: &clean::Static) -> fmt::Result { |
54a0048b SL |
1843 | write!(w, "<pre class='rust static'>{vis}static {mutability}\ |
1844 | {name}: {typ}{init}</pre>", | |
1845 | vis = VisSpace(&it.visibility), | |
1a4d82fc | 1846 | mutability = MutableSpace(s.mutability), |
85aaf69f | 1847 | name = it.name.as_ref().unwrap(), |
1a4d82fc | 1848 | typ = s.type_, |
54a0048b | 1849 | init = Initializer(&s.expr))?; |
e9174d1e | 1850 | document(w, cx, it) |
1a4d82fc JJ |
1851 | } |
1852 | ||
e9174d1e | 1853 | fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 1854 | f: &clean::Function) -> fmt::Result { |
54a0048b | 1855 | // FIXME(#24111): remove when `const_fn` is stabilized |
7453a54e SL |
1856 | let vis_constness = match get_unstable_features_setting() { |
1857 | UnstableFeatures::Allow => f.constness, | |
1858 | _ => hir::Constness::NotConst | |
1859 | }; | |
54a0048b SL |
1860 | write!(w, "<pre class='rust fn'>{vis}{constness}{unsafety}{abi}fn \ |
1861 | {name}{generics}{decl}{where_clause}</pre>", | |
1862 | vis = VisSpace(&it.visibility), | |
7453a54e | 1863 | constness = ConstnessSpace(vis_constness), |
1a4d82fc | 1864 | unsafety = UnsafetySpace(f.unsafety), |
9346a6ac | 1865 | abi = AbiSpace(f.abi), |
85aaf69f | 1866 | name = it.name.as_ref().unwrap(), |
1a4d82fc JJ |
1867 | generics = f.generics, |
1868 | where_clause = WhereClause(&f.generics), | |
54a0048b SL |
1869 | decl = f.decl)?; |
1870 | render_stability_since_raw(w, it.stable_since(), None)?; | |
e9174d1e | 1871 | document(w, cx, it) |
1a4d82fc JJ |
1872 | } |
1873 | ||
1874 | fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, | |
1875 | t: &clean::Trait) -> fmt::Result { | |
1876 | let mut bounds = String::new(); | |
9346a6ac AL |
1877 | if !t.bounds.is_empty() { |
1878 | if !bounds.is_empty() { | |
1a4d82fc JJ |
1879 | bounds.push(' '); |
1880 | } | |
1881 | bounds.push_str(": "); | |
1882 | for (i, p) in t.bounds.iter().enumerate() { | |
1883 | if i > 0 { bounds.push_str(" + "); } | |
85aaf69f | 1884 | bounds.push_str(&format!("{}", *p)); |
1a4d82fc JJ |
1885 | } |
1886 | } | |
1887 | ||
1888 | // Output the trait definition | |
54a0048b SL |
1889 | write!(w, "<pre class='rust trait'>{}{}trait {}{}{}{} ", |
1890 | VisSpace(&it.visibility), | |
1891 | UnsafetySpace(t.unsafety), | |
1892 | it.name.as_ref().unwrap(), | |
1893 | t.generics, | |
1894 | bounds, | |
1895 | WhereClause(&t.generics))?; | |
1896 | ||
1897 | let types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>(); | |
1898 | let consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>(); | |
1899 | let required = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>(); | |
1900 | let provided = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>(); | |
1a4d82fc | 1901 | |
9346a6ac | 1902 | if t.items.is_empty() { |
54a0048b | 1903 | write!(w, "{{ }}")?; |
1a4d82fc | 1904 | } else { |
54a0048b | 1905 | write!(w, "{{\n")?; |
85aaf69f | 1906 | for t in &types { |
54a0048b SL |
1907 | write!(w, " ")?; |
1908 | render_assoc_item(w, t, AssocItemLink::Anchor)?; | |
1909 | write!(w, ";\n")?; | |
d9579d0f AL |
1910 | } |
1911 | if !types.is_empty() && !consts.is_empty() { | |
54a0048b | 1912 | w.write_str("\n")?; |
d9579d0f AL |
1913 | } |
1914 | for t in &consts { | |
54a0048b SL |
1915 | write!(w, " ")?; |
1916 | render_assoc_item(w, t, AssocItemLink::Anchor)?; | |
1917 | write!(w, ";\n")?; | |
1a4d82fc | 1918 | } |
d9579d0f | 1919 | if !consts.is_empty() && !required.is_empty() { |
54a0048b | 1920 | w.write_str("\n")?; |
1a4d82fc | 1921 | } |
85aaf69f | 1922 | for m in &required { |
54a0048b SL |
1923 | write!(w, " ")?; |
1924 | render_assoc_item(w, m, AssocItemLink::Anchor)?; | |
1925 | write!(w, ";\n")?; | |
1a4d82fc | 1926 | } |
9346a6ac | 1927 | if !required.is_empty() && !provided.is_empty() { |
54a0048b | 1928 | w.write_str("\n")?; |
1a4d82fc | 1929 | } |
85aaf69f | 1930 | for m in &provided { |
54a0048b SL |
1931 | write!(w, " ")?; |
1932 | render_assoc_item(w, m, AssocItemLink::Anchor)?; | |
1933 | write!(w, " {{ ... }}\n")?; | |
1a4d82fc | 1934 | } |
54a0048b | 1935 | write!(w, "}}")?; |
1a4d82fc | 1936 | } |
54a0048b | 1937 | write!(w, "</pre>")?; |
1a4d82fc JJ |
1938 | |
1939 | // Trait documentation | |
54a0048b | 1940 | document(w, cx, it)?; |
1a4d82fc | 1941 | |
7453a54e | 1942 | fn trait_item(w: &mut fmt::Formatter, cx: &Context, m: &clean::Item, t: &clean::Item) |
1a4d82fc | 1943 | -> fmt::Result { |
92a42be0 SL |
1944 | let name = m.name.as_ref().unwrap(); |
1945 | let id = derive_id(format!("{}.{}", shortty(m), name)); | |
54a0048b SL |
1946 | write!(w, "<h3 id='{id}' class='method stab {stab}'><code>", |
1947 | id = id, | |
1948 | stab = m.stability_class())?; | |
1949 | render_assoc_item(w, m, AssocItemLink::Anchor)?; | |
1950 | write!(w, "</code>")?; | |
1951 | render_stability_since(w, m, t)?; | |
1952 | write!(w, "</h3>")?; | |
1953 | document(w, cx, m)?; | |
1a4d82fc JJ |
1954 | Ok(()) |
1955 | } | |
1956 | ||
9346a6ac | 1957 | if !types.is_empty() { |
54a0048b | 1958 | write!(w, " |
1a4d82fc JJ |
1959 | <h2 id='associated-types'>Associated Types</h2> |
1960 | <div class='methods'> | |
54a0048b | 1961 | ")?; |
85aaf69f | 1962 | for t in &types { |
54a0048b | 1963 | trait_item(w, cx, *t, it)?; |
1a4d82fc | 1964 | } |
54a0048b | 1965 | write!(w, "</div>")?; |
1a4d82fc JJ |
1966 | } |
1967 | ||
d9579d0f | 1968 | if !consts.is_empty() { |
54a0048b | 1969 | write!(w, " |
d9579d0f AL |
1970 | <h2 id='associated-const'>Associated Constants</h2> |
1971 | <div class='methods'> | |
54a0048b | 1972 | ")?; |
d9579d0f | 1973 | for t in &consts { |
54a0048b | 1974 | trait_item(w, cx, *t, it)?; |
d9579d0f | 1975 | } |
54a0048b | 1976 | write!(w, "</div>")?; |
d9579d0f AL |
1977 | } |
1978 | ||
1a4d82fc | 1979 | // Output the documentation for each function individually |
9346a6ac | 1980 | if !required.is_empty() { |
54a0048b | 1981 | write!(w, " |
1a4d82fc JJ |
1982 | <h2 id='required-methods'>Required Methods</h2> |
1983 | <div class='methods'> | |
54a0048b | 1984 | ")?; |
85aaf69f | 1985 | for m in &required { |
54a0048b | 1986 | trait_item(w, cx, *m, it)?; |
1a4d82fc | 1987 | } |
54a0048b | 1988 | write!(w, "</div>")?; |
1a4d82fc | 1989 | } |
9346a6ac | 1990 | if !provided.is_empty() { |
54a0048b | 1991 | write!(w, " |
1a4d82fc JJ |
1992 | <h2 id='provided-methods'>Provided Methods</h2> |
1993 | <div class='methods'> | |
54a0048b | 1994 | ")?; |
85aaf69f | 1995 | for m in &provided { |
54a0048b | 1996 | trait_item(w, cx, *m, it)?; |
1a4d82fc | 1997 | } |
54a0048b | 1998 | write!(w, "</div>")?; |
1a4d82fc JJ |
1999 | } |
2000 | ||
9346a6ac | 2001 | // If there are methods directly on this trait object, render them here. |
54a0048b | 2002 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?; |
9346a6ac | 2003 | |
1a4d82fc | 2004 | let cache = cache(); |
54a0048b | 2005 | write!(w, " |
1a4d82fc JJ |
2006 | <h2 id='implementors'>Implementors</h2> |
2007 | <ul class='item-list' id='implementors-list'> | |
54a0048b | 2008 | ")?; |
1a4d82fc JJ |
2009 | match cache.implementors.get(&it.def_id) { |
2010 | Some(implementors) => { | |
85aaf69f | 2011 | for i in implementors { |
54a0048b | 2012 | writeln!(w, "<li><code>{}</code></li>", i.impl_)?; |
1a4d82fc JJ |
2013 | } |
2014 | } | |
2015 | None => {} | |
2016 | } | |
54a0048b SL |
2017 | write!(w, "</ul>")?; |
2018 | write!(w, r#"<script type="text/javascript" async | |
2019 | src="{root_path}/implementors/{path}/{ty}.{name}.js"> | |
2020 | </script>"#, | |
2021 | root_path = vec![".."; cx.current.len()].join("/"), | |
2022 | path = if it.def_id.is_local() { | |
2023 | cx.current.join("/") | |
2024 | } else { | |
2025 | let path = &cache.external_paths[&it.def_id]; | |
2026 | path[..path.len() - 1].join("/") | |
2027 | }, | |
2028 | ty = shortty(it).to_static_str(), | |
2029 | name = *it.name.as_ref().unwrap())?; | |
1a4d82fc JJ |
2030 | Ok(()) |
2031 | } | |
2032 | ||
54a0048b SL |
2033 | fn naive_assoc_href(it: &clean::Item, link: AssocItemLink) -> String { |
2034 | use html::item_type::ItemType::*; | |
2035 | ||
2036 | let name = it.name.as_ref().unwrap(); | |
2037 | let ty = match shortty(it) { | |
2038 | Typedef | AssociatedType => AssociatedType, | |
2039 | s@_ => s, | |
2040 | }; | |
2041 | ||
2042 | let anchor = format!("#{}.{}", ty, name); | |
2043 | match link { | |
2044 | AssocItemLink::Anchor => anchor, | |
2045 | AssocItemLink::GotoSource(did, _) => { | |
2046 | href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor) | |
2047 | } | |
2048 | } | |
2049 | } | |
2050 | ||
2051 | fn assoc_const(w: &mut fmt::Formatter, | |
2052 | it: &clean::Item, | |
2053 | ty: &clean::Type, | |
2054 | default: Option<&String>, | |
2055 | link: AssocItemLink) -> fmt::Result { | |
2056 | write!(w, "const <a href='{}' class='constant'>{}</a>", | |
2057 | naive_assoc_href(it, link), | |
2058 | it.name.as_ref().unwrap())?; | |
2059 | ||
2060 | write!(w, ": {}", ty)?; | |
d9579d0f | 2061 | if let Some(default) = default { |
54a0048b | 2062 | write!(w, " = {}", default)?; |
d9579d0f AL |
2063 | } |
2064 | Ok(()) | |
2065 | } | |
2066 | ||
1a4d82fc | 2067 | fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item, |
c34b1796 | 2068 | bounds: &Vec<clean::TyParamBound>, |
54a0048b SL |
2069 | default: Option<&clean::Type>, |
2070 | link: AssocItemLink) -> fmt::Result { | |
2071 | write!(w, "type <a href='{}' class='type'>{}</a>", | |
2072 | naive_assoc_href(it, link), | |
2073 | it.name.as_ref().unwrap())?; | |
9346a6ac | 2074 | if !bounds.is_empty() { |
54a0048b | 2075 | write!(w, ": {}", TyParamBounds(bounds))? |
1a4d82fc | 2076 | } |
54a0048b SL |
2077 | if let Some(default) = default { |
2078 | write!(w, " = {}", default)?; | |
1a4d82fc JJ |
2079 | } |
2080 | Ok(()) | |
2081 | } | |
2082 | ||
7453a54e SL |
2083 | fn render_stability_since_raw<'a>(w: &mut fmt::Formatter, |
2084 | ver: Option<&'a str>, | |
2085 | containing_ver: Option<&'a str>) -> fmt::Result { | |
54a0048b SL |
2086 | if let Some(v) = ver { |
2087 | if containing_ver != ver && v.len() > 0 { | |
2088 | write!(w, "<span class=\"since\">{}</span>", | |
2089 | v)? | |
7453a54e SL |
2090 | } |
2091 | } | |
7453a54e SL |
2092 | Ok(()) |
2093 | } | |
2094 | ||
2095 | fn render_stability_since(w: &mut fmt::Formatter, | |
2096 | item: &clean::Item, | |
2097 | containing_item: &clean::Item) -> fmt::Result { | |
2098 | render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) | |
2099 | } | |
2100 | ||
54a0048b SL |
2101 | fn render_assoc_item(w: &mut fmt::Formatter, |
2102 | item: &clean::Item, | |
d9579d0f | 2103 | link: AssocItemLink) -> fmt::Result { |
62682a34 | 2104 | fn method(w: &mut fmt::Formatter, |
54a0048b | 2105 | meth: &clean::Item, |
e9174d1e SL |
2106 | unsafety: hir::Unsafety, |
2107 | constness: hir::Constness, | |
62682a34 SL |
2108 | abi: abi::Abi, |
2109 | g: &clean::Generics, | |
2110 | selfty: &clean::SelfTy, | |
2111 | d: &clean::FnDecl, | |
2112 | link: AssocItemLink) | |
2113 | -> fmt::Result { | |
85aaf69f SL |
2114 | use syntax::abi::Abi; |
2115 | ||
54a0048b SL |
2116 | let name = meth.name.as_ref().unwrap(); |
2117 | let anchor = format!("#{}.{}", shortty(meth), name); | |
9346a6ac | 2118 | let href = match link { |
d9579d0f | 2119 | AssocItemLink::Anchor => anchor, |
54a0048b SL |
2120 | AssocItemLink::GotoSource(did, provided_methods) => { |
2121 | // We're creating a link from an impl-item to the corresponding | |
2122 | // trait-item and need to map the anchored type accordingly. | |
2123 | let ty = if provided_methods.contains(name) { | |
2124 | ItemType::Method | |
2125 | } else { | |
2126 | ItemType::TyMethod | |
2127 | }; | |
2128 | ||
2129 | href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor) | |
9346a6ac AL |
2130 | } |
2131 | }; | |
54a0048b | 2132 | // FIXME(#24111): remove when `const_fn` is stabilized |
7453a54e SL |
2133 | let vis_constness = match get_unstable_features_setting() { |
2134 | UnstableFeatures::Allow => constness, | |
2135 | _ => hir::Constness::NotConst | |
2136 | }; | |
62682a34 | 2137 | write!(w, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\ |
1a4d82fc | 2138 | {generics}{decl}{where_clause}", |
7453a54e | 2139 | ConstnessSpace(vis_constness), |
92a42be0 | 2140 | UnsafetySpace(unsafety), |
85aaf69f SL |
2141 | match abi { |
2142 | Abi::Rust => String::new(), | |
2143 | a => format!("extern {} ", a.to_string()) | |
2144 | }, | |
9346a6ac AL |
2145 | href = href, |
2146 | name = name, | |
1a4d82fc JJ |
2147 | generics = *g, |
2148 | decl = Method(selfty, d), | |
2149 | where_clause = WhereClause(g)) | |
2150 | } | |
54a0048b SL |
2151 | match item.inner { |
2152 | clean::StrippedItem(..) => Ok(()), | |
1a4d82fc | 2153 | clean::TyMethodItem(ref m) => { |
54a0048b | 2154 | method(w, item, m.unsafety, hir::Constness::NotConst, |
62682a34 | 2155 | m.abi, &m.generics, &m.self_, &m.decl, link) |
1a4d82fc JJ |
2156 | } |
2157 | clean::MethodItem(ref m) => { | |
54a0048b | 2158 | method(w, item, m.unsafety, m.constness, |
62682a34 | 2159 | m.abi, &m.generics, &m.self_, &m.decl, |
9346a6ac | 2160 | link) |
1a4d82fc | 2161 | } |
d9579d0f | 2162 | clean::AssociatedConstItem(ref ty, ref default) => { |
54a0048b | 2163 | assoc_const(w, item, ty, default.as_ref(), link) |
d9579d0f | 2164 | } |
c34b1796 | 2165 | clean::AssociatedTypeItem(ref bounds, ref default) => { |
54a0048b | 2166 | assoc_type(w, item, bounds, default.as_ref(), link) |
1a4d82fc | 2167 | } |
d9579d0f | 2168 | _ => panic!("render_assoc_item called on non-associated-item") |
1a4d82fc JJ |
2169 | } |
2170 | } | |
2171 | ||
e9174d1e | 2172 | fn item_struct(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2173 | s: &clean::Struct) -> fmt::Result { |
54a0048b SL |
2174 | write!(w, "<pre class='rust struct'>")?; |
2175 | render_attributes(w, it)?; | |
2176 | render_struct(w, | |
2177 | it, | |
2178 | Some(&s.generics), | |
2179 | s.struct_type, | |
2180 | &s.fields, | |
2181 | "", | |
2182 | true)?; | |
2183 | write!(w, "</pre>")?; | |
2184 | render_stability_since_raw(w, it.stable_since(), None)?; | |
2185 | ||
2186 | document(w, cx, it)?; | |
1a4d82fc JJ |
2187 | let mut fields = s.fields.iter().filter(|f| { |
2188 | match f.inner { | |
54a0048b | 2189 | clean::StructFieldItem(..) => true, |
1a4d82fc JJ |
2190 | _ => false, |
2191 | } | |
2192 | }).peekable(); | |
2193 | if let doctree::Plain = s.struct_type { | |
2194 | if fields.peek().is_some() { | |
54a0048b | 2195 | write!(w, "<h2 class='fields'>Fields</h2>\n<table>")?; |
1a4d82fc | 2196 | for field in fields { |
54a0048b SL |
2197 | write!(w, "<tr class='stab {stab}'> |
2198 | <td id='{shortty}.{name}'>\ | |
2199 | <code>{name}</code></td><td>", | |
2200 | shortty = ItemType::StructField, | |
2201 | stab = field.stability_class(), | |
2202 | name = field.name.as_ref().unwrap())?; | |
2203 | document(w, cx, field)?; | |
2204 | write!(w, "</td></tr>")?; | |
1a4d82fc | 2205 | } |
54a0048b | 2206 | write!(w, "</table>")?; |
1a4d82fc JJ |
2207 | } |
2208 | } | |
7453a54e | 2209 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) |
1a4d82fc JJ |
2210 | } |
2211 | ||
e9174d1e | 2212 | fn item_enum(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2213 | e: &clean::Enum) -> fmt::Result { |
54a0048b SL |
2214 | write!(w, "<pre class='rust enum'>")?; |
2215 | render_attributes(w, it)?; | |
2216 | write!(w, "{}enum {}{}{}", | |
2217 | VisSpace(&it.visibility), | |
2218 | it.name.as_ref().unwrap(), | |
2219 | e.generics, | |
2220 | WhereClause(&e.generics))?; | |
9346a6ac | 2221 | if e.variants.is_empty() && !e.variants_stripped { |
54a0048b | 2222 | write!(w, " {{}}")?; |
1a4d82fc | 2223 | } else { |
54a0048b | 2224 | write!(w, " {{\n")?; |
85aaf69f | 2225 | for v in &e.variants { |
54a0048b | 2226 | write!(w, " ")?; |
85aaf69f | 2227 | let name = v.name.as_ref().unwrap(); |
1a4d82fc JJ |
2228 | match v.inner { |
2229 | clean::VariantItem(ref var) => { | |
2230 | match var.kind { | |
54a0048b | 2231 | clean::CLikeVariant => write!(w, "{}", name)?, |
1a4d82fc | 2232 | clean::TupleVariant(ref tys) => { |
54a0048b | 2233 | write!(w, "{}(", name)?; |
1a4d82fc JJ |
2234 | for (i, ty) in tys.iter().enumerate() { |
2235 | if i > 0 { | |
54a0048b | 2236 | write!(w, ", ")? |
1a4d82fc | 2237 | } |
54a0048b | 2238 | write!(w, "{}", *ty)?; |
1a4d82fc | 2239 | } |
54a0048b | 2240 | write!(w, ")")?; |
1a4d82fc JJ |
2241 | } |
2242 | clean::StructVariant(ref s) => { | |
54a0048b SL |
2243 | render_struct(w, |
2244 | v, | |
2245 | None, | |
2246 | s.struct_type, | |
2247 | &s.fields, | |
2248 | " ", | |
2249 | false)?; | |
1a4d82fc JJ |
2250 | } |
2251 | } | |
2252 | } | |
2253 | _ => unreachable!() | |
2254 | } | |
54a0048b | 2255 | write!(w, ",\n")?; |
1a4d82fc JJ |
2256 | } |
2257 | ||
2258 | if e.variants_stripped { | |
54a0048b | 2259 | write!(w, " // some variants omitted\n")?; |
1a4d82fc | 2260 | } |
54a0048b | 2261 | write!(w, "}}")?; |
1a4d82fc | 2262 | } |
54a0048b SL |
2263 | write!(w, "</pre>")?; |
2264 | render_stability_since_raw(w, it.stable_since(), None)?; | |
1a4d82fc | 2265 | |
54a0048b | 2266 | document(w, cx, it)?; |
9346a6ac | 2267 | if !e.variants.is_empty() { |
54a0048b | 2268 | write!(w, "<h2 class='variants'>Variants</h2>\n<table class='variants_table'>")?; |
85aaf69f | 2269 | for variant in &e.variants { |
54a0048b SL |
2270 | write!(w, "<tr><td id='{shortty}.{name}'><code>{name}</code></td><td>", |
2271 | shortty = ItemType::Variant, | |
2272 | name = variant.name.as_ref().unwrap())?; | |
2273 | document(w, cx, variant)?; | |
2274 | ||
2275 | use clean::{Variant, StructVariant}; | |
2276 | if let clean::VariantItem( Variant { kind: StructVariant(ref s) } ) = variant.inner { | |
2277 | let fields = s.fields.iter().filter(|f| { | |
2278 | match f.inner { | |
2279 | clean::StructFieldItem(..) => true, | |
2280 | _ => false, | |
1a4d82fc | 2281 | } |
54a0048b SL |
2282 | }); |
2283 | write!(w, "<h3 class='fields'>Fields</h3>\n | |
2284 | <table>")?; | |
2285 | for field in fields { | |
2286 | write!(w, "<tr><td \ | |
2287 | id='{shortty}.{v}.field.{f}'>\ | |
2288 | <code>{f}</code></td><td>", | |
2289 | shortty = ItemType::Variant, | |
2290 | v = variant.name.as_ref().unwrap(), | |
2291 | f = field.name.as_ref().unwrap())?; | |
2292 | document(w, cx, field)?; | |
2293 | write!(w, "</td></tr>")?; | |
1a4d82fc | 2294 | } |
54a0048b | 2295 | write!(w, "</table>")?; |
1a4d82fc | 2296 | } |
54a0048b SL |
2297 | write!(w, "</td><td>")?; |
2298 | render_stability_since(w, variant, it)?; | |
2299 | write!(w, "</td></tr>")?; | |
1a4d82fc | 2300 | } |
54a0048b | 2301 | write!(w, "</table>")?; |
1a4d82fc | 2302 | } |
54a0048b | 2303 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)?; |
1a4d82fc JJ |
2304 | Ok(()) |
2305 | } | |
2306 | ||
85aaf69f SL |
2307 | fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result { |
2308 | for attr in &it.attrs { | |
2309 | match *attr { | |
2310 | clean::Word(ref s) if *s == "must_use" => { | |
54a0048b | 2311 | write!(w, "#[{}]\n", s)?; |
85aaf69f SL |
2312 | } |
2313 | clean::NameValue(ref k, ref v) if *k == "must_use" => { | |
54a0048b | 2314 | write!(w, "#[{} = \"{}\"]\n", k, v)?; |
85aaf69f SL |
2315 | } |
2316 | _ => () | |
2317 | } | |
2318 | } | |
2319 | Ok(()) | |
2320 | } | |
2321 | ||
1a4d82fc JJ |
2322 | fn render_struct(w: &mut fmt::Formatter, it: &clean::Item, |
2323 | g: Option<&clean::Generics>, | |
2324 | ty: doctree::StructType, | |
2325 | fields: &[clean::Item], | |
2326 | tab: &str, | |
2327 | structhead: bool) -> fmt::Result { | |
54a0048b SL |
2328 | write!(w, "{}{}{}", |
2329 | VisSpace(&it.visibility), | |
2330 | if structhead {"struct "} else {""}, | |
2331 | it.name.as_ref().unwrap())?; | |
2332 | if let Some(g) = g { | |
2333 | write!(w, "{}{}", *g, WhereClause(g))? | |
1a4d82fc JJ |
2334 | } |
2335 | match ty { | |
2336 | doctree::Plain => { | |
54a0048b | 2337 | write!(w, " {{\n{}", tab)?; |
85aaf69f | 2338 | for field in fields { |
54a0048b SL |
2339 | if let clean::StructFieldItem(ref ty) = field.inner { |
2340 | write!(w, " {}{}: {},\n{}", | |
2341 | VisSpace(&field.visibility), | |
2342 | field.name.as_ref().unwrap(), | |
2343 | *ty, | |
2344 | tab)?; | |
2345 | } | |
1a4d82fc JJ |
2346 | } |
2347 | ||
54a0048b SL |
2348 | if it.has_stripped_fields().unwrap() { |
2349 | write!(w, " // some fields omitted\n{}", tab)?; | |
1a4d82fc | 2350 | } |
54a0048b | 2351 | write!(w, "}}")?; |
1a4d82fc JJ |
2352 | } |
2353 | doctree::Tuple | doctree::Newtype => { | |
54a0048b | 2354 | write!(w, "(")?; |
1a4d82fc JJ |
2355 | for (i, field) in fields.iter().enumerate() { |
2356 | if i > 0 { | |
54a0048b | 2357 | write!(w, ", ")?; |
1a4d82fc JJ |
2358 | } |
2359 | match field.inner { | |
54a0048b SL |
2360 | clean::StrippedItem(box clean::StructFieldItem(..)) => { |
2361 | write!(w, "_")? | |
1a4d82fc | 2362 | } |
54a0048b SL |
2363 | clean::StructFieldItem(ref ty) => { |
2364 | write!(w, "{}{}", VisSpace(&field.visibility), *ty)? | |
1a4d82fc JJ |
2365 | } |
2366 | _ => unreachable!() | |
2367 | } | |
2368 | } | |
54a0048b | 2369 | write!(w, ");")?; |
1a4d82fc JJ |
2370 | } |
2371 | doctree::Unit => { | |
54a0048b | 2372 | write!(w, ";")?; |
1a4d82fc JJ |
2373 | } |
2374 | } | |
2375 | Ok(()) | |
2376 | } | |
2377 | ||
9346a6ac | 2378 | #[derive(Copy, Clone)] |
54a0048b | 2379 | enum AssocItemLink<'a> { |
9346a6ac | 2380 | Anchor, |
54a0048b | 2381 | GotoSource(DefId, &'a HashSet<String>), |
9346a6ac AL |
2382 | } |
2383 | ||
d9579d0f AL |
2384 | enum AssocItemRender<'a> { |
2385 | All, | |
2386 | DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type }, | |
2387 | } | |
2388 | ||
2389 | fn render_assoc_items(w: &mut fmt::Formatter, | |
e9174d1e | 2390 | cx: &Context, |
7453a54e | 2391 | containing_item: &clean::Item, |
e9174d1e | 2392 | it: DefId, |
d9579d0f AL |
2393 | what: AssocItemRender) -> fmt::Result { |
2394 | let c = cache(); | |
2395 | let v = match c.impls.get(&it) { | |
2396 | Some(v) => v, | |
9346a6ac AL |
2397 | None => return Ok(()), |
2398 | }; | |
d9579d0f AL |
2399 | let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| { |
2400 | i.impl_.trait_.is_none() | |
2401 | }); | |
9346a6ac | 2402 | if !non_trait.is_empty() { |
d9579d0f AL |
2403 | let render_header = match what { |
2404 | AssocItemRender::All => { | |
54a0048b | 2405 | write!(w, "<h2 id='methods'>Methods</h2>")?; |
d9579d0f AL |
2406 | true |
2407 | } | |
2408 | AssocItemRender::DerefFor { trait_, type_ } => { | |
54a0048b SL |
2409 | write!(w, "<h2 id='deref-methods'>Methods from \ |
2410 | {}<Target={}></h2>", trait_, type_)?; | |
d9579d0f AL |
2411 | false |
2412 | } | |
2413 | }; | |
9346a6ac | 2414 | for i in &non_trait { |
54a0048b SL |
2415 | render_impl(w, cx, i, AssocItemLink::Anchor, render_header, |
2416 | containing_item.stable_since())?; | |
9346a6ac AL |
2417 | } |
2418 | } | |
d9579d0f | 2419 | if let AssocItemRender::DerefFor { .. } = what { |
54a0048b | 2420 | return Ok(()); |
d9579d0f | 2421 | } |
9346a6ac | 2422 | if !traits.is_empty() { |
d9579d0f | 2423 | let deref_impl = traits.iter().find(|t| { |
54a0048b | 2424 | t.impl_.trait_.def_id() == c.deref_trait_did |
d9579d0f AL |
2425 | }); |
2426 | if let Some(impl_) = deref_impl { | |
54a0048b | 2427 | render_deref_methods(w, cx, impl_, containing_item)?; |
d9579d0f | 2428 | } |
54a0048b SL |
2429 | write!(w, "<h2 id='implementations'>Trait \ |
2430 | Implementations</h2>")?; | |
62682a34 | 2431 | let (derived, manual): (Vec<_>, Vec<&Impl>) = traits.iter().partition(|i| { |
d9579d0f AL |
2432 | i.impl_.derived |
2433 | }); | |
9346a6ac AL |
2434 | for i in &manual { |
2435 | let did = i.trait_did().unwrap(); | |
54a0048b SL |
2436 | let assoc_link = AssocItemLink::GotoSource(did, &i.impl_.provided_trait_methods); |
2437 | render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?; | |
9346a6ac AL |
2438 | } |
2439 | if !derived.is_empty() { | |
54a0048b SL |
2440 | write!(w, "<h3 id='derived_implementations'>\ |
2441 | Derived Implementations \ | |
2442 | </h3>")?; | |
9346a6ac AL |
2443 | for i in &derived { |
2444 | let did = i.trait_did().unwrap(); | |
54a0048b SL |
2445 | let assoc_link = AssocItemLink::GotoSource(did, &i.impl_.provided_trait_methods); |
2446 | render_impl(w, cx, i, assoc_link, true, containing_item.stable_since())?; | |
1a4d82fc JJ |
2447 | } |
2448 | } | |
1a4d82fc JJ |
2449 | } |
2450 | Ok(()) | |
2451 | } | |
2452 | ||
7453a54e SL |
2453 | fn render_deref_methods(w: &mut fmt::Formatter, cx: &Context, impl_: &Impl, |
2454 | container_item: &clean::Item) -> fmt::Result { | |
d9579d0f AL |
2455 | let deref_type = impl_.impl_.trait_.as_ref().unwrap(); |
2456 | let target = impl_.impl_.items.iter().filter_map(|item| { | |
2457 | match item.inner { | |
62682a34 | 2458 | clean::TypedefItem(ref t, true) => Some(&t.type_), |
d9579d0f AL |
2459 | _ => None, |
2460 | } | |
62682a34 | 2461 | }).next().expect("Expected associated type binding"); |
d9579d0f | 2462 | let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target }; |
54a0048b SL |
2463 | if let Some(did) = target.def_id() { |
2464 | render_assoc_items(w, cx, container_item, did, what) | |
2465 | } else { | |
2466 | if let Some(prim) = target.primitive_type() { | |
2467 | if let Some(c) = cache().primitive_locations.get(&prim) { | |
2468 | let did = DefId { krate: *c, index: prim.to_def_index() }; | |
2469 | render_assoc_items(w, cx, container_item, did, what)?; | |
d9579d0f | 2470 | } |
d9579d0f | 2471 | } |
54a0048b | 2472 | Ok(()) |
1a4d82fc | 2473 | } |
d9579d0f AL |
2474 | } |
2475 | ||
62682a34 SL |
2476 | // Render_header is false when we are rendering a `Deref` impl and true |
2477 | // otherwise. If render_header is false, we will avoid rendering static | |
2478 | // methods, since they are not accessible for the type implementing `Deref` | |
e9174d1e | 2479 | fn render_impl(w: &mut fmt::Formatter, cx: &Context, i: &Impl, link: AssocItemLink, |
7453a54e | 2480 | render_header: bool, outer_version: Option<&str>) -> fmt::Result { |
d9579d0f | 2481 | if render_header { |
54a0048b | 2482 | write!(w, "<h3 class='impl'><code>{}</code>", i.impl_)?; |
7453a54e | 2483 | let since = i.stability.as_ref().map(|s| &s.since[..]); |
54a0048b SL |
2484 | render_stability_since_raw(w, since, outer_version)?; |
2485 | write!(w, "</h3>")?; | |
d9579d0f | 2486 | if let Some(ref dox) = i.dox { |
54a0048b | 2487 | write!(w, "<div class='docblock'>{}</div>", Markdown(dox))?; |
d9579d0f | 2488 | } |
1a4d82fc JJ |
2489 | } |
2490 | ||
e9174d1e | 2491 | fn doctraititem(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item, |
7453a54e SL |
2492 | link: AssocItemLink, render_static: bool, |
2493 | outer_version: Option<&str>) -> fmt::Result { | |
54a0048b | 2494 | let shortty = shortty(item); |
92a42be0 | 2495 | let name = item.name.as_ref().unwrap(); |
54a0048b SL |
2496 | |
2497 | let is_static = match item.inner { | |
2498 | clean::MethodItem(ref method) => method.self_ == SelfTy::SelfStatic, | |
2499 | clean::TyMethodItem(ref method) => method.self_ == SelfTy::SelfStatic, | |
2500 | _ => false | |
2501 | }; | |
2502 | ||
1a4d82fc JJ |
2503 | match item.inner { |
2504 | clean::MethodItem(..) | clean::TyMethodItem(..) => { | |
62682a34 | 2505 | // Only render when the method is not static or we allow static methods |
54a0048b SL |
2506 | if !is_static || render_static { |
2507 | let id = derive_id(format!("{}.{}", shortty, name)); | |
2508 | write!(w, "<h4 id='{}' class='{}'>", id, shortty)?; | |
2509 | render_stability_since_raw(w, item.stable_since(), outer_version)?; | |
2510 | write!(w, "<code>")?; | |
2511 | render_assoc_item(w, item, link)?; | |
2512 | write!(w, "</code></h4>\n")?; | |
62682a34 | 2513 | } |
1a4d82fc | 2514 | } |
62682a34 | 2515 | clean::TypedefItem(ref tydef, _) => { |
54a0048b SL |
2516 | let id = derive_id(format!("{}.{}", ItemType::AssociatedType, name)); |
2517 | write!(w, "<h4 id='{}' class='{}'><code>", id, shortty)?; | |
2518 | assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link)?; | |
2519 | write!(w, "</code></h4>\n")?; | |
1a4d82fc | 2520 | } |
d9579d0f | 2521 | clean::AssociatedConstItem(ref ty, ref default) => { |
54a0048b SL |
2522 | let id = derive_id(format!("{}.{}", shortty, name)); |
2523 | write!(w, "<h4 id='{}' class='{}'><code>", id, shortty)?; | |
2524 | assoc_const(w, item, ty, default.as_ref(), link)?; | |
2525 | write!(w, "</code></h4>\n")?; | |
d9579d0f AL |
2526 | } |
2527 | clean::ConstantItem(ref c) => { | |
54a0048b SL |
2528 | let id = derive_id(format!("{}.{}", shortty, name)); |
2529 | write!(w, "<h4 id='{}' class='{}'><code>", id, shortty)?; | |
2530 | assoc_const(w, item, &c.type_, Some(&c.expr), link)?; | |
2531 | write!(w, "</code></h4>\n")?; | |
d9579d0f | 2532 | } |
c34b1796 | 2533 | clean::AssociatedTypeItem(ref bounds, ref default) => { |
54a0048b SL |
2534 | let id = derive_id(format!("{}.{}", shortty, name)); |
2535 | write!(w, "<h4 id='{}' class='{}'><code>", id, shortty)?; | |
2536 | assoc_type(w, item, bounds, default.as_ref(), link)?; | |
2537 | write!(w, "</code></h4>\n")?; | |
1a4d82fc | 2538 | } |
54a0048b | 2539 | clean::StrippedItem(..) => return Ok(()), |
1a4d82fc JJ |
2540 | _ => panic!("can't make docs for trait item with name {:?}", item.name) |
2541 | } | |
62682a34 | 2542 | |
54a0048b SL |
2543 | match link { |
2544 | AssocItemLink::Anchor if !is_static || render_static => { | |
e9174d1e | 2545 | document(w, cx, item) |
54a0048b SL |
2546 | }, |
2547 | _ => Ok(()), | |
1a4d82fc JJ |
2548 | } |
2549 | } | |
2550 | ||
54a0048b | 2551 | write!(w, "<div class='impl-items'>")?; |
62682a34 | 2552 | for trait_item in &i.impl_.items { |
54a0048b | 2553 | doctraititem(w, cx, trait_item, link, render_header, outer_version)?; |
1a4d82fc JJ |
2554 | } |
2555 | ||
d9579d0f | 2556 | fn render_default_items(w: &mut fmt::Formatter, |
e9174d1e | 2557 | cx: &Context, |
d9579d0f | 2558 | t: &clean::Trait, |
54a0048b SL |
2559 | i: &clean::Impl, |
2560 | render_static: bool, | |
2561 | outer_version: Option<&str>) -> fmt::Result { | |
85aaf69f | 2562 | for trait_item in &t.items { |
c34b1796 | 2563 | let n = trait_item.name.clone(); |
54a0048b SL |
2564 | if i.items.iter().find(|m| m.name == n).is_some() { |
2565 | continue; | |
1a4d82fc | 2566 | } |
54a0048b SL |
2567 | let did = i.trait_.as_ref().unwrap().def_id().unwrap(); |
2568 | let assoc_link = AssocItemLink::GotoSource(did, &i.provided_trait_methods); | |
1a4d82fc | 2569 | |
54a0048b SL |
2570 | doctraititem(w, cx, trait_item, assoc_link, render_static, |
2571 | outer_version)?; | |
1a4d82fc JJ |
2572 | } |
2573 | Ok(()) | |
2574 | } | |
2575 | ||
2576 | // If we've implemented a trait, then also emit documentation for all | |
54a0048b SL |
2577 | // default items which weren't overridden in the implementation block. |
2578 | if let Some(did) = i.trait_did() { | |
9346a6ac | 2579 | if let Some(t) = cache().traits.get(&did) { |
54a0048b | 2580 | render_default_items(w, cx, t, &i.impl_, render_header, outer_version)?; |
1a4d82fc | 2581 | } |
1a4d82fc | 2582 | } |
54a0048b | 2583 | write!(w, "</div>")?; |
1a4d82fc JJ |
2584 | Ok(()) |
2585 | } | |
2586 | ||
e9174d1e | 2587 | fn item_typedef(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2588 | t: &clean::Typedef) -> fmt::Result { |
54a0048b SL |
2589 | write!(w, "<pre class='rust typedef'>type {}{}{where_clause} = {type_};</pre>", |
2590 | it.name.as_ref().unwrap(), | |
2591 | t.generics, | |
2592 | where_clause = WhereClause(&t.generics), | |
2593 | type_ = t.type_)?; | |
1a4d82fc | 2594 | |
e9174d1e | 2595 | document(w, cx, it) |
1a4d82fc JJ |
2596 | } |
2597 | ||
85aaf69f | 2598 | impl<'a> fmt::Display for Sidebar<'a> { |
1a4d82fc JJ |
2599 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
2600 | let cx = self.cx; | |
2601 | let it = self.item; | |
c34b1796 AL |
2602 | let parentlen = cx.current.len() - if it.is_mod() {1} else {0}; |
2603 | ||
2604 | // the sidebar is designed to display sibling functions, modules and | |
2605 | // other miscellaneous informations. since there are lots of sibling | |
2606 | // items (and that causes quadratic growth in large modules), | |
2607 | // we refactor common parts into a shared JavaScript file per module. | |
2608 | // still, we don't move everything into JS because we want to preserve | |
2609 | // as much HTML as possible in order to allow non-JS-enabled browsers | |
2610 | // to navigate the documentation (though slightly inefficiently). | |
2611 | ||
54a0048b | 2612 | write!(fmt, "<p class='location'>")?; |
c34b1796 | 2613 | for (i, name) in cx.current.iter().take(parentlen).enumerate() { |
1a4d82fc | 2614 | if i > 0 { |
54a0048b | 2615 | write!(fmt, "::<wbr>")?; |
1a4d82fc | 2616 | } |
54a0048b SL |
2617 | write!(fmt, "<a href='{}index.html'>{}</a>", |
2618 | &cx.root_path[..(cx.current.len() - i - 1) * 3], | |
2619 | *name)?; | |
1a4d82fc | 2620 | } |
54a0048b | 2621 | write!(fmt, "</p>")?; |
1a4d82fc | 2622 | |
c34b1796 | 2623 | // sidebar refers to the enclosing module, not this module |
54a0048b SL |
2624 | let relpath = if it.is_mod() { "../" } else { "" }; |
2625 | write!(fmt, | |
2626 | "<script>window.sidebarCurrent = {{\ | |
2627 | name: '{name}', \ | |
2628 | ty: '{ty}', \ | |
2629 | relpath: '{path}'\ | |
2630 | }};</script>", | |
2631 | name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""), | |
2632 | ty = shortty(it).to_static_str(), | |
2633 | path = relpath)?; | |
c34b1796 AL |
2634 | if parentlen == 0 { |
2635 | // there is no sidebar-items.js beyond the crate root path | |
2636 | // FIXME maybe dynamic crate loading can be merged here | |
2637 | } else { | |
54a0048b SL |
2638 | write!(fmt, "<script defer src=\"{path}sidebar-items.js\"></script>", |
2639 | path = relpath)?; | |
1a4d82fc JJ |
2640 | } |
2641 | ||
1a4d82fc JJ |
2642 | Ok(()) |
2643 | } | |
2644 | } | |
2645 | ||
85aaf69f | 2646 | impl<'a> fmt::Display for Source<'a> { |
1a4d82fc JJ |
2647 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
2648 | let Source(s) = *self; | |
2649 | let lines = s.lines().count(); | |
2650 | let mut cols = 0; | |
2651 | let mut tmp = lines; | |
2652 | while tmp > 0 { | |
2653 | cols += 1; | |
2654 | tmp /= 10; | |
2655 | } | |
54a0048b | 2656 | write!(fmt, "<pre class=\"line-numbers\">")?; |
85aaf69f | 2657 | for i in 1..lines + 1 { |
54a0048b | 2658 | write!(fmt, "<span id=\"{0}\">{0:1$}</span>\n", i, cols)?; |
1a4d82fc | 2659 | } |
54a0048b SL |
2660 | write!(fmt, "</pre>")?; |
2661 | write!(fmt, "{}", highlight::render_with_highlighting(s, None, None))?; | |
1a4d82fc JJ |
2662 | Ok(()) |
2663 | } | |
2664 | } | |
2665 | ||
e9174d1e | 2666 | fn item_macro(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, |
1a4d82fc | 2667 | t: &clean::Macro) -> fmt::Result { |
54a0048b SL |
2668 | w.write_str(&highlight::render_with_highlighting(&t.source, |
2669 | Some("macro"), | |
2670 | None))?; | |
2671 | render_stability_since_raw(w, it.stable_since(), None)?; | |
e9174d1e | 2672 | document(w, cx, it) |
1a4d82fc JJ |
2673 | } |
2674 | ||
e9174d1e | 2675 | fn item_primitive(w: &mut fmt::Formatter, cx: &Context, |
1a4d82fc JJ |
2676 | it: &clean::Item, |
2677 | _p: &clean::PrimitiveType) -> fmt::Result { | |
54a0048b | 2678 | document(w, cx, it)?; |
7453a54e | 2679 | render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All) |
1a4d82fc JJ |
2680 | } |
2681 | ||
54a0048b | 2682 | const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang"; |
1a4d82fc JJ |
2683 | |
2684 | fn make_item_keywords(it: &clean::Item) -> String { | |
54a0048b | 2685 | format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap()) |
1a4d82fc JJ |
2686 | } |
2687 | ||
c34b1796 AL |
2688 | fn get_index_search_type(item: &clean::Item, |
2689 | parent: Option<String>) -> Option<IndexItemFunctionType> { | |
7453a54e SL |
2690 | let (decl, selfty) = match item.inner { |
2691 | clean::FunctionItem(ref f) => (&f.decl, None), | |
2692 | clean::MethodItem(ref m) => (&m.decl, Some(&m.self_)), | |
2693 | clean::TyMethodItem(ref m) => (&m.decl, Some(&m.self_)), | |
c34b1796 AL |
2694 | _ => return None |
2695 | }; | |
2696 | ||
2697 | let mut inputs = Vec::new(); | |
2698 | ||
2699 | // Consider `self` an argument as well. | |
7453a54e SL |
2700 | match parent.and_then(|p| selfty.map(|s| (p, s)) ) { |
2701 | Some((_, &clean::SelfStatic)) | None => (), | |
2702 | Some((name, _)) => inputs.push(Type { name: Some(name.to_ascii_lowercase()) }), | |
c34b1796 AL |
2703 | } |
2704 | ||
2705 | inputs.extend(&mut decl.inputs.values.iter().map(|arg| { | |
2706 | get_index_type(&arg.type_) | |
2707 | })); | |
2708 | ||
2709 | let output = match decl.output { | |
2710 | clean::FunctionRetTy::Return(ref return_type) => Some(get_index_type(return_type)), | |
2711 | _ => None | |
2712 | }; | |
2713 | ||
2714 | Some(IndexItemFunctionType { inputs: inputs, output: output }) | |
2715 | } | |
2716 | ||
2717 | fn get_index_type(clean_type: &clean::Type) -> Type { | |
c1a9b12d | 2718 | Type { name: get_index_type_name(clean_type).map(|s| s.to_ascii_lowercase()) } |
c34b1796 AL |
2719 | } |
2720 | ||
2721 | fn get_index_type_name(clean_type: &clean::Type) -> Option<String> { | |
2722 | match *clean_type { | |
2723 | clean::ResolvedPath { ref path, .. } => { | |
2724 | let segments = &path.segments; | |
2725 | Some(segments[segments.len() - 1].name.clone()) | |
2726 | }, | |
2727 | clean::Generic(ref s) => Some(s.clone()), | |
2728 | clean::Primitive(ref p) => Some(format!("{:?}", p)), | |
2729 | clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_), | |
2730 | // FIXME: add all from clean::Type. | |
2731 | _ => None | |
2732 | } | |
2733 | } | |
2734 | ||
1a4d82fc JJ |
2735 | pub fn cache() -> Arc<Cache> { |
2736 | CACHE_KEY.with(|c| c.borrow().clone()) | |
2737 | } | |
92a42be0 SL |
2738 | |
2739 | #[cfg(test)] | |
2740 | #[test] | |
2741 | fn test_unique_id() { | |
2742 | let input = ["foo", "examples", "examples", "method.into_iter","examples", | |
2743 | "method.into_iter", "foo", "main", "search", "methods", | |
2744 | "examples", "method.into_iter", "assoc_type.Item", "assoc_type.Item"]; | |
2745 | let expected = ["foo", "examples", "examples-1", "method.into_iter", "examples-2", | |
2746 | "method.into_iter-1", "foo-1", "main-1", "search-1", "methods-1", | |
2747 | "examples-3", "method.into_iter-2", "assoc_type.Item", "assoc_type.Item-1"]; | |
2748 | ||
2749 | let test = || { | |
2750 | let actual: Vec<String> = input.iter().map(|s| derive_id(s.to_string())).collect(); | |
2751 | assert_eq!(&actual[..], expected); | |
2752 | }; | |
2753 | test(); | |
54a0048b | 2754 | reset_ids(true); |
92a42be0 SL |
2755 | test(); |
2756 | } |