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