]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/html/render.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / librustdoc / html / render.rs
CommitLineData
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.
35pub use self::ExternalLocation::*;
36
c1a9b12d 37use std::ascii::AsciiExt;
1a4d82fc 38use std::cell::RefCell;
85aaf69f 39use std::cmp::Ordering;
d9579d0f 40use std::collections::{BTreeMap, HashMap, HashSet};
1a4d82fc 41use std::default::Default;
b039eaaf
SL
42use std::error;
43use std::fmt::{self, Display, Formatter};
c34b1796
AL
44use std::fs::{self, File};
45use std::io::prelude::*;
46use std::io::{self, BufWriter, BufReader};
1a4d82fc 47use std::iter::repeat;
9346a6ac 48use std::mem;
7453a54e 49use std::path::{PathBuf, Path, Component};
1a4d82fc
JJ
50use std::str;
51use std::sync::Arc;
52
53use externalfiles::ExternalHtml;
54
7453a54e 55use serialize::json::{ToJson, Json, as_json};
e9174d1e 56use syntax::{abi, ast};
7453a54e 57use syntax::feature_gate::UnstableFeatures;
92a42be0 58use rustc::middle::cstore::LOCAL_CRATE;
54a0048b 59use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
92a42be0 60use rustc::middle::privacy::AccessLevels;
b039eaaf 61use rustc::middle::stability;
7453a54e 62use rustc::session::config::get_unstable_features_setting;
54a0048b 63use rustc::hir;
1a4d82fc 64
54a0048b 65use clean::{self, SelfTy, Attributes, GetDefId};
1a4d82fc
JJ
66use doctree;
67use fold::DocFolder;
d9579d0f 68use html::escape::Escape;
62682a34 69use html::format::{ConstnessSpace};
d9579d0f
AL
70use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
71use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
1a4d82fc 72use html::item_type::ItemType;
62682a34
SL
73use html::markdown::{self, Markdown};
74use html::{highlight, layout};
1a4d82fc 75
85aaf69f 76/// A pair of name and its optional document.
c34b1796 77pub 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)]
87pub 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
104pub 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.
128pub 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.
138pub 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)]
146pub struct Impl {
147 pub impl_: clean::Impl,
148 pub dox: Option<String>,
149 pub stability: Option<clean::Stability>,
150}
151
9346a6ac 152impl 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)]
159pub struct Error {
160 file: PathBuf,
161 error: io::Error,
162}
163
164impl error::Error for Error {
165 fn description(&self) -> &str {
166 self.error.description()
167 }
168}
169
170impl Display for Error {
171 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
172 write!(f, "\"{}\": {}", self.file.display(), self.error)
173 }
174}
175
176impl 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
185macro_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)]
204pub 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
268struct 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.
277struct 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
283struct Item<'a> {
284 cx: &'a Context,
285 item: &'a clean::Item,
286}
287
288struct 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.
292struct 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
302impl 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.
319struct Type {
320 name: Option<String>,
321}
322
7453a54e
SL
323impl 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.
337struct IndexItemFunctionType {
338 inputs: Vec<Type>,
339 output: Option<Type>
340}
341
7453a54e
SL
342impl 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
358thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default());
359thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> =
360 RefCell::new(Vec::new()));
92a42be0
SL
361thread_local!(static USED_ID_MAP: RefCell<HashMap<String, usize>> =
362 RefCell::new(init_ids()));
363
364fn 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
387pub 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
397pub 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`
414pub 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 559fn 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
633fn 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 786fn 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
802fn 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 808fn 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.
817fn 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 828fn 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.
851fn 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
869impl<'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
902impl<'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
960impl 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
1205impl<'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
1213impl 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
1418impl<'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 1498impl<'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'>&#x2212;</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
1580fn 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
1590fn 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 1597fn 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
1609fn 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
1614fn 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
1624fn 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 1764fn 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(&note));
1814 Some(format!("<em class='stab deprecated'>{}</em>", text))
54a0048b
SL
1815 })
1816 })
d9579d0f
AL
1817}
1818
1a4d82fc
JJ
1819struct Initializer<'a>(&'a str);
1820
85aaf69f 1821impl<'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 1830fn 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 1841fn 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 1853fn 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
1874fn 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
2033fn 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
2051fn 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 2067fn 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
2083fn 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
2095fn 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
2101fn 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 2172fn 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 2212fn 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
2307fn 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
2322fn 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 2379enum AssocItemLink<'a> {
9346a6ac 2380 Anchor,
54a0048b 2381 GotoSource(DefId, &'a HashSet<String>),
9346a6ac
AL
2382}
2383
d9579d0f
AL
2384enum AssocItemRender<'a> {
2385 All,
2386 DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type },
2387}
2388
2389fn 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 {}&lt;Target={}&gt;</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
2453fn 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 2479fn 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 2587fn 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 2598impl<'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 2646impl<'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 2666fn 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 2675fn 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 2682const BASIC_KEYWORDS: &'static str = "rust, rustlang, rust-lang";
1a4d82fc
JJ
2683
2684fn make_item_keywords(it: &clean::Item) -> String {
54a0048b 2685 format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap())
1a4d82fc
JJ
2686}
2687
c34b1796
AL
2688fn 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
2717fn 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
2721fn 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
2735pub fn cache() -> Arc<Cache> {
2736 CACHE_KEY.with(|c| c.borrow().clone())
2737}
92a42be0
SL
2738
2739#[cfg(test)]
2740#[test]
2741fn 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}