2015-07-01 23:52:40 -04:00
|
|
|
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
|
2013-09-18 22:18:38 -07:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
//! Rustdoc's HTML Rendering module
|
|
|
|
//!
|
|
|
|
//! This modules contains the bulk of the logic necessary for rendering a
|
|
|
|
//! rustdoc `clean::Crate` instance to a set of static HTML pages. This
|
|
|
|
//! rendering process is largely driven by the `format!` syntax extension to
|
|
|
|
//! perform all I/O into files and streams.
|
|
|
|
//!
|
|
|
|
//! The rendering process is largely driven by the `Context` and `Cache`
|
|
|
|
//! structures. The cache is pre-populated by crawling the crate in question,
|
2015-05-09 00:12:29 +09:00
|
|
|
//! and then it is shared among the various rendering threads. The cache is meant
|
2013-10-03 10:24:40 -07:00
|
|
|
//! to be a fairly large structure not implementing `Clone` (because it's shared
|
2015-05-09 00:12:29 +09:00
|
|
|
//! among threads). The context, however, should be a lightweight structure. This
|
|
|
|
//! is cloned per-thread and contains information about what is currently being
|
2013-10-03 10:24:40 -07:00
|
|
|
//! rendered.
|
|
|
|
//!
|
|
|
|
//! In order to speed up rendering (mostly because of markdown rendering), the
|
|
|
|
//! rendering process has been parallelized. This parallelization is only
|
|
|
|
//! exposed through the `crate` method on the context, and then also from the
|
|
|
|
//! fact that the shared cache is stored in TLS (and must be accessed as such).
|
|
|
|
//!
|
|
|
|
//! In addition to rendering the crate itself, this module is also responsible
|
|
|
|
//! for creating the corresponding search index and source file renderings.
|
2015-05-09 00:12:29 +09:00
|
|
|
//! These threads are not parallelized (they haven't been a bottleneck yet), and
|
2013-10-03 10:24:40 -07:00
|
|
|
//! both occur before the crate is rendered.
|
2014-11-06 00:05:53 -08:00
|
|
|
pub use self::ExternalLocation::*;
|
2013-10-03 10:24:40 -07:00
|
|
|
|
2015-07-08 08:33:13 -07:00
|
|
|
use std::ascii::AsciiExt;
|
2014-11-14 14:20:57 -08:00
|
|
|
use std::cell::RefCell;
|
2014-12-26 10:55:16 +02:00
|
|
|
use std::cmp::Ordering;
|
2015-04-16 12:14:45 +02:00
|
|
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
2014-11-14 14:20:57 -08:00
|
|
|
use std::default::Default;
|
2013-09-18 22:18:38 -07:00
|
|
|
use std::fmt;
|
2015-02-26 21:00:43 -08:00
|
|
|
use std::fs::{self, File};
|
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::io::{self, BufWriter, BufReader};
|
2014-12-10 19:46:38 -08:00
|
|
|
use std::iter::repeat;
|
2015-04-06 15:10:55 -07:00
|
|
|
use std::mem;
|
2015-02-26 21:00:43 -08:00
|
|
|
use std::path::{PathBuf, Path};
|
2014-04-02 16:54:22 -07:00
|
|
|
use std::str;
|
2014-06-07 11:13:26 -07:00
|
|
|
use std::sync::Arc;
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-06-28 03:35:25 -07:00
|
|
|
use externalfiles::ExternalHtml;
|
|
|
|
|
2015-04-26 21:03:38 +02:00
|
|
|
use serialize::json::{self, ToJson};
|
|
|
|
use syntax::{abi, ast, ast_util, attr};
|
2014-02-28 14:34:26 -08:00
|
|
|
use rustc::util::nodemap::NodeSet;
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2015-04-26 21:03:38 +02:00
|
|
|
use clean::{self, SelfTy};
|
2013-09-18 22:18:38 -07:00
|
|
|
use doctree;
|
|
|
|
use fold::DocFolder;
|
2015-04-13 11:55:00 -07:00
|
|
|
use html::escape::Escape;
|
2015-02-25 22:05:07 +02:00
|
|
|
use html::format::{ConstnessSpace};
|
2015-04-13 11:55:00 -07:00
|
|
|
use html::format::{TyParamBounds, WhereClause, href, AbiSpace};
|
|
|
|
use html::format::{VisSpace, Method, UnsafetySpace, MutableSpace};
|
2014-12-03 23:57:45 +09:00
|
|
|
use html::item_type::ItemType;
|
2015-04-26 21:03:38 +02:00
|
|
|
use html::markdown::{self, Markdown};
|
|
|
|
use html::{highlight, layout};
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-12-23 09:58:38 +08:00
|
|
|
/// A pair of name and its optional document.
|
2015-03-05 16:35:43 +09:00
|
|
|
pub type NameDoc = (String, Option<String>);
|
2014-12-23 09:58:38 +08:00
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Major driving force in all rustdoc rendering. This contains information
|
|
|
|
/// about where in the tree-like hierarchy rendering is occurring and controls
|
|
|
|
/// how the current page is being rendered.
|
|
|
|
///
|
|
|
|
/// It is intended that this context is a lightweight object which can be fairly
|
|
|
|
/// easily cloned because it is cloned per work-job (about once per item in the
|
|
|
|
/// rustdoc tree).
|
2015-01-03 22:54:18 -05:00
|
|
|
#[derive(Clone)]
|
2013-09-18 22:18:38 -07:00
|
|
|
pub struct Context {
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Current hierarchy of components leading down to what's currently being
|
|
|
|
/// rendered
|
2014-05-29 13:50:47 -07:00
|
|
|
pub current: Vec<String>,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// String representation of how to get back to the root path of the 'doc/'
|
|
|
|
/// folder in terms of a relative URL.
|
2014-05-22 16:57:53 -07:00
|
|
|
pub root_path: String,
|
2014-12-01 20:46:51 +09:00
|
|
|
/// The path to the crate root source minus the file name.
|
|
|
|
/// Used for simplifying paths to the highlighted source code files.
|
2015-02-26 21:00:43 -08:00
|
|
|
pub src_root: PathBuf,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// The current destination folder of where HTML artifacts should be placed.
|
|
|
|
/// This changes as the context descends into the module hierarchy.
|
2015-02-26 21:00:43 -08:00
|
|
|
pub dst: PathBuf,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This describes the layout of each page, and is not modified after
|
2014-06-28 03:35:25 -07:00
|
|
|
/// creation of the context (contains info like the favicon and added html).
|
2014-03-28 10:27:24 -07:00
|
|
|
pub layout: layout::Layout,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This flag indicates whether [src] links should be generated or not. If
|
|
|
|
/// the source files are present in the html rendering, then this will be
|
|
|
|
/// `true`.
|
2014-03-28 10:27:24 -07:00
|
|
|
pub include_sources: bool,
|
2014-05-29 13:50:47 -07:00
|
|
|
/// A flag, which when turned off, will render pages which redirect to the
|
|
|
|
/// real location of an item. This is used to allow external links to
|
|
|
|
/// publicly reused items to redirect to the right location.
|
|
|
|
pub render_redirect_pages: bool,
|
2014-11-09 19:09:17 -05:00
|
|
|
/// All the passes that were run on this crate.
|
|
|
|
pub passes: HashSet<String>,
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Indicates where an external crate can be found.
|
2013-10-02 15:39:32 -07:00
|
|
|
pub enum ExternalLocation {
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Remote URL root of the external crate
|
2014-05-22 16:57:53 -07:00
|
|
|
Remote(String),
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This external crate can be found in the local doc/ folder
|
|
|
|
Local,
|
|
|
|
/// The external crate could not be found.
|
|
|
|
Unknown,
|
2013-10-02 15:39:32 -07:00
|
|
|
}
|
|
|
|
|
2014-05-27 17:52:40 -07:00
|
|
|
/// Metadata about an implementor of a trait.
|
|
|
|
pub struct Implementor {
|
2014-11-10 15:33:21 -08:00
|
|
|
pub def_id: ast::DefId,
|
|
|
|
pub stability: Option<clean::Stability>,
|
2015-07-18 02:02:57 -04:00
|
|
|
pub impl_: clean::Impl,
|
2014-06-26 11:37:39 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Metadata about implementations for a type.
|
2015-01-03 22:54:18 -05:00
|
|
|
#[derive(Clone)]
|
2014-06-26 11:37:39 -07:00
|
|
|
pub struct Impl {
|
2014-11-10 15:33:21 -08:00
|
|
|
pub impl_: clean::Impl,
|
|
|
|
pub dox: Option<String>,
|
|
|
|
pub stability: Option<clean::Stability>,
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-04-06 17:56:35 -07:00
|
|
|
impl Impl {
|
|
|
|
fn trait_did(&self) -> Option<ast::DefId> {
|
|
|
|
self.impl_.trait_.as_ref().and_then(|tr| {
|
|
|
|
if let clean::ResolvedPath { did, .. } = *tr {Some(did)} else {None}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This cache is used to store information about the `clean::Crate` being
|
|
|
|
/// rendered in order to provide more useful documentation. This contains
|
|
|
|
/// information like all implementors of a trait, all traits a type implements,
|
|
|
|
/// documentation for all known traits, etc.
|
|
|
|
///
|
|
|
|
/// This structure purposefully does not implement `Clone` because it's intended
|
|
|
|
/// to be a fairly large and expensive structure to clone. Instead this adheres
|
2014-03-22 14:42:32 +01:00
|
|
|
/// to `Send` so it may be stored in a `Arc` instance and shared among the various
|
2015-05-09 00:12:29 +09:00
|
|
|
/// rendering threads.
|
2015-01-03 22:54:18 -05:00
|
|
|
#[derive(Default)]
|
2013-10-05 14:44:37 -07:00
|
|
|
pub struct Cache {
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Mapping of typaram ids to the name of the type parameter. This is used
|
|
|
|
/// when pretty-printing a type (so pretty printing doesn't have to
|
|
|
|
/// painfully maintain a context like this)
|
2014-05-22 16:57:53 -07:00
|
|
|
pub typarams: HashMap<ast::DefId, String>,
|
2013-10-03 10:24:40 -07:00
|
|
|
|
|
|
|
/// Maps a type id to all known implementations for that type. This is only
|
|
|
|
/// recognized for intra-crate `ResolvedPath` types, and is used to print
|
|
|
|
/// out extra documentation on the page of an enum/struct.
|
|
|
|
///
|
|
|
|
/// The values of the map are a list of implementations and documentation
|
|
|
|
/// found on that implementation.
|
2014-06-26 11:37:39 -07:00
|
|
|
pub impls: HashMap<ast::DefId, Vec<Impl>>,
|
2013-10-03 10:24:40 -07:00
|
|
|
|
|
|
|
/// Maintains a mapping of local crate node ids to the fully qualified name
|
|
|
|
/// and "short type description" of that node. This is used when generating
|
|
|
|
/// URLs when a type is being linked to. External paths are not located in
|
|
|
|
/// this map because the `External` type itself has all the information
|
|
|
|
/// necessary.
|
2014-05-22 16:57:53 -07:00
|
|
|
pub paths: HashMap<ast::DefId, (Vec<String>, ItemType)>,
|
2013-10-03 10:24:40 -07:00
|
|
|
|
2014-05-23 20:17:27 -07:00
|
|
|
/// Similar to `paths`, but only holds external paths. This is only used for
|
|
|
|
/// generating explicit hyperlinks to other crates.
|
2014-05-24 11:56:38 -07:00
|
|
|
pub external_paths: HashMap<ast::DefId, Vec<String>>,
|
2014-05-23 20:17:27 -07:00
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This map contains information about all known traits of this crate.
|
|
|
|
/// Implementations of a crate should inherit the documentation of the
|
2013-10-21 11:33:04 -07:00
|
|
|
/// parent trait if no extra documentation is specified, and default methods
|
|
|
|
/// should show up in documentation about trait implementations.
|
2014-05-03 02:08:58 -07:00
|
|
|
pub traits: HashMap<ast::DefId, clean::Trait>,
|
2013-10-03 10:24:40 -07:00
|
|
|
|
|
|
|
/// When rendering traits, it's often useful to be able to list all
|
|
|
|
/// implementors of the trait, and this mapping is exactly, that: a mapping
|
|
|
|
/// of trait ids to the list of known implementors of the trait
|
2014-05-03 02:08:58 -07:00
|
|
|
pub implementors: HashMap<ast::DefId, Vec<Implementor>>,
|
2013-10-03 10:24:40 -07:00
|
|
|
|
|
|
|
/// Cache of where external crate documentation can be found.
|
2015-04-13 15:25:40 -07:00
|
|
|
pub extern_locations: HashMap<ast::CrateNum, (String, ExternalLocation)>,
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-05-28 19:53:37 -07:00
|
|
|
/// Cache of where documentation for primitives can be found.
|
2014-09-11 17:07:49 +12:00
|
|
|
pub primitive_locations: HashMap<clean::PrimitiveType, ast::CrateNum>,
|
2014-05-28 19:53:37 -07:00
|
|
|
|
2014-05-26 21:51:59 -07:00
|
|
|
/// Set of definitions which have been inlined from external crates.
|
|
|
|
pub inlined: HashSet<ast::DefId>,
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
// Private fields only used when initially crawling a crate to build a cache
|
|
|
|
|
2014-05-22 22:00:18 -07:00
|
|
|
stack: Vec<String>,
|
|
|
|
parent_stack: Vec<ast::DefId>,
|
|
|
|
search_index: Vec<IndexItem>,
|
2014-03-28 10:27:24 -07:00
|
|
|
privmod: bool,
|
2014-11-09 19:09:17 -05:00
|
|
|
remove_priv: bool,
|
2014-03-28 10:27:24 -07:00
|
|
|
public_items: NodeSet,
|
2015-04-13 16:23:32 -07:00
|
|
|
deref_trait_did: Option<ast::DefId>,
|
2014-03-07 17:26:06 +08:00
|
|
|
|
|
|
|
// In rare case where a structure is defined in one module but implemented
|
|
|
|
// in another, if the implementing module is parsed before defining module,
|
|
|
|
// then the fully qualified name of the structure isn't presented in `paths`
|
|
|
|
// yet when its implementation methods are being indexed. Caches such methods
|
|
|
|
// and their parent id here and indexes them at the end of crate parsing.
|
2014-03-28 10:27:24 -07:00
|
|
|
orphan_methods: Vec<(ast::NodeId, clean::Item)>,
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Helper struct to render all source code to HTML pages
|
2013-12-09 23:16:18 -08:00
|
|
|
struct SourceCollector<'a> {
|
|
|
|
cx: &'a mut Context,
|
2013-10-03 10:24:40 -07:00
|
|
|
|
|
|
|
/// Processed source-file paths
|
2014-05-22 16:57:53 -07:00
|
|
|
seen: HashSet<String>,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Root destination to place all HTML output into
|
2015-02-26 21:00:43 -08:00
|
|
|
dst: PathBuf,
|
2013-09-27 15:12:23 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Wrapper struct to render the source code of a file. This will do things like
|
|
|
|
/// adding line numbers to the left-hand side.
|
2013-12-09 23:16:18 -08:00
|
|
|
struct Source<'a>(&'a str);
|
2013-10-03 10:24:40 -07:00
|
|
|
|
|
|
|
// Helper structs for rendering items/sidebars and carrying along contextual
|
|
|
|
// information
|
|
|
|
|
2015-03-30 09:40:52 -04:00
|
|
|
#[derive(Copy, Clone)]
|
librustc: Make `Copy` opt-in.
This change makes the compiler no longer infer whether types (structures
and enumerations) implement the `Copy` trait (and thus are implicitly
copyable). Rather, you must implement `Copy` yourself via `impl Copy for
MyType {}`.
A new warning has been added, `missing_copy_implementations`, to warn
you if a non-generic public type has been added that could have
implemented `Copy` but didn't.
For convenience, you may *temporarily* opt out of this behavior by using
`#![feature(opt_out_copy)]`. Note though that this feature gate will never be
accepted and will be removed by the time that 1.0 is released, so you should
transition your code away from using it.
This breaks code like:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
Change this code to:
#[deriving(Show)]
struct Point2D {
x: int,
y: int,
}
impl Copy for Point2D {}
fn main() {
let mypoint = Point2D {
x: 1,
y: 1,
};
let otherpoint = mypoint;
println!("{}{}", mypoint, otherpoint);
}
This is the backwards-incompatible part of #13231.
Part of RFC #3.
[breaking-change]
2014-12-05 17:01:33 -08:00
|
|
|
struct Item<'a> {
|
|
|
|
cx: &'a Context,
|
|
|
|
item: &'a clean::Item,
|
|
|
|
}
|
|
|
|
|
2013-12-09 23:16:18 -08:00
|
|
|
struct Sidebar<'a> { cx: &'a Context, item: &'a clean::Item, }
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Struct representing one entry in the JS search index. These are all emitted
|
|
|
|
/// by hand to a large JS file at the end of cache-creation.
|
2013-09-18 22:18:38 -07:00
|
|
|
struct IndexItem {
|
2014-04-09 16:49:31 +09:00
|
|
|
ty: ItemType,
|
2014-05-22 16:57:53 -07:00
|
|
|
name: String,
|
|
|
|
path: String,
|
|
|
|
desc: String,
|
2014-05-22 22:00:18 -07:00
|
|
|
parent: Option<ast::DefId>,
|
2015-02-26 01:03:06 +02:00
|
|
|
search_type: Option<IndexItemFunctionType>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A type used for the search index.
|
|
|
|
struct Type {
|
|
|
|
name: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Type {
|
|
|
|
/// Formats type as {name: $name}.
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
// Wrapping struct fmt should never call us when self.name is None,
|
|
|
|
// but just to be safe we write `null` in that case.
|
|
|
|
match self.name {
|
|
|
|
Some(ref n) => write!(f, "{{\"name\":\"{}\"}}", n),
|
|
|
|
None => write!(f, "null")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Full type of functions/methods in the search index.
|
|
|
|
struct IndexItemFunctionType {
|
|
|
|
inputs: Vec<Type>,
|
|
|
|
output: Option<Type>
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for IndexItemFunctionType {
|
|
|
|
/// Formats a full fn type as a JSON {inputs: [Type], outputs: Type/null}.
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
// If we couldn't figure out a type, just write `null`.
|
|
|
|
if self.inputs.iter().any(|ref i| i.name.is_none()) ||
|
|
|
|
(self.output.is_some() && self.output.as_ref().unwrap().name.is_none()) {
|
|
|
|
return write!(f, "null")
|
|
|
|
}
|
|
|
|
|
2015-04-06 17:56:35 -07:00
|
|
|
let inputs: Vec<String> = self.inputs.iter().map(|ref t| {
|
|
|
|
format!("{}", t)
|
|
|
|
}).collect();
|
2015-07-10 08:19:21 -04:00
|
|
|
try!(write!(f, "{{\"inputs\":[{}],\"output\":", inputs.join(",")));
|
2015-02-26 01:03:06 +02:00
|
|
|
|
|
|
|
match self.output {
|
|
|
|
Some(ref t) => try!(write!(f, "{}", t)),
|
|
|
|
None => try!(write!(f, "null"))
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(try!(write!(f, "}}")))
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
// TLS keys used to carry information around during rendering.
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2014-11-14 09:18:10 -08:00
|
|
|
thread_local!(static CACHE_KEY: RefCell<Arc<Cache>> = Default::default());
|
2014-11-14 14:20:57 -08:00
|
|
|
thread_local!(pub static CURRENT_LOCATION_KEY: RefCell<Vec<String>> =
|
2014-11-14 09:18:10 -08:00
|
|
|
RefCell::new(Vec::new()));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
/// Generates the documentation for `crate` into the directory `dst`
|
2014-11-09 19:09:17 -05:00
|
|
|
pub fn run(mut krate: clean::Crate,
|
|
|
|
external_html: &ExternalHtml,
|
2015-02-26 21:00:43 -08:00
|
|
|
dst: PathBuf,
|
|
|
|
passes: HashSet<String>) -> io::Result<()> {
|
|
|
|
let src_root = match krate.src.parent() {
|
|
|
|
Some(p) => p.to_path_buf(),
|
2015-03-18 09:14:54 -07:00
|
|
|
None => PathBuf::new(),
|
2015-02-26 21:00:43 -08:00
|
|
|
};
|
2013-09-18 22:18:38 -07:00
|
|
|
let mut cx = Context {
|
|
|
|
dst: dst,
|
2015-02-26 21:00:43 -08:00
|
|
|
src_root: src_root,
|
2014-11-09 19:09:17 -05:00
|
|
|
passes: passes,
|
2014-03-05 15:28:08 -08:00
|
|
|
current: Vec::new(),
|
2014-05-22 16:57:53 -07:00
|
|
|
root_path: String::new(),
|
2013-09-18 22:18:38 -07:00
|
|
|
layout: layout::Layout {
|
2014-05-25 03:17:19 -07:00
|
|
|
logo: "".to_string(),
|
|
|
|
favicon: "".to_string(),
|
2014-06-28 03:35:25 -07:00
|
|
|
external_html: external_html.clone(),
|
2014-02-05 22:15:24 +01:00
|
|
|
krate: krate.name.clone(),
|
2014-06-06 09:12:18 -07:00
|
|
|
playground_url: "".to_string(),
|
2013-09-18 22:18:38 -07:00
|
|
|
},
|
2013-09-27 15:12:23 -07:00
|
|
|
include_sources: true,
|
2014-05-29 13:50:47 -07:00
|
|
|
render_redirect_pages: false,
|
2013-09-18 22:18:38 -07:00
|
|
|
};
|
2014-06-28 03:35:25 -07:00
|
|
|
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(mkdir(&cx.dst));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
// Crawl the crate attributes looking for attributes which control how we're
|
|
|
|
// going to emit HTML
|
DST coercions and DST structs
[breaking-change]
1. The internal layout for traits has changed from (vtable, data) to (data, vtable). If you were relying on this in unsafe transmutes, you might get some very weird and apparently unrelated errors. You should not be doing this! Prefer not to do this at all, but if you must, you should use raw::TraitObject rather than hardcoding rustc's internal representation into your code.
2. The minimal type of reference-to-vec-literals (e.g., `&[1, 2, 3]`) is now a fixed size vec (e.g., `&[int, ..3]`) where it used to be an unsized vec (e.g., `&[int]`). If you want the unszied type, you must explicitly give the type (e.g., `let x: &[_] = &[1, 2, 3]`). Note in particular where multiple blocks must have the same type (e.g., if and else clauses, vec elements), the compiler will not coerce to the unsized type without a hint. E.g., `[&[1], &[1, 2]]` used to be a valid expression of type '[&[int]]'. It no longer type checks since the first element now has type `&[int, ..1]` and the second has type &[int, ..2]` which are incompatible.
3. The type of blocks (including functions) must be coercible to the expected type (used to be a subtype). Mostly this makes things more flexible and not less (in particular, in the case of coercing function bodies to the return type). However, in some rare cases, this is less flexible. TBH, I'm not exactly sure of the exact effects. I think the change causes us to resolve inferred type variables slightly earlier which might make us slightly more restrictive. Possibly it only affects blocks with unreachable code. E.g., `if ... { fail!(); "Hello" }` used to type check, it no longer does. The fix is to add a semicolon after the string.
2014-08-04 14:20:11 +02:00
|
|
|
let default: &[_] = &[];
|
|
|
|
match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(default)) {
|
2013-09-18 22:18:38 -07:00
|
|
|
Some(attrs) => {
|
2015-01-31 12:20:46 -05:00
|
|
|
for attr in attrs {
|
2013-09-18 22:18:38 -07:00
|
|
|
match *attr {
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::NameValue(ref x, ref s)
|
2014-11-27 14:19:27 -05:00
|
|
|
if "html_favicon_url" == *x => {
|
2014-05-25 03:17:19 -07:00
|
|
|
cx.layout.favicon = s.to_string();
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::NameValue(ref x, ref s)
|
2014-11-27 14:19:27 -05:00
|
|
|
if "html_logo_url" == *x => {
|
2014-05-25 03:17:19 -07:00
|
|
|
cx.layout.logo = s.to_string();
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-06-06 09:12:18 -07:00
|
|
|
clean::NameValue(ref x, ref s)
|
2014-11-27 14:19:27 -05:00
|
|
|
if "html_playground_url" == *x => {
|
2014-06-06 09:12:18 -07:00
|
|
|
cx.layout.playground_url = s.to_string();
|
2014-11-14 14:20:57 -08:00
|
|
|
markdown::PLAYGROUND_KRATE.with(|slot| {
|
|
|
|
if slot.borrow().is_none() {
|
|
|
|
let name = krate.name.clone();
|
|
|
|
*slot.borrow_mut() = Some(Some(name));
|
|
|
|
}
|
|
|
|
});
|
2014-06-06 09:12:18 -07:00
|
|
|
}
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::Word(ref x)
|
2014-11-27 14:19:27 -05:00
|
|
|
if "html_no_source" == *x => {
|
2013-09-27 15:12:23 -07:00
|
|
|
cx.include_sources = false;
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Crawl the crate to build various caches used for the output
|
2014-11-14 14:20:57 -08:00
|
|
|
let analysis = ::ANALYSISKEY.with(|a| a.clone());
|
|
|
|
let analysis = analysis.borrow();
|
2014-05-09 13:52:17 -07:00
|
|
|
let public_items = analysis.as_ref().map(|a| a.public_items.clone());
|
2015-01-16 14:27:43 -08:00
|
|
|
let public_items = public_items.unwrap_or(NodeSet());
|
2014-05-24 11:56:38 -07:00
|
|
|
let paths: HashMap<ast::DefId, (Vec<String>, ItemType)> =
|
2014-05-23 20:17:27 -07:00
|
|
|
analysis.as_ref().map(|a| {
|
2014-08-18 17:52:38 -07:00
|
|
|
let paths = a.external_paths.borrow_mut().take().unwrap();
|
2014-12-03 23:57:45 +09:00
|
|
|
paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from_type_kind(t)))).collect()
|
|
|
|
}).unwrap_or(HashMap::new());
|
2014-04-28 20:36:08 -07:00
|
|
|
let mut cache = Cache {
|
|
|
|
impls: HashMap::new(),
|
2014-12-09 12:58:15 -05:00
|
|
|
external_paths: paths.iter().map(|(&k, v)| (k, v.0.clone()))
|
2014-05-23 20:17:27 -07:00
|
|
|
.collect(),
|
2014-05-09 13:52:17 -07:00
|
|
|
paths: paths,
|
2014-04-28 20:36:08 -07:00
|
|
|
implementors: HashMap::new(),
|
|
|
|
stack: Vec::new(),
|
|
|
|
parent_stack: Vec::new(),
|
|
|
|
search_index: Vec::new(),
|
|
|
|
extern_locations: HashMap::new(),
|
2014-05-28 19:53:37 -07:00
|
|
|
primitive_locations: HashMap::new(),
|
2014-11-09 19:09:17 -05:00
|
|
|
remove_priv: cx.passes.contains("strip-private"),
|
2014-04-28 20:36:08 -07:00
|
|
|
privmod: false,
|
2014-05-03 02:08:58 -07:00
|
|
|
public_items: public_items,
|
2014-04-28 20:36:08 -07:00
|
|
|
orphan_methods: Vec::new(),
|
2015-04-06 15:10:55 -07:00
|
|
|
traits: mem::replace(&mut krate.external_traits, HashMap::new()),
|
2015-04-13 16:23:32 -07:00
|
|
|
deref_trait_did: analysis.as_ref().and_then(|a| a.deref_trait_did),
|
2014-05-03 02:08:58 -07:00
|
|
|
typarams: analysis.as_ref().map(|a| {
|
2014-08-18 17:52:38 -07:00
|
|
|
a.external_typarams.borrow_mut().take().unwrap()
|
2014-05-03 02:08:58 -07:00
|
|
|
}).unwrap_or(HashMap::new()),
|
2014-05-26 21:51:59 -07:00
|
|
|
inlined: analysis.as_ref().map(|a| {
|
2014-08-18 17:52:38 -07:00
|
|
|
a.inlined.borrow_mut().take().unwrap()
|
2014-05-26 21:51:59 -07:00
|
|
|
}).unwrap_or(HashSet::new()),
|
2014-04-28 20:36:08 -07:00
|
|
|
};
|
2014-04-09 15:52:31 +09:00
|
|
|
|
2014-05-28 19:53:37 -07:00
|
|
|
// Cache where all our extern crates are located
|
2015-01-31 12:20:46 -05:00
|
|
|
for &(n, ref e) in &krate.externs {
|
2015-04-13 15:25:40 -07:00
|
|
|
cache.extern_locations.insert(n, (e.name.clone(),
|
|
|
|
extern_location(e, &cx.dst)));
|
2014-05-27 17:12:48 -07:00
|
|
|
let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID };
|
2014-12-03 23:57:45 +09:00
|
|
|
cache.paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
|
2014-05-27 17:12:48 -07:00
|
|
|
}
|
|
|
|
|
2014-05-28 19:53:37 -07:00
|
|
|
// Cache where all known primitives have their documentation located.
|
|
|
|
//
|
|
|
|
// Favor linking to as local extern as possible, so iterate all crates in
|
|
|
|
// reverse topological order.
|
|
|
|
for &(n, ref e) in krate.externs.iter().rev() {
|
2015-01-31 12:20:46 -05:00
|
|
|
for &prim in &e.primitives {
|
2014-05-28 19:53:37 -07:00
|
|
|
cache.primitive_locations.insert(prim, n);
|
|
|
|
}
|
|
|
|
}
|
2015-01-31 12:20:46 -05:00
|
|
|
for &prim in &krate.primitives {
|
2014-05-28 19:53:37 -07:00
|
|
|
cache.primitive_locations.insert(prim, ast::LOCAL_CRATE);
|
|
|
|
}
|
|
|
|
|
2015-04-13 16:23:32 -07:00
|
|
|
cache.stack.push(krate.name.clone());
|
|
|
|
krate = cache.fold_crate(krate);
|
|
|
|
|
2014-05-28 19:53:37 -07:00
|
|
|
// Build our search index
|
2014-05-27 17:12:48 -07:00
|
|
|
let index = try!(build_index(&krate, &mut cache));
|
2014-05-27 17:15:10 -07:00
|
|
|
|
|
|
|
// Freeze the cache now that the index has been built. Put an Arc into TLS
|
|
|
|
// for future parallelization opportunities
|
|
|
|
let cache = Arc::new(cache);
|
2014-11-14 14:20:57 -08:00
|
|
|
CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone());
|
|
|
|
CURRENT_LOCATION_KEY.with(|s| s.borrow_mut().clear());
|
2014-05-27 17:15:10 -07:00
|
|
|
|
|
|
|
try!(write_shared(&cx, &krate, &*cache, index));
|
2014-05-27 17:12:48 -07:00
|
|
|
let krate = try!(render_sources(&mut cx, krate));
|
|
|
|
|
|
|
|
// And finally render the whole crate's documentation
|
2015-04-13 11:55:00 -07:00
|
|
|
cx.krate(krate)
|
2014-05-27 17:12:48 -07:00
|
|
|
}
|
|
|
|
|
2015-02-26 21:00:43 -08:00
|
|
|
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::Result<String> {
|
2014-05-27 17:12:48 -07:00
|
|
|
// Build the search index from the collected metadata
|
2014-04-09 15:52:31 +09:00
|
|
|
let mut nodeid_to_pathid = HashMap::new();
|
|
|
|
let mut pathid_to_nodeid = Vec::new();
|
2014-03-07 17:26:06 +08:00
|
|
|
{
|
2014-05-27 17:12:48 -07:00
|
|
|
let Cache { ref mut search_index,
|
|
|
|
ref orphan_methods,
|
|
|
|
ref mut paths, .. } = *cache;
|
2014-04-09 03:25:54 +09:00
|
|
|
|
2014-03-07 17:26:06 +08:00
|
|
|
// Attach all orphan methods to the type's definition if the type
|
|
|
|
// has since been learned.
|
2015-01-31 12:20:46 -05:00
|
|
|
for &(pid, ref item) in orphan_methods {
|
2014-05-09 13:52:17 -07:00
|
|
|
let did = ast_util::local_def(pid);
|
2014-11-06 12:25:16 -05:00
|
|
|
match paths.get(&did) {
|
2014-03-07 17:26:06 +08:00
|
|
|
Some(&(ref fqp, _)) => {
|
2015-06-14 14:33:31 +03:00
|
|
|
// Needed to determine `self` type.
|
|
|
|
let parent_basename = Some(fqp[fqp.len() - 1].clone());
|
2014-05-27 17:12:48 -07:00
|
|
|
search_index.push(IndexItem {
|
2014-03-07 17:26:06 +08:00
|
|
|
ty: shortty(item),
|
|
|
|
name: item.name.clone().unwrap(),
|
2015-07-10 08:19:21 -04:00
|
|
|
path: fqp[..fqp.len() - 1].join("::"),
|
2015-03-14 00:45:39 +02:00
|
|
|
desc: shorter(item.doc_value()),
|
2014-05-22 22:00:18 -07:00
|
|
|
parent: Some(did),
|
2015-06-14 14:33:31 +03:00
|
|
|
search_type: get_index_search_type(&item, parent_basename),
|
2014-03-07 17:26:06 +08:00
|
|
|
});
|
|
|
|
},
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
};
|
2014-04-09 03:25:54 +09:00
|
|
|
|
2014-04-09 15:52:31 +09:00
|
|
|
// Reduce `NodeId` in paths into smaller sequential numbers,
|
|
|
|
// and prune the paths that do not appear in the index.
|
2015-06-11 13:56:07 +01:00
|
|
|
for item in search_index.iter() {
|
2014-04-09 03:25:54 +09:00
|
|
|
match item.parent {
|
2014-04-09 15:52:31 +09:00
|
|
|
Some(nodeid) => {
|
|
|
|
if !nodeid_to_pathid.contains_key(&nodeid) {
|
|
|
|
let pathid = pathid_to_nodeid.len();
|
|
|
|
nodeid_to_pathid.insert(nodeid, pathid);
|
|
|
|
pathid_to_nodeid.push(nodeid);
|
|
|
|
}
|
|
|
|
}
|
2014-04-09 03:25:54 +09:00
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
2014-04-09 15:52:31 +09:00
|
|
|
assert_eq!(nodeid_to_pathid.len(), pathid_to_nodeid.len());
|
2014-03-07 17:26:06 +08:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
// Collect the index into a string
|
2015-02-26 21:00:43 -08:00
|
|
|
let mut w = io::Cursor::new(Vec::new());
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(&mut w, r#"searchIndex['{}'] = {{"items":["#, krate.name));
|
2014-05-27 17:12:48 -07:00
|
|
|
|
|
|
|
let mut lastpath = "".to_string();
|
|
|
|
for (i, item) in cache.search_index.iter().enumerate() {
|
|
|
|
// Omit the path if it is same to that of the prior item.
|
|
|
|
let path;
|
2014-11-27 14:19:27 -05:00
|
|
|
if lastpath == item.path {
|
2014-05-27 17:12:48 -07:00
|
|
|
path = "";
|
|
|
|
} else {
|
|
|
|
lastpath = item.path.to_string();
|
2015-02-01 21:53:25 -05:00
|
|
|
path = &item.path;
|
2014-05-27 17:12:48 -07:00
|
|
|
};
|
2014-04-10 02:24:00 +09:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
if i > 0 {
|
|
|
|
try!(write!(&mut w, ","));
|
|
|
|
}
|
2014-11-17 11:29:38 -08:00
|
|
|
try!(write!(&mut w, r#"[{},"{}","{}",{}"#,
|
2015-03-25 17:06:52 -07:00
|
|
|
item.ty as usize, item.name, path,
|
2014-06-21 03:39:03 -07:00
|
|
|
item.desc.to_json().to_string()));
|
2014-05-27 17:12:48 -07:00
|
|
|
match item.parent {
|
|
|
|
Some(nodeid) => {
|
2014-11-06 12:25:16 -05:00
|
|
|
let pathid = *nodeid_to_pathid.get(&nodeid).unwrap();
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write!(&mut w, ",{}", pathid));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-02-26 01:03:06 +02:00
|
|
|
None => try!(write!(&mut w, ",null"))
|
|
|
|
}
|
|
|
|
match item.search_type {
|
|
|
|
Some(ref t) => try!(write!(&mut w, ",{}", t)),
|
|
|
|
None => try!(write!(&mut w, ",null"))
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write!(&mut w, "]"));
|
|
|
|
}
|
2014-04-10 02:24:00 +09:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write!(&mut w, r#"],"paths":["#));
|
2014-04-10 02:24:00 +09:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
for (i, &did) in pathid_to_nodeid.iter().enumerate() {
|
2014-11-06 12:25:16 -05:00
|
|
|
let &(ref fqp, short) = cache.paths.get(&did).unwrap();
|
2014-05-27 17:12:48 -07:00
|
|
|
if i > 0 {
|
|
|
|
try!(write!(&mut w, ","));
|
2014-03-16 01:08:56 -07:00
|
|
|
}
|
2014-11-17 11:29:38 -08:00
|
|
|
try!(write!(&mut w, r#"[{},"{}"]"#,
|
2015-03-25 17:06:52 -07:00
|
|
|
short as usize, *fqp.last().unwrap()));
|
2014-05-27 17:12:48 -07:00
|
|
|
}
|
2014-04-10 02:24:00 +09:00
|
|
|
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(&mut w, "]}};"));
|
2014-03-16 01:08:56 -07:00
|
|
|
|
2015-02-26 21:00:43 -08:00
|
|
|
Ok(String::from_utf8(w.into_inner()).unwrap())
|
2014-05-27 17:12:48 -07:00
|
|
|
}
|
2014-03-16 01:08:56 -07:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
fn write_shared(cx: &Context,
|
|
|
|
krate: &clean::Crate,
|
|
|
|
cache: &Cache,
|
2015-02-26 21:00:43 -08:00
|
|
|
search_index: String) -> io::Result<()> {
|
2014-03-16 01:08:56 -07:00
|
|
|
// Write out the shared files. Note that these are shared among all rustdoc
|
|
|
|
// docs placed in the output directory, so this needs to be a synchronized
|
|
|
|
// operation with respect to all other rustdocs running around.
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(mkdir(&cx.dst));
|
|
|
|
let _lock = ::flock::Lock::new(&cx.dst.join(".lock"));
|
|
|
|
|
|
|
|
// Add all the static files. These may already exist, but we just
|
|
|
|
// overwrite them anyway to make sure that they're fresh and up-to-date.
|
|
|
|
try!(write(cx.dst.join("jquery.js"),
|
2015-07-01 23:52:40 -04:00
|
|
|
include_bytes!("static/jquery-2.1.4.min.js")));
|
2014-12-22 10:57:09 +13:00
|
|
|
try!(write(cx.dst.join("main.js"), include_bytes!("static/main.js")));
|
|
|
|
try!(write(cx.dst.join("playpen.js"), include_bytes!("static/playpen.js")));
|
|
|
|
try!(write(cx.dst.join("main.css"), include_bytes!("static/main.css")));
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write(cx.dst.join("normalize.css"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/normalize.css")));
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write(cx.dst.join("FiraSans-Regular.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/FiraSans-Regular.woff")));
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write(cx.dst.join("FiraSans-Medium.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/FiraSans-Medium.woff")));
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write(cx.dst.join("Heuristica-Italic.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/Heuristica-Italic.woff")));
|
2014-07-11 09:49:59 +02:00
|
|
|
try!(write(cx.dst.join("SourceSerifPro-Regular.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/SourceSerifPro-Regular.woff")));
|
2014-07-08 19:51:06 +02:00
|
|
|
try!(write(cx.dst.join("SourceSerifPro-Bold.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/SourceSerifPro-Bold.woff")));
|
2014-07-08 20:26:23 +02:00
|
|
|
try!(write(cx.dst.join("SourceCodePro-Regular.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/SourceCodePro-Regular.woff")));
|
2014-07-08 20:26:23 +02:00
|
|
|
try!(write(cx.dst.join("SourceCodePro-Semibold.woff"),
|
2014-12-22 10:57:09 +13:00
|
|
|
include_bytes!("static/SourceCodePro-Semibold.woff")));
|
2014-05-27 17:12:48 -07:00
|
|
|
|
|
|
|
fn collect(path: &Path, krate: &str,
|
2015-02-26 21:00:43 -08:00
|
|
|
key: &str) -> io::Result<Vec<String>> {
|
2014-05-27 17:12:48 -07:00
|
|
|
let mut ret = Vec::new();
|
|
|
|
if path.exists() {
|
2015-02-26 21:00:43 -08:00
|
|
|
for line in BufReader::new(try!(File::open(path))).lines() {
|
2014-05-27 17:12:48 -07:00
|
|
|
let line = try!(line);
|
2014-11-27 14:19:27 -05:00
|
|
|
if !line.starts_with(key) {
|
2014-05-27 17:12:48 -07:00
|
|
|
continue
|
2014-03-16 01:08:56 -07:00
|
|
|
}
|
2015-02-01 21:53:25 -05:00
|
|
|
if line.starts_with(&format!("{}['{}']", key, krate)) {
|
2014-05-27 17:12:48 -07:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
ret.push(line.to_string());
|
2014-01-30 11:30:21 -08:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-27 17:12:48 -07:00
|
|
|
return Ok(ret);
|
|
|
|
}
|
2014-05-21 16:41:58 -07:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
// Update the search index
|
|
|
|
let dst = cx.dst.join("search-index.js");
|
2015-02-01 21:53:25 -05:00
|
|
|
let all_indexes = try!(collect(&dst, &krate.name, "searchIndex"));
|
2014-05-27 17:12:48 -07:00
|
|
|
let mut w = try!(File::create(&dst));
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(writeln!(&mut w, "var searchIndex = {{}};"));
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(writeln!(&mut w, "{}", search_index));
|
2015-01-31 12:20:46 -05:00
|
|
|
for index in &all_indexes {
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(writeln!(&mut w, "{}", *index));
|
|
|
|
}
|
|
|
|
try!(writeln!(&mut w, "initSearch(searchIndex);"));
|
|
|
|
|
|
|
|
// Update the list of all implementors for traits
|
|
|
|
let dst = cx.dst.join("implementors");
|
|
|
|
try!(mkdir(&dst));
|
2015-01-31 12:20:46 -05:00
|
|
|
for (&did, imps) in &cache.implementors {
|
2014-05-29 13:50:47 -07:00
|
|
|
// Private modules can leak through to this phase of rustdoc, which
|
|
|
|
// could contain implementations for otherwise private types. In some
|
|
|
|
// rare cases we could find an implementation for an item which wasn't
|
|
|
|
// indexed, so we just skip this step in that case.
|
|
|
|
//
|
|
|
|
// FIXME: this is a vague explanation for why this can't be a `get`, in
|
|
|
|
// theory it should be...
|
2014-11-06 12:25:16 -05:00
|
|
|
let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) {
|
2014-05-29 13:50:47 -07:00
|
|
|
Some(p) => p,
|
|
|
|
None => continue,
|
|
|
|
};
|
2014-05-27 17:12:48 -07:00
|
|
|
|
|
|
|
let mut mydst = dst.clone();
|
2015-01-31 12:20:46 -05:00
|
|
|
for part in &remote_path[..remote_path.len() - 1] {
|
2015-02-01 21:53:25 -05:00
|
|
|
mydst.push(part);
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(mkdir(&mydst));
|
|
|
|
}
|
2015-02-26 21:00:43 -08:00
|
|
|
mydst.push(&format!("{}.{}.js",
|
|
|
|
remote_item_type.to_static_str(),
|
|
|
|
remote_path[remote_path.len() - 1]));
|
2015-02-01 21:53:25 -05:00
|
|
|
let all_implementors = try!(collect(&mydst, &krate.name,
|
2014-05-27 17:12:48 -07:00
|
|
|
"implementors"));
|
|
|
|
|
2015-02-26 21:00:43 -08:00
|
|
|
try!(mkdir(mydst.parent().unwrap()));
|
|
|
|
let mut f = BufWriter::new(try!(File::create(&mydst)));
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(writeln!(&mut f, "(function() {{var implementors = {{}};"));
|
2014-05-27 17:12:48 -07:00
|
|
|
|
2015-01-31 12:20:46 -05:00
|
|
|
for implementor in &all_implementors {
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(write!(&mut f, "{}", *implementor));
|
2014-03-16 01:08:56 -07:00
|
|
|
}
|
2014-05-21 16:41:58 -07:00
|
|
|
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(write!(&mut f, r"implementors['{}'] = [", krate.name));
|
2015-01-31 12:20:46 -05:00
|
|
|
for imp in imps {
|
2014-06-01 11:30:53 -07:00
|
|
|
// If the trait and implementation are in the same crate, then
|
|
|
|
// there's no need to emit information about it (there's inlining
|
|
|
|
// going on). If they're in different crates then the crate defining
|
|
|
|
// the trait will be interested in our implementation.
|
|
|
|
if imp.def_id.krate == did.krate { continue }
|
2015-07-18 02:02:57 -04:00
|
|
|
try!(write!(&mut f, r#""{}","#, imp.impl_));
|
2014-05-21 16:41:58 -07:00
|
|
|
}
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(writeln!(&mut f, r"];"));
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(writeln!(&mut f, "{}", r"
|
|
|
|
if (window.register_implementors) {
|
|
|
|
window.register_implementors(implementors);
|
|
|
|
} else {
|
|
|
|
window.pending_implementors = implementors;
|
|
|
|
}
|
|
|
|
"));
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(writeln!(&mut f, r"}})()"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-27 17:12:48 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
fn render_sources(cx: &mut Context,
|
2015-02-26 21:00:43 -08:00
|
|
|
krate: clean::Crate) -> io::Result<clean::Crate> {
|
2014-05-27 17:12:48 -07:00
|
|
|
info!("emitting source files");
|
|
|
|
let dst = cx.dst.join("src");
|
|
|
|
try!(mkdir(&dst));
|
2015-02-01 21:53:25 -05:00
|
|
|
let dst = dst.join(&krate.name);
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(mkdir(&dst));
|
|
|
|
let mut folder = SourceCollector {
|
|
|
|
dst: dst,
|
|
|
|
seen: HashSet::new(),
|
|
|
|
cx: cx,
|
|
|
|
};
|
|
|
|
// skip all invalid spans
|
|
|
|
folder.seen.insert("".to_string());
|
|
|
|
Ok(folder.fold_crate(krate))
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Writes the entire contents of a string to a destination, not attempting to
|
|
|
|
/// catch any errors.
|
2015-02-26 21:00:43 -08:00
|
|
|
fn write(dst: PathBuf, contents: &[u8]) -> io::Result<()> {
|
|
|
|
try!(File::create(&dst)).write_all(contents)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-05-09 00:12:29 +09:00
|
|
|
/// Makes a directory on the filesystem, failing the thread if an error occurs and
|
2013-10-03 10:24:40 -07:00
|
|
|
/// skipping if the directory already exists.
|
2015-02-26 21:00:43 -08:00
|
|
|
fn mkdir(path: &Path) -> io::Result<()> {
|
2014-01-30 11:30:21 -08:00
|
|
|
if !path.exists() {
|
2015-02-26 21:00:43 -08:00
|
|
|
fs::create_dir(path)
|
2014-01-30 11:30:21 -08:00
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-12-03 23:57:45 +09:00
|
|
|
/// Returns a documentation-level item type from the item.
|
|
|
|
fn shortty(item: &clean::Item) -> ItemType {
|
|
|
|
ItemType::from_item(item)
|
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Takes a path to a source file and cleans the path to it. This canonicalizes
|
2013-09-26 17:21:59 -07:00
|
|
|
/// things like ".." to components which preserve the "top down" hierarchy of a
|
2015-03-16 19:44:13 -07:00
|
|
|
/// static HTML tree. Each component in the cleaned path will be passed as an
|
|
|
|
/// argument to `f`. The very last component of the path (ie the file name) will
|
|
|
|
/// be passed to `f` if `keep_filename` is true, and ignored otherwise.
|
2013-09-26 17:21:59 -07:00
|
|
|
// FIXME (#9639): The closure should deal with &[u8] instead of &str
|
2014-12-01 20:46:51 +09:00
|
|
|
// FIXME (#9639): This is too conservative, rejecting non-UTF-8 paths
|
2015-03-16 19:44:13 -07:00
|
|
|
fn clean_srcpath<F>(src_root: &Path, p: &Path, keep_filename: bool, mut f: F) where
|
2014-12-09 15:22:19 -05:00
|
|
|
F: FnMut(&str),
|
|
|
|
{
|
2014-12-01 20:46:51 +09:00
|
|
|
// make it relative, if possible
|
2015-02-26 21:00:43 -08:00
|
|
|
let p = p.relative_from(src_root).unwrap_or(p);
|
2014-12-01 20:46:51 +09:00
|
|
|
|
2015-03-16 19:44:13 -07:00
|
|
|
let mut iter = p.iter().map(|x| x.to_str().unwrap()).peekable();
|
|
|
|
while let Some(c) = iter.next() {
|
|
|
|
if !keep_filename && iter.peek().is_none() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-26 21:00:43 -08:00
|
|
|
if ".." == c {
|
|
|
|
f("up");
|
|
|
|
} else {
|
|
|
|
f(c)
|
2013-09-27 15:12:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Attempts to find where an external crate is located, given that we're
|
|
|
|
/// rendering in to the specified source destination.
|
2013-10-02 15:39:32 -07:00
|
|
|
fn extern_location(e: &clean::ExternalCrate, dst: &Path) -> ExternalLocation {
|
|
|
|
// See if there's documentation generated into the local directory
|
2015-02-01 21:53:25 -05:00
|
|
|
let local_location = dst.join(&e.name);
|
2013-10-02 15:39:32 -07:00
|
|
|
if local_location.is_dir() {
|
|
|
|
return Local;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Failing that, see if there's an attribute specifying where to find this
|
|
|
|
// external crate
|
2015-01-31 12:20:46 -05:00
|
|
|
for attr in &e.attrs {
|
2013-10-02 15:39:32 -07:00
|
|
|
match *attr {
|
2014-11-27 14:19:27 -05:00
|
|
|
clean::List(ref x, ref list) if "doc" == *x => {
|
2015-01-31 12:20:46 -05:00
|
|
|
for attr in list {
|
2013-10-02 15:39:32 -07:00
|
|
|
match *attr {
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::NameValue(ref x, ref s)
|
2014-11-27 14:19:27 -05:00
|
|
|
if "html_root_url" == *x => {
|
|
|
|
if s.ends_with("/") {
|
2014-05-25 03:17:19 -07:00
|
|
|
return Remote(s.to_string());
|
2013-10-02 15:39:32 -07:00
|
|
|
}
|
2014-05-27 20:44:58 -07:00
|
|
|
return Remote(format!("{}/", s));
|
2013-10-02 15:39:32 -07:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Well, at least we tried.
|
|
|
|
return Unknown;
|
|
|
|
}
|
|
|
|
|
2013-12-09 23:16:18 -08:00
|
|
|
impl<'a> DocFolder for SourceCollector<'a> {
|
2013-09-27 15:12:23 -07:00
|
|
|
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
2013-10-03 10:24:40 -07:00
|
|
|
// If we're including source files, and we haven't seen this file yet,
|
|
|
|
// then we need to render it out to the filesystem
|
2013-09-30 12:58:18 -07:00
|
|
|
if self.cx.include_sources && !self.seen.contains(&item.source.filename) {
|
2013-10-03 10:24:40 -07:00
|
|
|
|
2013-09-30 12:58:18 -07:00
|
|
|
// If it turns out that we couldn't read this file, then we probably
|
|
|
|
// can't read any of the files (generating html output from json or
|
|
|
|
// something like that), so just don't include sources for the
|
|
|
|
// entire crate. The other option is maintaining this mapping on a
|
|
|
|
// per-file basis, but that's probably not worth it...
|
2014-05-12 13:44:59 -07:00
|
|
|
self.cx
|
2015-02-01 21:53:25 -05:00
|
|
|
.include_sources = match self.emit_source(&item.source .filename) {
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(()) => true,
|
|
|
|
Err(e) => {
|
|
|
|
println!("warning: source code was requested to be rendered, \
|
|
|
|
but processing `{}` had an error: {}",
|
|
|
|
item.source.filename, e);
|
|
|
|
println!(" skipping rendering of source code");
|
|
|
|
false
|
|
|
|
}
|
|
|
|
};
|
2013-09-27 15:12:23 -07:00
|
|
|
self.seen.insert(item.source.filename.clone());
|
|
|
|
}
|
2013-10-03 10:24:40 -07:00
|
|
|
|
2013-09-27 15:12:23 -07:00
|
|
|
self.fold_item_recur(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-09 23:16:18 -08:00
|
|
|
impl<'a> SourceCollector<'a> {
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Renders the given filename into its corresponding HTML source file.
|
2015-02-26 21:00:43 -08:00
|
|
|
fn emit_source(&mut self, filename: &str) -> io::Result<()> {
|
2015-03-18 09:14:54 -07:00
|
|
|
let p = PathBuf::from(filename);
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
// If we couldn't open this file, then just returns because it
|
|
|
|
// probably means that it's some standard library macro thing and we
|
|
|
|
// can't have the source to it anyway.
|
2015-02-26 21:00:43 -08:00
|
|
|
let mut contents = Vec::new();
|
|
|
|
match File::open(&p).and_then(|mut f| f.read_to_end(&mut contents)) {
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(r) => r,
|
2014-02-14 13:11:36 -08:00
|
|
|
// macros from other libraries get special filenames which we can
|
|
|
|
// safely ignore
|
|
|
|
Err(..) if filename.starts_with("<") &&
|
|
|
|
filename.ends_with("macros>") => return Ok(()),
|
2014-01-30 11:30:21 -08:00
|
|
|
Err(e) => return Err(e)
|
|
|
|
};
|
2015-02-01 21:53:25 -05:00
|
|
|
let contents = str::from_utf8(&contents).unwrap();
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2014-03-18 08:59:44 +08:00
|
|
|
// Remove the utf-8 BOM if any
|
2014-12-09 14:08:10 -08:00
|
|
|
let contents = if contents.starts_with("\u{feff}") {
|
2015-01-17 16:15:52 -08:00
|
|
|
&contents[3..]
|
2014-03-18 08:59:44 +08:00
|
|
|
} else {
|
2014-07-04 22:38:13 +02:00
|
|
|
contents
|
2014-03-18 08:59:44 +08:00
|
|
|
};
|
|
|
|
|
2013-09-27 15:12:23 -07:00
|
|
|
// Create the intermediate directories
|
|
|
|
let mut cur = self.dst.clone();
|
2015-06-08 16:55:35 +02:00
|
|
|
let mut root_path = String::from("../../");
|
2015-03-16 19:44:13 -07:00
|
|
|
clean_srcpath(&self.cx.src_root, &p, false, |component| {
|
2013-10-05 19:49:32 -07:00
|
|
|
cur.push(component);
|
2014-01-30 11:30:21 -08:00
|
|
|
mkdir(&cur).unwrap();
|
2013-09-27 15:12:23 -07:00
|
|
|
root_path.push_str("../");
|
2013-11-21 15:42:55 -08:00
|
|
|
});
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2015-02-26 21:00:43 -08:00
|
|
|
let mut fname = p.file_name().expect("source has no filename")
|
|
|
|
.to_os_string();
|
2015-03-02 10:46:05 -08:00
|
|
|
fname.push(".html");
|
2015-03-18 09:14:54 -07:00
|
|
|
cur.push(&fname[..]);
|
2015-02-26 21:00:43 -08:00
|
|
|
let mut w = BufWriter::new(try!(File::create(&cur)));
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2015-02-26 21:00:43 -08:00
|
|
|
let title = format!("{} -- source", cur.file_name().unwrap()
|
|
|
|
.to_string_lossy());
|
2014-08-04 13:53:33 -07:00
|
|
|
let desc = format!("Source to the Rust file `{}`.", filename);
|
2013-09-27 15:12:23 -07:00
|
|
|
let page = layout::Page {
|
2015-02-01 21:53:25 -05:00
|
|
|
title: &title,
|
2013-09-27 15:12:23 -07:00
|
|
|
ty: "source",
|
2015-02-01 21:53:25 -05:00
|
|
|
root_path: &root_path,
|
|
|
|
description: &desc,
|
2014-08-04 14:30:06 -07:00
|
|
|
keywords: get_basic_keywords(),
|
2013-09-27 15:12:23 -07:00
|
|
|
};
|
2015-02-26 21:00:43 -08:00
|
|
|
try!(layout::render(&mut w, &self.cx.layout,
|
2014-05-10 14:05:06 -07:00
|
|
|
&page, &(""), &Source(contents)));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(w.flush());
|
2014-01-30 11:30:21 -08:00
|
|
|
return Ok(());
|
2013-09-27 15:12:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl DocFolder for Cache {
|
2013-09-18 22:18:38 -07:00
|
|
|
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
2014-01-10 14:54:11 -08:00
|
|
|
// If this is a private module, we don't want it in the search index.
|
|
|
|
let orig_privmod = match item.inner {
|
|
|
|
clean::ModuleItem(..) => {
|
|
|
|
let prev = self.privmod;
|
2014-11-09 19:09:17 -05:00
|
|
|
self.privmod = prev || (self.remove_priv && item.visibility != Some(ast::Public));
|
2014-01-10 14:54:11 -08:00
|
|
|
prev
|
|
|
|
}
|
|
|
|
_ => self.privmod,
|
|
|
|
};
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
// Register any generics to their corresponding string. This is used
|
|
|
|
// when pretty-printing types
|
|
|
|
match item.inner {
|
2013-11-21 13:17:46 -08:00
|
|
|
clean::StructItem(ref s) => self.generics(&s.generics),
|
|
|
|
clean::EnumItem(ref e) => self.generics(&e.generics),
|
|
|
|
clean::FunctionItem(ref f) => self.generics(&f.generics),
|
2015-05-21 14:17:37 +02:00
|
|
|
clean::TypedefItem(ref t, _) => self.generics(&t.generics),
|
2013-11-21 13:17:46 -08:00
|
|
|
clean::TraitItem(ref t) => self.generics(&t.generics),
|
|
|
|
clean::ImplItem(ref i) => self.generics(&i.generics),
|
|
|
|
clean::TyMethodItem(ref i) => self.generics(&i.generics),
|
|
|
|
clean::MethodItem(ref i) => self.generics(&i.generics),
|
|
|
|
clean::ForeignFunctionItem(ref f) => self.generics(&f.generics),
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Propagate a trait methods' documentation to all implementors of the
|
|
|
|
// trait
|
2014-11-29 16:41:21 -05:00
|
|
|
if let clean::TraitItem(ref t) = item.inner {
|
|
|
|
self.traits.insert(item.def_id, t.clone());
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all the implementors of traits.
|
2014-11-29 16:41:21 -05:00
|
|
|
if let clean::ImplItem(ref i) = item.inner {
|
|
|
|
match i.trait_ {
|
|
|
|
Some(clean::ResolvedPath{ did, .. }) => {
|
2015-03-20 13:43:01 -04:00
|
|
|
self.implementors.entry(did).or_insert(vec![]).push(Implementor {
|
2014-11-29 16:41:21 -05:00
|
|
|
def_id: item.def_id,
|
|
|
|
stability: item.stability.clone(),
|
2015-07-18 02:02:57 -04:00
|
|
|
impl_: i.clone(),
|
2014-11-29 16:41:21 -05:00
|
|
|
});
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-11-29 16:41:21 -05:00
|
|
|
Some(..) | None => {}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Index this method for searching later on
|
2014-11-29 16:41:21 -05:00
|
|
|
if let Some(ref s) = item.name {
|
|
|
|
let (parent, is_method) = match item.inner {
|
2015-05-09 00:03:42 +02:00
|
|
|
clean::AssociatedTypeItem(..) |
|
|
|
|
clean::AssociatedConstItem(..) |
|
2014-11-29 16:41:21 -05:00
|
|
|
clean::TyMethodItem(..) |
|
|
|
|
clean::StructFieldItem(..) |
|
|
|
|
clean::VariantItem(..) => {
|
|
|
|
((Some(*self.parent_stack.last().unwrap()),
|
2015-01-19 11:07:13 -05:00
|
|
|
Some(&self.stack[..self.stack.len() - 1])),
|
2014-11-29 16:41:21 -05:00
|
|
|
false)
|
|
|
|
}
|
|
|
|
clean::MethodItem(..) => {
|
2015-03-24 16:53:34 -07:00
|
|
|
if self.parent_stack.is_empty() {
|
2014-11-29 16:41:21 -05:00
|
|
|
((None, None), false)
|
|
|
|
} else {
|
|
|
|
let last = self.parent_stack.last().unwrap();
|
|
|
|
let did = *last;
|
|
|
|
let path = match self.paths.get(&did) {
|
2014-12-03 23:57:45 +09:00
|
|
|
Some(&(_, ItemType::Trait)) =>
|
2015-01-19 11:07:13 -05:00
|
|
|
Some(&self.stack[..self.stack.len() - 1]),
|
2015-04-06 19:21:52 -07:00
|
|
|
// The current stack not necessarily has correlation
|
|
|
|
// for where the type was defined. On the other
|
|
|
|
// hand, `paths` always has the right
|
|
|
|
// information if present.
|
2014-12-03 23:57:45 +09:00
|
|
|
Some(&(ref fqp, ItemType::Struct)) |
|
|
|
|
Some(&(ref fqp, ItemType::Enum)) =>
|
2015-01-19 11:07:13 -05:00
|
|
|
Some(&fqp[..fqp.len() - 1]),
|
2015-02-01 21:53:25 -05:00
|
|
|
Some(..) => Some(&*self.stack),
|
2014-11-29 16:41:21 -05:00
|
|
|
None => None
|
|
|
|
};
|
|
|
|
((Some(*last), path), true)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-11-29 16:41:21 -05:00
|
|
|
}
|
2015-05-21 14:17:37 +02:00
|
|
|
clean::TypedefItem(_, true) => {
|
|
|
|
// skip associated types in impls
|
|
|
|
((None, None), false)
|
|
|
|
}
|
2015-02-01 21:53:25 -05:00
|
|
|
_ => ((None, Some(&*self.stack)), false)
|
2014-11-29 16:41:21 -05:00
|
|
|
};
|
|
|
|
let hidden_field = match item.inner {
|
|
|
|
clean::StructFieldItem(clean::HiddenStructField) => true,
|
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
|
|
|
|
match parent {
|
|
|
|
(parent, Some(path)) if is_method || (!self.privmod && !hidden_field) => {
|
2015-02-26 01:03:06 +02:00
|
|
|
// Needed to determine `self` type.
|
|
|
|
let parent_basename = self.parent_stack.first().and_then(|parent| {
|
|
|
|
match self.paths.get(parent) {
|
|
|
|
Some(&(ref fqp, _)) => Some(fqp[fqp.len() - 1].clone()),
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-11-29 16:41:21 -05:00
|
|
|
self.search_index.push(IndexItem {
|
|
|
|
ty: shortty(&item),
|
|
|
|
name: s.to_string(),
|
2015-07-10 08:19:21 -04:00
|
|
|
path: path.join("::").to_string(),
|
2015-03-14 00:45:39 +02:00
|
|
|
desc: shorter(item.doc_value()),
|
2014-11-29 16:41:21 -05:00
|
|
|
parent: parent,
|
2015-02-26 01:03:06 +02:00
|
|
|
search_type: get_index_search_type(&item, parent_basename),
|
2014-11-29 16:41:21 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
(Some(parent), None) if is_method || (!self.privmod && !hidden_field)=> {
|
|
|
|
if ast_util::is_local(parent) {
|
|
|
|
// We have a parent, but we don't know where they're
|
|
|
|
// defined yet. Wait for later to index this item.
|
|
|
|
self.orphan_methods.push((parent.node, item.clone()))
|
2014-03-07 17:26:06 +08:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-11-29 16:41:21 -05:00
|
|
|
_ => {}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep track of the fully qualified path for this item.
|
|
|
|
let pushed = if item.name.is_some() {
|
2014-08-18 17:52:38 -07:00
|
|
|
let n = item.name.as_ref().unwrap();
|
2015-03-24 16:54:09 -07:00
|
|
|
if !n.is_empty() {
|
2014-05-25 03:17:19 -07:00
|
|
|
self.stack.push(n.to_string());
|
2013-09-18 22:18:38 -07:00
|
|
|
true
|
|
|
|
} else { false }
|
|
|
|
} else { false };
|
|
|
|
match item.inner {
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::StructItem(..) | clean::EnumItem(..) |
|
|
|
|
clean::TypedefItem(..) | clean::TraitItem(..) |
|
|
|
|
clean::FunctionItem(..) | clean::ModuleItem(..) |
|
2014-05-29 13:50:47 -07:00
|
|
|
clean::ForeignFunctionItem(..) if !self.privmod => {
|
2014-05-22 22:00:18 -07:00
|
|
|
// Reexported items mean that the same id can show up twice
|
|
|
|
// in the rustdoc ast that we're looking at. We know,
|
|
|
|
// however, that a reexported item doesn't show up in the
|
|
|
|
// `public_items` map, so we can skip inserting into the
|
|
|
|
// paths map if there was already an entry present and we're
|
|
|
|
// not a public item.
|
|
|
|
let id = item.def_id.node;
|
|
|
|
if !self.paths.contains_key(&item.def_id) ||
|
2014-05-23 19:41:24 -07:00
|
|
|
!ast_util::is_local(item.def_id) ||
|
2014-05-22 22:00:18 -07:00
|
|
|
self.public_items.contains(&id) {
|
|
|
|
self.paths.insert(item.def_id,
|
|
|
|
(self.stack.clone(), shortty(&item)));
|
2014-02-16 23:11:09 -08:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-02-16 14:35:13 -08:00
|
|
|
// link variants to their parent enum because pages aren't emitted
|
|
|
|
// for each variant
|
2014-05-29 13:50:47 -07:00
|
|
|
clean::VariantItem(..) if !self.privmod => {
|
2014-02-16 14:35:13 -08:00
|
|
|
let mut stack = self.stack.clone();
|
|
|
|
stack.pop();
|
2014-12-03 23:57:45 +09:00
|
|
|
self.paths.insert(item.def_id, (stack, ItemType::Enum));
|
2014-02-16 14:35:13 -08:00
|
|
|
}
|
2014-06-03 17:55:30 -07:00
|
|
|
|
|
|
|
clean::PrimitiveItem(..) if item.visibility.is_some() => {
|
|
|
|
self.paths.insert(item.def_id, (self.stack.clone(),
|
|
|
|
shortty(&item)));
|
|
|
|
}
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maintain the parent stack
|
|
|
|
let parent_pushed = match item.inner {
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => {
|
2014-05-22 22:00:18 -07:00
|
|
|
self.parent_stack.push(item.def_id);
|
2014-05-03 02:08:58 -07:00
|
|
|
true
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
clean::ImplItem(ref i) => {
|
|
|
|
match i.for_ {
|
2014-05-09 13:52:17 -07:00
|
|
|
clean::ResolvedPath{ did, .. } => {
|
2014-05-22 22:00:18 -07:00
|
|
|
self.parent_stack.push(did);
|
|
|
|
true
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-04-07 17:35:23 -07:00
|
|
|
ref t => {
|
|
|
|
match t.primitive_type() {
|
|
|
|
Some(prim) => {
|
|
|
|
let did = ast_util::local_def(prim.to_node_id());
|
|
|
|
self.parent_stack.push(did);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
|
|
|
|
// Once we've recursively found all the generics, then hoard off all the
|
|
|
|
// implementations elsewhere
|
|
|
|
let ret = match self.fold_item_recur(item) {
|
|
|
|
Some(item) => {
|
2013-09-30 17:04:14 -07:00
|
|
|
match item {
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::Item{ attrs, inner: clean::ImplItem(i), .. } => {
|
2014-05-28 19:53:37 -07:00
|
|
|
// extract relevant documentation for this impl
|
2014-09-14 20:27:36 -07:00
|
|
|
let dox = match attrs.into_iter().find(|a| {
|
2014-05-28 19:53:37 -07:00
|
|
|
match *a {
|
|
|
|
clean::NameValue(ref x, _)
|
2014-11-27 14:19:27 -05:00
|
|
|
if "doc" == *x => {
|
2014-05-28 19:53:37 -07:00
|
|
|
true
|
|
|
|
}
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
Some(clean::NameValue(_, dox)) => Some(dox),
|
|
|
|
Some(..) | None => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Figure out the id of this impl. This may map to a
|
|
|
|
// primitive rather than always to a struct/enum.
|
|
|
|
let did = match i.for_ {
|
2015-04-07 17:35:23 -07:00
|
|
|
clean::ResolvedPath { did, .. } |
|
|
|
|
clean::BorrowedRef {
|
|
|
|
type_: box clean::ResolvedPath { did, .. }, ..
|
2015-04-06 20:15:59 -07:00
|
|
|
} => {
|
|
|
|
Some(did)
|
|
|
|
}
|
2014-05-28 19:53:37 -07:00
|
|
|
|
2015-04-07 17:35:23 -07:00
|
|
|
ref t => {
|
2015-04-13 16:23:32 -07:00
|
|
|
t.primitive_type().and_then(|t| {
|
|
|
|
self.primitive_locations.get(&t).map(|n| {
|
|
|
|
let id = t.to_node_id();
|
|
|
|
ast::DefId { krate: *n, node: id }
|
|
|
|
})
|
2015-04-07 17:35:23 -07:00
|
|
|
})
|
2014-05-28 19:53:37 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-11-29 16:41:21 -05:00
|
|
|
if let Some(did) = did {
|
2015-03-20 13:43:01 -04:00
|
|
|
self.impls.entry(did).or_insert(vec![]).push(Impl {
|
2014-11-29 16:41:21 -05:00
|
|
|
impl_: i,
|
|
|
|
dox: dox,
|
|
|
|
stability: item.stability.clone(),
|
|
|
|
});
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-11-29 16:41:21 -05:00
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
None
|
|
|
|
}
|
2014-01-10 14:54:11 -08:00
|
|
|
|
2013-09-30 17:04:14 -07:00
|
|
|
i => Some(i),
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
i => i,
|
|
|
|
};
|
|
|
|
|
2013-12-23 16:20:52 +01:00
|
|
|
if pushed { self.stack.pop().unwrap(); }
|
|
|
|
if parent_pushed { self.parent_stack.pop().unwrap(); }
|
2014-01-10 14:54:11 -08:00
|
|
|
self.privmod = orig_privmod;
|
2013-09-18 22:18:38 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-09 23:16:18 -08:00
|
|
|
impl<'a> Cache {
|
2013-09-18 22:18:38 -07:00
|
|
|
fn generics(&mut self, generics: &clean::Generics) {
|
2015-01-31 12:20:46 -05:00
|
|
|
for typ in &generics.type_params {
|
2014-05-03 02:08:58 -07:00
|
|
|
self.typarams.insert(typ.did, typ.name.clone());
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Context {
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Recurse in the directory structure and change the "root path" to make
|
|
|
|
/// sure it always points to the top (relatively)
|
2014-12-09 15:22:19 -05:00
|
|
|
fn recurse<T, F>(&mut self, s: String, f: F) -> T where
|
|
|
|
F: FnOnce(&mut Context) -> T,
|
|
|
|
{
|
2015-03-24 16:53:34 -07:00
|
|
|
if s.is_empty() {
|
2014-12-20 00:09:35 -08:00
|
|
|
panic!("Unexpected empty destination: {:?}", self.current);
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-09-26 17:21:59 -07:00
|
|
|
let prev = self.dst.clone();
|
2015-02-01 21:53:25 -05:00
|
|
|
self.dst.push(&s);
|
2013-09-18 22:18:38 -07:00
|
|
|
self.root_path.push_str("../");
|
|
|
|
self.current.push(s);
|
|
|
|
|
2013-12-17 11:19:14 -05:00
|
|
|
info!("Recursing into {}", self.dst.display());
|
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
mkdir(&self.dst).unwrap();
|
2013-09-18 22:18:38 -07:00
|
|
|
let ret = f(self);
|
|
|
|
|
2013-12-17 11:19:14 -05:00
|
|
|
info!("Recursed; leaving {}", self.dst.display());
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
// Go back to where we were at
|
|
|
|
self.dst = prev;
|
|
|
|
let len = self.root_path.len();
|
|
|
|
self.root_path.truncate(len - 3);
|
2013-12-23 16:20:52 +01:00
|
|
|
self.current.pop().unwrap();
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-12-05 18:19:06 -08:00
|
|
|
/// Main method for rendering a crate.
|
|
|
|
///
|
|
|
|
/// This currently isn't parallelized, but it'd be pretty easy to add
|
|
|
|
/// parallelization to this function.
|
2015-04-13 11:55:00 -07:00
|
|
|
fn krate(self, mut krate: clean::Crate) -> io::Result<()> {
|
2014-02-05 22:15:24 +01:00
|
|
|
let mut item = match krate.module.take() {
|
2013-09-18 22:18:38 -07:00
|
|
|
Some(i) => i,
|
2014-01-30 11:30:21 -08:00
|
|
|
None => return Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
};
|
2014-02-05 22:15:24 +01:00
|
|
|
item.name = Some(krate.name);
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-07-04 00:51:46 -07:00
|
|
|
// render the crate documentation
|
2014-03-05 15:28:08 -08:00
|
|
|
let mut work = vec!((self, item));
|
2013-12-23 16:20:52 +01:00
|
|
|
loop {
|
|
|
|
match work.pop() {
|
2014-02-19 10:07:49 -08:00
|
|
|
Some((mut cx, item)) => try!(cx.item(item, |cx, item| {
|
2013-12-23 16:20:52 +01:00
|
|
|
work.push((cx.clone(), item));
|
2014-01-30 11:30:21 -08:00
|
|
|
})),
|
2013-12-23 16:20:52 +01:00
|
|
|
None => break,
|
|
|
|
}
|
2013-12-18 12:26:19 -05:00
|
|
|
}
|
2014-07-04 00:51:46 -07:00
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-08-01 19:40:21 -04:00
|
|
|
/// Non-parallelized version of rendering an item. This will take the input
|
2013-10-03 10:24:40 -07:00
|
|
|
/// item, render its contents, and then invoke the specified closure with
|
|
|
|
/// all sub-items which need to be rendered.
|
|
|
|
///
|
|
|
|
/// The rendering driver uses this closure to queue up more work.
|
2015-02-26 21:00:43 -08:00
|
|
|
fn item<F>(&mut self, item: clean::Item, mut f: F) -> io::Result<()> where
|
2014-12-09 15:22:19 -05:00
|
|
|
F: FnMut(&mut Context, clean::Item),
|
|
|
|
{
|
2015-02-26 21:00:43 -08:00
|
|
|
fn render(w: File, cx: &Context, it: &clean::Item,
|
|
|
|
pushname: bool) -> io::Result<()> {
|
2013-09-18 22:18:38 -07:00
|
|
|
// A little unfortunate that this is done like this, but it sure
|
|
|
|
// does make formatting *a lot* nicer.
|
2014-11-14 14:20:57 -08:00
|
|
|
CURRENT_LOCATION_KEY.with(|slot| {
|
|
|
|
*slot.borrow_mut() = cx.current.clone();
|
|
|
|
});
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2015-07-10 08:19:21 -04:00
|
|
|
let mut title = cx.current.join("::");
|
2013-09-18 22:18:38 -07:00
|
|
|
if pushname {
|
2015-03-24 16:54:09 -07:00
|
|
|
if !title.is_empty() {
|
2014-04-02 16:54:22 -07:00
|
|
|
title.push_str("::");
|
|
|
|
}
|
2015-02-01 21:53:25 -05:00
|
|
|
title.push_str(it.name.as_ref().unwrap());
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
title.push_str(" - Rust");
|
2014-08-04 13:53:33 -07:00
|
|
|
let tyname = shortty(it).to_static_str();
|
|
|
|
let is_crate = match it.inner {
|
|
|
|
clean::ModuleItem(clean::Module { items: _, is_crate: true }) => true,
|
|
|
|
_ => false
|
|
|
|
};
|
|
|
|
let desc = if is_crate {
|
|
|
|
format!("API documentation for the Rust `{}` crate.",
|
|
|
|
cx.layout.krate)
|
|
|
|
} else {
|
|
|
|
format!("API documentation for the Rust `{}` {} in crate `{}`.",
|
2014-10-14 23:05:01 -07:00
|
|
|
it.name.as_ref().unwrap(), tyname, cx.layout.krate)
|
2014-08-04 13:53:33 -07:00
|
|
|
};
|
2014-08-04 14:30:06 -07:00
|
|
|
let keywords = make_item_keywords(it);
|
2013-09-18 22:18:38 -07:00
|
|
|
let page = layout::Page {
|
2014-08-04 13:53:33 -07:00
|
|
|
ty: tyname,
|
2015-02-01 21:53:25 -05:00
|
|
|
root_path: &cx.root_path,
|
|
|
|
title: &title,
|
|
|
|
description: &desc,
|
|
|
|
keywords: &keywords,
|
2013-09-18 22:18:38 -07:00
|
|
|
};
|
|
|
|
|
2014-03-04 11:24:20 -08:00
|
|
|
markdown::reset_headers();
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
// We have a huge number of calls to write, so try to alleviate some
|
|
|
|
// of the pain by using a buffered writer instead of invoking the
|
2014-09-02 01:35:58 -04:00
|
|
|
// write syscall all the time.
|
2015-02-26 21:00:43 -08:00
|
|
|
let mut writer = BufWriter::new(w);
|
2014-05-29 13:50:47 -07:00
|
|
|
if !cx.render_redirect_pages {
|
|
|
|
try!(layout::render(&mut writer, &cx.layout, &page,
|
|
|
|
&Sidebar{ cx: cx, item: it },
|
|
|
|
&Item{ cx: cx, item: it }));
|
|
|
|
} else {
|
2014-12-10 19:46:38 -08:00
|
|
|
let mut url = repeat("../").take(cx.current.len())
|
|
|
|
.collect::<String>();
|
2014-11-14 14:20:57 -08:00
|
|
|
match cache().paths.get(&it.def_id) {
|
2014-05-29 13:50:47 -07:00
|
|
|
Some(&(ref names, _)) => {
|
2015-01-31 12:20:46 -05:00
|
|
|
for name in &names[..names.len() - 1] {
|
2015-02-01 21:53:25 -05:00
|
|
|
url.push_str(name);
|
2014-05-29 13:50:47 -07:00
|
|
|
url.push_str("/");
|
|
|
|
}
|
2015-02-01 21:53:25 -05:00
|
|
|
url.push_str(&item_path(it));
|
|
|
|
try!(layout::redirect(&mut writer, &url));
|
2014-05-29 13:50:47 -07:00
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
writer.flush()
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-06-03 17:55:30 -07:00
|
|
|
// Private modules may survive the strip-private pass if they
|
|
|
|
// contain impls for public types. These modules can also
|
|
|
|
// contain items such as publicly reexported structures.
|
|
|
|
//
|
|
|
|
// External crates will provide links to these structures, so
|
|
|
|
// these modules are recursed into, but not rendered normally (a
|
|
|
|
// flag on the context).
|
|
|
|
if !self.render_redirect_pages {
|
2014-11-09 19:09:17 -05:00
|
|
|
self.render_redirect_pages = self.ignore_private_item(&item);
|
2014-06-03 17:55:30 -07:00
|
|
|
}
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
match item.inner {
|
2013-09-24 13:56:52 -07:00
|
|
|
// modules are special because they add a namespace. We also need to
|
|
|
|
// recurse into the items of the module as well.
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::ModuleItem(..) => {
|
2014-08-18 17:52:38 -07:00
|
|
|
let name = item.name.as_ref().unwrap().to_string();
|
2013-12-04 21:28:47 -08:00
|
|
|
let mut item = Some(item);
|
2013-11-21 15:42:55 -08:00
|
|
|
self.recurse(name, |this| {
|
2014-08-18 17:52:38 -07:00
|
|
|
let item = item.take().unwrap();
|
2013-10-05 19:49:32 -07:00
|
|
|
let dst = this.dst.join("index.html");
|
2014-02-19 10:07:49 -08:00
|
|
|
let dst = try!(File::create(&dst));
|
|
|
|
try!(render(dst, this, &item, false));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
let m = match item.inner {
|
|
|
|
clean::ModuleItem(m) => m,
|
|
|
|
_ => unreachable!()
|
|
|
|
};
|
2015-03-05 16:35:43 +09:00
|
|
|
|
|
|
|
// render sidebar-items.js used throughout this module
|
|
|
|
{
|
|
|
|
let items = this.build_sidebar_items(&m);
|
|
|
|
let js_dst = this.dst.join("sidebar-items.js");
|
2015-03-06 11:03:51 +05:30
|
|
|
let mut js_out = BufWriter::new(try!(File::create(&js_dst)));
|
2015-03-05 16:35:43 +09:00
|
|
|
try!(write!(&mut js_out, "initSidebarItems({});",
|
|
|
|
json::as_json(&items)));
|
|
|
|
}
|
|
|
|
|
2015-01-31 20:03:04 -05:00
|
|
|
for item in m.items {
|
2013-12-18 12:26:19 -05:00
|
|
|
f(this,item);
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-11-21 15:42:55 -08:00
|
|
|
})
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-09-24 13:56:52 -07:00
|
|
|
|
|
|
|
// Things which don't have names (like impls) don't get special
|
|
|
|
// pages dedicated to them.
|
2013-09-18 22:18:38 -07:00
|
|
|
_ if item.name.is_some() => {
|
2015-02-26 21:00:43 -08:00
|
|
|
let dst = self.dst.join(&item_path(&item));
|
2014-02-19 10:07:49 -08:00
|
|
|
let dst = try!(File::create(&dst));
|
2014-01-30 11:30:21 -08:00
|
|
|
render(dst, self, &item, true)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-09-24 13:56:52 -07:00
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
_ => Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-11-09 19:09:17 -05:00
|
|
|
|
2015-04-16 12:14:45 +02:00
|
|
|
fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> {
|
|
|
|
// BTreeMap instead of HashMap to get a sorted output
|
|
|
|
let mut map = BTreeMap::new();
|
2015-01-31 12:20:46 -05:00
|
|
|
for item in &m.items {
|
2014-11-09 19:09:17 -05:00
|
|
|
if self.ignore_private_item(item) { continue }
|
|
|
|
|
|
|
|
let short = shortty(item).to_static_str();
|
|
|
|
let myname = match item.name {
|
|
|
|
None => continue,
|
|
|
|
Some(ref s) => s.to_string(),
|
|
|
|
};
|
2015-01-04 14:07:32 -05:00
|
|
|
let short = short.to_string();
|
2015-03-20 13:43:01 -04:00
|
|
|
map.entry(short).or_insert(vec![])
|
2015-03-01 09:42:11 -05:00
|
|
|
.push((myname, Some(plain_summary_line(item.doc_value()))));
|
2014-11-09 19:09:17 -05:00
|
|
|
}
|
|
|
|
|
2015-01-31 20:02:00 -05:00
|
|
|
for (_, items) in &mut map {
|
2014-11-27 16:49:29 -05:00
|
|
|
items.sort();
|
2014-11-09 19:09:17 -05:00
|
|
|
}
|
|
|
|
return map;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ignore_private_item(&self, it: &clean::Item) -> bool {
|
|
|
|
match it.inner {
|
|
|
|
clean::ModuleItem(ref m) => {
|
2015-03-24 16:53:34 -07:00
|
|
|
(m.items.is_empty() &&
|
2015-04-06 16:43:55 -07:00
|
|
|
it.doc_value().is_none() &&
|
|
|
|
it.visibility != Some(ast::Public)) ||
|
2014-11-09 19:09:17 -05:00
|
|
|
(self.passes.contains("strip-private") && it.visibility != Some(ast::Public))
|
|
|
|
}
|
|
|
|
clean::PrimitiveItem(..) => it.visibility != Some(ast::Public),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-12-09 23:16:18 -08:00
|
|
|
impl<'a> Item<'a> {
|
2013-09-18 22:18:38 -07:00
|
|
|
fn ismodule(&self) -> bool {
|
|
|
|
match self.item.inner {
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::ModuleItem(..) => true, _ => false
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-05-02 17:08:11 -07:00
|
|
|
|
2014-05-24 11:56:38 -07:00
|
|
|
/// Generate a url appropriate for an `href` attribute back to the source of
|
|
|
|
/// this item.
|
|
|
|
///
|
|
|
|
/// The url generated, when clicked, will redirect the browser back to the
|
|
|
|
/// original source code.
|
|
|
|
///
|
|
|
|
/// If `None` is returned, then a source link couldn't be generated. This
|
|
|
|
/// may happen, for example, with externally inlined items where the source
|
|
|
|
/// of their crate documentation isn't known.
|
2014-12-01 20:46:51 +09:00
|
|
|
fn href(&self, cx: &Context) -> Option<String> {
|
2015-04-13 15:25:40 -07:00
|
|
|
let href = if self.item.source.loline == self.item.source.hiline {
|
|
|
|
format!("{}", self.item.source.loline)
|
|
|
|
} else {
|
|
|
|
format!("{}-{}", self.item.source.loline, self.item.source.hiline)
|
|
|
|
};
|
|
|
|
|
|
|
|
// First check to see if this is an imported macro source. In this case
|
|
|
|
// we need to handle it specially as cross-crate inlined macros have...
|
|
|
|
// odd locations!
|
|
|
|
let imported_macro_from = match self.item.inner {
|
|
|
|
clean::MacroItem(ref m) => m.imported_from.as_ref(),
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
if let Some(krate) = imported_macro_from {
|
|
|
|
let cache = cache();
|
|
|
|
let root = cache.extern_locations.values().find(|&&(ref n, _)| {
|
|
|
|
*krate == *n
|
|
|
|
}).map(|l| &l.1);
|
|
|
|
let root = match root {
|
|
|
|
Some(&Remote(ref s)) => s.to_string(),
|
|
|
|
Some(&Local) => self.cx.root_path.clone(),
|
|
|
|
None | Some(&Unknown) => return None,
|
|
|
|
};
|
|
|
|
Some(format!("{root}/{krate}/macro.{name}.html?gotomacrosrc=1",
|
|
|
|
root = root,
|
|
|
|
krate = krate,
|
|
|
|
name = self.item.name.as_ref().unwrap()))
|
|
|
|
|
2014-05-24 11:56:38 -07:00
|
|
|
// If this item is part of the local crate, then we're guaranteed to
|
|
|
|
// know the span, so we plow forward and generate a proper url. The url
|
|
|
|
// has anchors for the line numbers that we're linking to.
|
2015-04-13 15:25:40 -07:00
|
|
|
} else if ast_util::is_local(self.item.def_id) {
|
2014-05-23 20:17:27 -07:00
|
|
|
let mut path = Vec::new();
|
2015-02-26 21:00:43 -08:00
|
|
|
clean_srcpath(&cx.src_root, Path::new(&self.item.source.filename),
|
2015-03-16 19:44:13 -07:00
|
|
|
true, |component| {
|
2014-05-25 03:10:11 -07:00
|
|
|
path.push(component.to_string());
|
2014-05-23 20:17:27 -07:00
|
|
|
});
|
2014-05-28 09:24:28 -07:00
|
|
|
Some(format!("{root}src/{krate}/{path}.html#{href}",
|
2014-05-23 20:17:27 -07:00
|
|
|
root = self.cx.root_path,
|
|
|
|
krate = self.cx.layout.krate,
|
2015-07-10 08:19:21 -04:00
|
|
|
path = path.join("/"),
|
2014-05-23 20:17:27 -07:00
|
|
|
href = href))
|
2014-05-24 11:56:38 -07:00
|
|
|
|
|
|
|
// If this item is not part of the local crate, then things get a little
|
|
|
|
// trickier. We don't actually know the span of the external item, but
|
|
|
|
// we know that the documentation on the other end knows the span!
|
|
|
|
//
|
|
|
|
// In this case, we generate a link to the *documentation* for this type
|
|
|
|
// in the original crate. There's an extra URL parameter which says that
|
|
|
|
// we want to go somewhere else, and the JS on the destination page will
|
|
|
|
// pick it up and instantly redirect the browser to the source code.
|
|
|
|
//
|
|
|
|
// If we don't know where the external documentation for this crate is
|
|
|
|
// located, then we return `None`.
|
2014-05-02 17:08:11 -07:00
|
|
|
} else {
|
2014-11-14 14:20:57 -08:00
|
|
|
let cache = cache();
|
2015-03-21 21:15:47 -04:00
|
|
|
let path = &cache.external_paths[&self.item.def_id];
|
|
|
|
let root = match cache.extern_locations[&self.item.def_id.krate] {
|
2015-04-13 15:25:40 -07:00
|
|
|
(_, Remote(ref s)) => s.to_string(),
|
|
|
|
(_, Local) => self.cx.root_path.clone(),
|
|
|
|
(_, Unknown) => return None,
|
2014-05-23 20:17:27 -07:00
|
|
|
};
|
2014-05-27 22:52:00 +10:00
|
|
|
Some(format!("{root}{path}/{file}?gotosrc={goto}",
|
2014-05-23 20:17:27 -07:00
|
|
|
root = root,
|
2015-07-10 08:19:21 -04:00
|
|
|
path = path[..path.len() - 1].join("/"),
|
2014-05-23 20:17:27 -07:00
|
|
|
file = item_path(self.item),
|
|
|
|
goto = self.item.def_id.node))
|
|
|
|
}
|
2014-05-02 17:08:11 -07:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-07-04 00:51:46 -07:00
|
|
|
|
2015-01-20 15:45:07 -08:00
|
|
|
impl<'a> fmt::Display for Item<'a> {
|
2014-02-05 23:55:13 +11:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
2013-09-18 22:18:38 -07:00
|
|
|
// Write the breadcrumb trail header for the top
|
2014-09-24 12:47:25 +08:00
|
|
|
try!(write!(fmt, "\n<h1 class='fqn'><span class='in-band'>"));
|
2014-02-05 23:55:13 +11:00
|
|
|
match self.item.inner {
|
2014-02-28 22:33:45 +01:00
|
|
|
clean::ModuleItem(ref m) => if m.is_crate {
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "Crate "));
|
2014-02-28 22:33:45 +01:00
|
|
|
} else {
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "Module "));
|
2014-02-28 22:33:45 +01:00
|
|
|
},
|
2014-05-10 14:05:06 -07:00
|
|
|
clean::FunctionItem(..) => try!(write!(fmt, "Function ")),
|
|
|
|
clean::TraitItem(..) => try!(write!(fmt, "Trait ")),
|
|
|
|
clean::StructItem(..) => try!(write!(fmt, "Struct ")),
|
|
|
|
clean::EnumItem(..) => try!(write!(fmt, "Enum ")),
|
2014-05-28 19:53:37 -07:00
|
|
|
clean::PrimitiveItem(..) => try!(write!(fmt, "Primitive Type ")),
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {}
|
|
|
|
}
|
2014-05-28 19:53:37 -07:00
|
|
|
let is_primitive = match self.item.inner {
|
|
|
|
clean::PrimitiveItem(..) => true,
|
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
if !is_primitive {
|
2015-02-01 21:53:25 -05:00
|
|
|
let cur = &self.cx.current;
|
2014-05-28 19:53:37 -07:00
|
|
|
let amt = if self.ismodule() { cur.len() - 1 } else { cur.len() };
|
|
|
|
for (i, component) in cur.iter().enumerate().take(amt) {
|
2014-08-17 19:28:20 +01:00
|
|
|
try!(write!(fmt, "<a href='{}index.html'>{}</a>::<wbr>",
|
2014-12-10 19:46:38 -08:00
|
|
|
repeat("../").take(cur.len() - i - 1)
|
|
|
|
.collect::<String>(),
|
2015-02-01 21:53:25 -05:00
|
|
|
component));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<a class='{}' href=''>{}</a>",
|
2015-02-01 21:53:25 -05:00
|
|
|
shortty(self.item), self.item.name.as_ref().unwrap()));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-09-24 12:47:25 +08:00
|
|
|
try!(write!(fmt, "</span>")); // in-band
|
|
|
|
try!(write!(fmt, "<span class='out-of-band'>"));
|
2014-07-30 21:31:34 -04:00
|
|
|
try!(write!(fmt,
|
|
|
|
r##"<span id='render-detail'>
|
2015-05-07 03:53:21 -04:00
|
|
|
<a id="toggle-all-docs" href="javascript:void(0)" title="collapse all docs">
|
|
|
|
[<span class='inner'>−</span>]
|
|
|
|
</a>
|
2014-07-30 21:31:34 -04:00
|
|
|
</span>"##));
|
|
|
|
|
2014-04-25 17:34:32 +09:00
|
|
|
// Write `src` tag
|
2014-05-24 11:56:38 -07:00
|
|
|
//
|
|
|
|
// When this item is part of a `pub use` in a downstream crate, the
|
|
|
|
// [src] link in the downstream documentation will actually come back to
|
|
|
|
// this page, and this link will be auto-clicked. The `id` attribute is
|
|
|
|
// used to find the link to auto-click.
|
2014-05-28 19:53:37 -07:00
|
|
|
if self.cx.include_sources && !is_primitive {
|
2014-12-01 20:46:51 +09:00
|
|
|
match self.href(self.cx) {
|
2014-05-23 20:17:27 -07:00
|
|
|
Some(l) => {
|
2015-04-13 15:25:40 -07:00
|
|
|
try!(write!(fmt, "<a id='src-{}' class='srclink' \
|
2015-04-23 00:50:19 +08:00
|
|
|
href='{}' title='{}'>[src]</a>",
|
|
|
|
self.item.def_id.node, l, "goto source code"));
|
2014-05-23 20:17:27 -07:00
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2014-04-12 21:39:12 +02:00
|
|
|
}
|
2014-07-04 00:51:46 -07:00
|
|
|
|
2014-09-24 12:47:25 +08:00
|
|
|
try!(write!(fmt, "</span>")); // out-of-band
|
2014-07-04 00:51:46 -07:00
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "</h1>\n"));
|
2014-04-12 21:39:12 +02:00
|
|
|
|
2014-02-05 23:55:13 +11:00
|
|
|
match self.item.inner {
|
2014-03-05 15:28:08 -08:00
|
|
|
clean::ModuleItem(ref m) => {
|
2015-02-01 21:53:25 -05:00
|
|
|
item_module(fmt, self.cx, self.item, &m.items)
|
2014-03-05 15:28:08 -08:00
|
|
|
}
|
2013-09-26 11:57:25 -07:00
|
|
|
clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) =>
|
2014-05-10 14:05:06 -07:00
|
|
|
item_function(fmt, self.item, f),
|
2014-05-21 16:41:58 -07:00
|
|
|
clean::TraitItem(ref t) => item_trait(fmt, self.cx, self.item, t),
|
2014-05-10 14:05:06 -07:00
|
|
|
clean::StructItem(ref s) => item_struct(fmt, self.item, s),
|
|
|
|
clean::EnumItem(ref e) => item_enum(fmt, self.item, e),
|
2015-05-21 14:17:37 +02:00
|
|
|
clean::TypedefItem(ref t, _) => item_typedef(fmt, self.item, t),
|
2014-05-10 14:05:06 -07:00
|
|
|
clean::MacroItem(ref m) => item_macro(fmt, self.item, m),
|
2014-05-28 19:53:37 -07:00
|
|
|
clean::PrimitiveItem(ref p) => item_primitive(fmt, self.item, p),
|
2014-12-04 00:56:45 +09:00
|
|
|
clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) =>
|
|
|
|
item_static(fmt, self.item, i),
|
2014-11-22 20:48:55 +13:00
|
|
|
clean::ConstantItem(ref c) => item_constant(fmt, self.item, c),
|
2014-01-30 11:30:21 -08:00
|
|
|
_ => Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-22 16:57:53 -07:00
|
|
|
fn item_path(item: &clean::Item) -> String {
|
2013-09-18 22:18:38 -07:00
|
|
|
match item.inner {
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::ModuleItem(..) => {
|
2014-10-14 23:05:01 -07:00
|
|
|
format!("{}/index.html", item.name.as_ref().unwrap())
|
2014-05-12 13:44:59 -07:00
|
|
|
}
|
|
|
|
_ => {
|
2014-05-27 20:44:58 -07:00
|
|
|
format!("{}.{}.html",
|
|
|
|
shortty(item).to_static_str(),
|
2014-10-14 23:05:01 -07:00
|
|
|
*item.name.as_ref().unwrap())
|
2014-05-12 13:44:59 -07:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-22 16:57:53 -07:00
|
|
|
fn full_path(cx: &Context, item: &clean::Item) -> String {
|
2015-07-10 08:19:21 -04:00
|
|
|
let mut s = cx.current.join("::");
|
2013-09-18 22:18:38 -07:00
|
|
|
s.push_str("::");
|
2015-02-01 21:53:25 -05:00
|
|
|
s.push_str(item.name.as_ref().unwrap());
|
2014-05-12 13:44:59 -07:00
|
|
|
return s
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-03-14 00:45:39 +02:00
|
|
|
fn shorter<'a>(s: Option<&'a str>) -> String {
|
2013-09-18 22:18:38 -07:00
|
|
|
match s {
|
2015-03-14 00:45:39 +02:00
|
|
|
Some(s) => s.lines().take_while(|line|{
|
|
|
|
(*line).chars().any(|chr|{
|
|
|
|
!chr.is_whitespace()
|
|
|
|
})
|
2015-07-10 08:19:21 -04:00
|
|
|
}).collect::<Vec<_>>().join("\n"),
|
2015-03-14 00:45:39 +02:00
|
|
|
None => "".to_string()
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-23 09:58:38 +08:00
|
|
|
#[inline]
|
2015-03-10 20:55:09 +08:00
|
|
|
fn plain_summary_line(s: Option<&str>) -> String {
|
|
|
|
let line = shorter(s).replace("\n", " ");
|
|
|
|
markdown::plain_summary_line(&line[..])
|
2014-12-23 09:58:38 +08:00
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
|
2015-04-13 11:55:00 -07:00
|
|
|
if let Some(s) = short_stability(item, true) {
|
|
|
|
try!(write!(w, "<div class='stability'>{}</div>", s));
|
|
|
|
}
|
|
|
|
if let Some(s) = item.doc_value() {
|
|
|
|
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn item_module(w: &mut fmt::Formatter, cx: &Context,
|
2014-01-30 11:30:21 -08:00
|
|
|
item: &clean::Item, items: &[clean::Item]) -> fmt::Result {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, item));
|
2014-07-04 00:51:46 -07:00
|
|
|
|
2015-01-26 16:05:07 -05:00
|
|
|
let mut indices = (0..items.len()).filter(|i| {
|
2014-11-09 19:09:17 -05:00
|
|
|
!cx.ignore_private_item(&items[*i])
|
2015-03-25 17:06:52 -07:00
|
|
|
}).collect::<Vec<usize>>();
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-12-03 23:57:45 +09:00
|
|
|
// the order of item types in the listing
|
|
|
|
fn reorder(ty: ItemType) -> u8 {
|
|
|
|
match ty {
|
2014-12-26 10:55:16 +02:00
|
|
|
ItemType::ExternCrate => 0,
|
|
|
|
ItemType::Import => 1,
|
|
|
|
ItemType::Primitive => 2,
|
|
|
|
ItemType::Module => 3,
|
|
|
|
ItemType::Macro => 4,
|
|
|
|
ItemType::Struct => 5,
|
|
|
|
ItemType::Enum => 6,
|
|
|
|
ItemType::Constant => 7,
|
|
|
|
ItemType::Static => 8,
|
|
|
|
ItemType::Trait => 9,
|
|
|
|
ItemType::Function => 10,
|
|
|
|
ItemType::Typedef => 12,
|
|
|
|
_ => 13 + ty as u8,
|
2014-12-03 23:57:45 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-25 17:06:52 -07:00
|
|
|
fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: usize, idx2: usize) -> Ordering {
|
2014-12-03 23:57:45 +09:00
|
|
|
let ty1 = shortty(i1);
|
|
|
|
let ty2 = shortty(i2);
|
2015-04-13 11:55:00 -07:00
|
|
|
if ty1 != ty2 {
|
|
|
|
return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2))
|
|
|
|
}
|
|
|
|
let s1 = i1.stability.as_ref().map(|s| s.level);
|
|
|
|
let s2 = i2.stability.as_ref().map(|s| s.level);
|
|
|
|
match (s1, s2) {
|
|
|
|
(Some(attr::Unstable), Some(attr::Stable)) => return Ordering::Greater,
|
|
|
|
(Some(attr::Stable), Some(attr::Unstable)) => return Ordering::Less,
|
|
|
|
_ => {}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-04-13 11:55:00 -07:00
|
|
|
i1.name.cmp(&i2.name)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-12-20 14:42:00 +11:00
|
|
|
indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-12-20 00:09:35 -08:00
|
|
|
debug!("{:?}", indices);
|
2014-04-09 16:49:31 +09:00
|
|
|
let mut curty = None;
|
2015-01-31 12:20:46 -05:00
|
|
|
for &idx in &indices {
|
2013-09-18 22:18:38 -07:00
|
|
|
let myitem = &items[idx];
|
|
|
|
|
2014-04-09 16:49:31 +09:00
|
|
|
let myty = Some(shortty(myitem));
|
2014-12-26 10:55:16 +02:00
|
|
|
if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) {
|
|
|
|
// Put `extern crate` and `use` re-exports in the same section.
|
|
|
|
curty = myty;
|
|
|
|
} else if myty != curty {
|
2014-04-09 16:49:31 +09:00
|
|
|
if curty.is_some() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</table>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
curty = myty;
|
2014-12-03 23:57:45 +09:00
|
|
|
let (short, name) = match myty.unwrap() {
|
2014-12-26 10:55:16 +02:00
|
|
|
ItemType::ExternCrate |
|
|
|
|
ItemType::Import => ("reexports", "Reexports"),
|
2014-12-03 23:57:45 +09:00
|
|
|
ItemType::Module => ("modules", "Modules"),
|
|
|
|
ItemType::Struct => ("structs", "Structs"),
|
|
|
|
ItemType::Enum => ("enums", "Enums"),
|
|
|
|
ItemType::Function => ("functions", "Functions"),
|
|
|
|
ItemType::Typedef => ("types", "Type Definitions"),
|
|
|
|
ItemType::Static => ("statics", "Statics"),
|
|
|
|
ItemType::Constant => ("constants", "Constants"),
|
|
|
|
ItemType::Trait => ("traits", "Traits"),
|
|
|
|
ItemType::Impl => ("impls", "Implementations"),
|
|
|
|
ItemType::TyMethod => ("tymethods", "Type Methods"),
|
|
|
|
ItemType::Method => ("methods", "Methods"),
|
|
|
|
ItemType::StructField => ("fields", "Struct Fields"),
|
|
|
|
ItemType::Variant => ("variants", "Variants"),
|
|
|
|
ItemType::Macro => ("macros", "Macros"),
|
|
|
|
ItemType::Primitive => ("primitives", "Primitive Types"),
|
|
|
|
ItemType::AssociatedType => ("associated-types", "Associated Types"),
|
2015-03-14 12:05:00 -06:00
|
|
|
ItemType::AssociatedConst => ("associated-consts", "Associated Constants"),
|
2014-03-04 11:24:20 -08:00
|
|
|
};
|
2014-03-12 23:09:03 +11:00
|
|
|
try!(write!(w,
|
2014-04-26 21:09:19 +09:00
|
|
|
"<h2 id='{id}' class='section-header'>\
|
2014-05-28 09:24:28 -07:00
|
|
|
<a href=\"#{id}\">{name}</a></h2>\n<table>",
|
2014-03-08 01:24:54 +11:00
|
|
|
id = short, name = name));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-10-06 17:41:15 -07:00
|
|
|
match myitem.inner {
|
2014-12-26 10:55:16 +02:00
|
|
|
clean::ExternCrateItem(ref name, ref src) => {
|
|
|
|
match *src {
|
|
|
|
Some(ref src) => {
|
2015-05-08 20:48:54 -04:00
|
|
|
try!(write!(w, "<tr><td><code>{}extern crate {} as {};",
|
2014-12-26 10:55:16 +02:00
|
|
|
VisSpace(myitem.visibility),
|
2015-02-01 21:53:25 -05:00
|
|
|
src,
|
|
|
|
name))
|
2013-09-24 13:56:52 -07:00
|
|
|
}
|
2014-12-26 10:55:16 +02:00
|
|
|
None => {
|
|
|
|
try!(write!(w, "<tr><td><code>{}extern crate {};",
|
2015-02-01 21:53:25 -05:00
|
|
|
VisSpace(myitem.visibility), name))
|
2013-09-24 13:56:52 -07:00
|
|
|
}
|
|
|
|
}
|
2014-12-26 10:55:16 +02:00
|
|
|
try!(write!(w, "</code></td></tr>"));
|
|
|
|
}
|
2013-09-24 13:56:52 -07:00
|
|
|
|
2014-12-26 10:55:16 +02:00
|
|
|
clean::ImportItem(ref import) => {
|
|
|
|
try!(write!(w, "<tr><td><code>{}{}</code></td></tr>",
|
|
|
|
VisSpace(myitem.visibility), *import));
|
2013-09-24 13:56:52 -07:00
|
|
|
}
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {
|
2013-10-01 14:31:03 -07:00
|
|
|
if myitem.name.is_none() { continue }
|
2015-04-13 11:55:00 -07:00
|
|
|
let stab_docs = if let Some(s) = short_stability(myitem, false) {
|
|
|
|
format!("[{}]", s)
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
};
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "
|
2015-04-13 11:55:00 -07:00
|
|
|
<tr class='{stab} module-item'>
|
|
|
|
<td><a class='{class}' href='{href}'
|
|
|
|
title='{title}'>{name}</a></td>
|
|
|
|
<td class='docblock short'>
|
|
|
|
{stab_docs} {docs}
|
|
|
|
</td>
|
2013-09-18 22:18:38 -07:00
|
|
|
</tr>
|
|
|
|
",
|
2015-04-13 11:55:00 -07:00
|
|
|
name = *myitem.name.as_ref().unwrap(),
|
|
|
|
stab_docs = stab_docs,
|
|
|
|
docs = Markdown(&shorter(myitem.doc_value())),
|
2013-09-18 22:18:38 -07:00
|
|
|
class = shortty(myitem),
|
2015-04-13 11:55:00 -07:00
|
|
|
stab = myitem.stability_class(),
|
2013-09-18 22:18:38 -07:00
|
|
|
href = item_path(myitem),
|
2015-04-13 11:55:00 -07:00
|
|
|
title = full_path(cx, myitem)));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-04 00:51:46 -07:00
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
write!(w, "</table>")
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-04-13 11:55:00 -07:00
|
|
|
fn short_stability(item: &clean::Item, show_reason: bool) -> Option<String> {
|
|
|
|
item.stability.as_ref().and_then(|stab| {
|
2015-04-13 16:23:32 -07:00
|
|
|
let reason = if show_reason && !stab.reason.is_empty() {
|
2015-04-13 11:55:00 -07:00
|
|
|
format!(": {}", stab.reason)
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
};
|
2015-04-13 16:23:32 -07:00
|
|
|
let text = if !stab.deprecated_since.is_empty() {
|
2015-04-13 11:55:00 -07:00
|
|
|
let since = if show_reason {
|
|
|
|
format!(" since {}", Escape(&stab.deprecated_since))
|
|
|
|
} else {
|
|
|
|
String::new()
|
|
|
|
};
|
|
|
|
format!("Deprecated{}{}", since, Markdown(&reason))
|
|
|
|
} else if stab.level == attr::Unstable {
|
|
|
|
format!("Unstable{}", Markdown(&reason))
|
|
|
|
} else {
|
|
|
|
return None
|
|
|
|
};
|
|
|
|
Some(format!("<em class='stab {}'>{}</em>",
|
|
|
|
item.stability_class(), text))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2014-11-22 20:48:55 +13:00
|
|
|
struct Initializer<'a>(&'a str);
|
2014-12-20 00:09:35 -08:00
|
|
|
|
2015-01-20 15:45:07 -08:00
|
|
|
impl<'a> fmt::Display for Initializer<'a> {
|
2014-11-22 20:48:55 +13:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
let Initializer(s) = *self;
|
2015-03-24 16:53:34 -07:00
|
|
|
if s.is_empty() { return Ok(()); }
|
2014-11-22 20:48:55 +13:00
|
|
|
try!(write!(f, "<code> = </code>"));
|
2015-02-01 21:53:25 -05:00
|
|
|
write!(f, "<code>{}</code>", s)
|
2014-11-22 20:48:55 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_constant(w: &mut fmt::Formatter, it: &clean::Item,
|
|
|
|
c: &clean::Constant) -> fmt::Result {
|
|
|
|
try!(write!(w, "<pre class='rust const'>{vis}const \
|
|
|
|
{name}: {typ}{init}</pre>",
|
|
|
|
vis = VisSpace(it.visibility),
|
2015-02-01 21:53:25 -05:00
|
|
|
name = it.name.as_ref().unwrap(),
|
2014-11-22 20:48:55 +13:00
|
|
|
typ = c.type_,
|
2015-02-01 21:53:25 -05:00
|
|
|
init = Initializer(&c.expr)));
|
2014-11-22 20:48:55 +13:00
|
|
|
document(w, it)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_static(w: &mut fmt::Formatter, it: &clean::Item,
|
|
|
|
s: &clean::Static) -> fmt::Result {
|
|
|
|
try!(write!(w, "<pre class='rust static'>{vis}static {mutability}\
|
|
|
|
{name}: {typ}{init}</pre>",
|
|
|
|
vis = VisSpace(it.visibility),
|
|
|
|
mutability = MutableSpace(s.mutability),
|
2015-02-01 21:53:25 -05:00
|
|
|
name = it.name.as_ref().unwrap(),
|
2014-11-22 20:48:55 +13:00
|
|
|
typ = s.type_,
|
2015-02-01 21:53:25 -05:00
|
|
|
init = Initializer(&s.expr)));
|
2014-11-22 20:48:55 +13:00
|
|
|
document(w, it)
|
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn item_function(w: &mut fmt::Formatter, it: &clean::Item,
|
2014-01-30 11:30:21 -08:00
|
|
|
f: &clean::Function) -> fmt::Result {
|
2015-02-25 22:05:07 +02:00
|
|
|
try!(write!(w, "<pre class='rust fn'>{vis}{unsafety}{abi}{constness}fn \
|
2014-09-25 02:01:42 -07:00
|
|
|
{name}{generics}{decl}{where_clause}</pre>",
|
2013-09-18 22:18:38 -07:00
|
|
|
vis = VisSpace(it.visibility),
|
2014-12-09 10:36:46 -05:00
|
|
|
unsafety = UnsafetySpace(f.unsafety),
|
2015-04-07 14:22:55 -07:00
|
|
|
abi = AbiSpace(f.abi),
|
2015-02-25 22:05:07 +02:00
|
|
|
constness = ConstnessSpace(f.constness),
|
2015-02-01 21:53:25 -05:00
|
|
|
name = it.name.as_ref().unwrap(),
|
2013-09-18 22:18:38 -07:00
|
|
|
generics = f.generics,
|
2014-09-25 02:01:42 -07:00
|
|
|
where_clause = WhereClause(&f.generics),
|
2014-01-30 11:30:21 -08:00
|
|
|
decl = f.decl));
|
|
|
|
document(w, it)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-05-21 16:41:58 -07:00
|
|
|
fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
|
2014-01-30 11:30:21 -08:00
|
|
|
t: &clean::Trait) -> fmt::Result {
|
2014-08-27 21:46:52 -04:00
|
|
|
let mut bounds = String::new();
|
2015-03-24 16:54:09 -07:00
|
|
|
if !t.bounds.is_empty() {
|
|
|
|
if !bounds.is_empty() {
|
2014-11-24 10:14:46 -08:00
|
|
|
bounds.push(' ');
|
|
|
|
}
|
2014-08-27 21:46:52 -04:00
|
|
|
bounds.push_str(": ");
|
|
|
|
for (i, p) in t.bounds.iter().enumerate() {
|
|
|
|
if i > 0 { bounds.push_str(" + "); }
|
2015-02-01 21:53:25 -05:00
|
|
|
bounds.push_str(&format!("{}", *p));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the trait definition
|
2014-12-09 19:59:20 -05:00
|
|
|
try!(write!(w, "<pre class='rust trait'>{}{}trait {}{}{}{} ",
|
2014-01-30 11:30:21 -08:00
|
|
|
VisSpace(it.visibility),
|
2014-12-09 19:59:20 -05:00
|
|
|
UnsafetySpace(t.unsafety),
|
2015-02-01 21:53:25 -05:00
|
|
|
it.name.as_ref().unwrap(),
|
2014-01-30 11:30:21 -08:00
|
|
|
t.generics,
|
2014-09-25 02:01:42 -07:00
|
|
|
bounds,
|
|
|
|
WhereClause(&t.generics)));
|
2014-11-20 11:22:09 -08:00
|
|
|
|
2015-03-10 12:28:44 +02:00
|
|
|
let types = t.items.iter().filter(|m| {
|
|
|
|
match m.inner { clean::AssociatedTypeItem(..) => true, _ => false }
|
|
|
|
}).collect::<Vec<_>>();
|
2015-04-30 09:37:13 -07:00
|
|
|
let consts = t.items.iter().filter(|m| {
|
|
|
|
match m.inner { clean::AssociatedConstItem(..) => true, _ => false }
|
|
|
|
}).collect::<Vec<_>>();
|
2015-03-10 12:28:44 +02:00
|
|
|
let required = t.items.iter().filter(|m| {
|
|
|
|
match m.inner { clean::TyMethodItem(_) => true, _ => false }
|
|
|
|
}).collect::<Vec<_>>();
|
|
|
|
let provided = t.items.iter().filter(|m| {
|
|
|
|
match m.inner { clean::MethodItem(_) => true, _ => false }
|
|
|
|
}).collect::<Vec<_>>();
|
2014-08-04 13:56:56 -07:00
|
|
|
|
2015-03-24 16:53:34 -07:00
|
|
|
if t.items.is_empty() {
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, "{{ }}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
} else {
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, "{{\n"));
|
2015-01-31 12:20:46 -05:00
|
|
|
for t in &types {
|
2014-11-20 11:22:09 -08:00
|
|
|
try!(write!(w, " "));
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_item(w, t, AssocItemLink::Anchor));
|
2014-11-20 11:22:09 -08:00
|
|
|
try!(write!(w, ";\n"));
|
|
|
|
}
|
2015-04-30 09:37:13 -07:00
|
|
|
if !types.is_empty() && !consts.is_empty() {
|
|
|
|
try!(w.write_str("\n"));
|
|
|
|
}
|
|
|
|
for t in &consts {
|
|
|
|
try!(write!(w, " "));
|
|
|
|
try!(render_assoc_item(w, t, AssocItemLink::Anchor));
|
|
|
|
try!(write!(w, ";\n"));
|
|
|
|
}
|
|
|
|
if !consts.is_empty() && !required.is_empty() {
|
2014-12-12 10:59:41 -08:00
|
|
|
try!(w.write_str("\n"));
|
2014-11-20 11:22:09 -08:00
|
|
|
}
|
2015-01-31 12:20:46 -05:00
|
|
|
for m in &required {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " "));
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_item(w, m, AssocItemLink::Anchor));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ";\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-03-24 16:54:09 -07:00
|
|
|
if !required.is_empty() && !provided.is_empty() {
|
2014-12-12 10:59:41 -08:00
|
|
|
try!(w.write_str("\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-01-31 12:20:46 -05:00
|
|
|
for m in &provided {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " "));
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_item(w, m, AssocItemLink::Anchor));
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, " {{ ... }}\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, "}}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</pre>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
// Trait documentation
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, it));
|
2014-01-30 11:30:21 -08:00
|
|
|
|
2015-03-10 12:28:44 +02:00
|
|
|
fn trait_item(w: &mut fmt::Formatter, m: &clean::Item)
|
2014-08-04 13:56:56 -07:00
|
|
|
-> fmt::Result {
|
2015-04-13 11:55:00 -07:00
|
|
|
try!(write!(w, "<h3 id='{ty}.{name}' class='method stab {stab}'><code>",
|
|
|
|
ty = shortty(m),
|
|
|
|
name = *m.name.as_ref().unwrap(),
|
|
|
|
stab = m.stability_class()));
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_item(w, m, AssocItemLink::Anchor));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</code></h3>"));
|
2015-03-10 12:28:44 +02:00
|
|
|
try!(document(w, m));
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-03-24 16:54:09 -07:00
|
|
|
if !types.is_empty() {
|
2014-11-20 11:22:09 -08:00
|
|
|
try!(write!(w, "
|
|
|
|
<h2 id='associated-types'>Associated Types</h2>
|
|
|
|
<div class='methods'>
|
|
|
|
"));
|
2015-01-31 12:20:46 -05:00
|
|
|
for t in &types {
|
2014-11-20 11:22:09 -08:00
|
|
|
try!(trait_item(w, *t));
|
|
|
|
}
|
|
|
|
try!(write!(w, "</div>"));
|
|
|
|
}
|
|
|
|
|
2015-05-09 00:16:20 +02:00
|
|
|
if !consts.is_empty() {
|
|
|
|
try!(write!(w, "
|
|
|
|
<h2 id='associated-const'>Associated Constants</h2>
|
|
|
|
<div class='methods'>
|
|
|
|
"));
|
|
|
|
for t in &consts {
|
|
|
|
try!(trait_item(w, *t));
|
|
|
|
}
|
|
|
|
try!(write!(w, "</div>"));
|
|
|
|
}
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
// Output the documentation for each function individually
|
2015-03-24 16:54:09 -07:00
|
|
|
if !required.is_empty() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "
|
2013-09-18 22:18:38 -07:00
|
|
|
<h2 id='required-methods'>Required Methods</h2>
|
|
|
|
<div class='methods'>
|
2014-01-30 11:30:21 -08:00
|
|
|
"));
|
2015-01-31 12:20:46 -05:00
|
|
|
for m in &required {
|
2014-08-04 13:56:56 -07:00
|
|
|
try!(trait_item(w, *m));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</div>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-03-24 16:54:09 -07:00
|
|
|
if !provided.is_empty() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "
|
2013-09-18 22:18:38 -07:00
|
|
|
<h2 id='provided-methods'>Provided Methods</h2>
|
|
|
|
<div class='methods'>
|
2014-01-30 11:30:21 -08:00
|
|
|
"));
|
2015-01-31 12:20:46 -05:00
|
|
|
for m in &provided {
|
2014-08-04 13:56:56 -07:00
|
|
|
try!(trait_item(w, *m));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</div>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-04-06 19:21:52 -07:00
|
|
|
// If there are methods directly on this trait object, render them here.
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_items(w, it.def_id, AssocItemRender::All));
|
2015-04-06 19:21:52 -07:00
|
|
|
|
2014-11-14 14:20:57 -08:00
|
|
|
let cache = cache();
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(write!(w, "
|
|
|
|
<h2 id='implementors'>Implementors</h2>
|
|
|
|
<ul class='item-list' id='implementors-list'>
|
|
|
|
"));
|
2014-11-06 12:25:16 -05:00
|
|
|
match cache.implementors.get(&it.def_id) {
|
2014-04-28 20:36:08 -07:00
|
|
|
Some(implementors) => {
|
2015-01-31 12:20:46 -05:00
|
|
|
for i in implementors {
|
2015-07-18 02:02:57 -04:00
|
|
|
try!(writeln!(w, "<li><code>{}</code></li>", i.impl_));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-12-05 18:19:06 -08:00
|
|
|
}
|
2014-04-28 20:36:08 -07:00
|
|
|
None => {}
|
|
|
|
}
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(write!(w, "</ul>"));
|
|
|
|
try!(write!(w, r#"<script type="text/javascript" async
|
2014-05-29 09:58:09 -07:00
|
|
|
src="{root_path}/implementors/{path}/{ty}.{name}.js">
|
|
|
|
</script>"#,
|
2015-07-10 08:19:21 -04:00
|
|
|
root_path = vec![".."; cx.current.len()].join("/"),
|
2014-05-27 17:52:40 -07:00
|
|
|
path = if ast_util::is_local(it.def_id) {
|
2015-07-10 08:19:21 -04:00
|
|
|
cx.current.join("/")
|
2014-05-27 17:52:40 -07:00
|
|
|
} else {
|
2015-03-21 21:15:47 -04:00
|
|
|
let path = &cache.external_paths[&it.def_id];
|
2015-07-10 08:19:21 -04:00
|
|
|
path[..path.len() - 1].join("/")
|
2014-05-27 17:52:40 -07:00
|
|
|
},
|
|
|
|
ty = shortty(it).to_static_str(),
|
2014-10-14 23:05:01 -07:00
|
|
|
name = *it.name.as_ref().unwrap()));
|
2014-04-28 20:36:08 -07:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-03-15 19:35:25 -06:00
|
|
|
fn assoc_const(w: &mut fmt::Formatter, it: &clean::Item,
|
2015-04-30 09:37:13 -07:00
|
|
|
ty: &clean::Type, default: Option<&String>)
|
2015-03-15 19:35:25 -06:00
|
|
|
-> fmt::Result {
|
|
|
|
try!(write!(w, "const {}", it.name.as_ref().unwrap()));
|
|
|
|
try!(write!(w, ": {}", ty));
|
2015-04-30 09:37:13 -07:00
|
|
|
if let Some(default) = default {
|
2015-03-15 19:35:25 -06:00
|
|
|
try!(write!(w, " = {}", default));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-01-02 08:26:55 -05:00
|
|
|
fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item,
|
2015-03-10 12:28:44 +02:00
|
|
|
bounds: &Vec<clean::TyParamBound>,
|
|
|
|
default: &Option<clean::Type>)
|
|
|
|
-> fmt::Result {
|
2015-01-02 08:26:55 -05:00
|
|
|
try!(write!(w, "type {}", it.name.as_ref().unwrap()));
|
2015-03-24 16:54:09 -07:00
|
|
|
if !bounds.is_empty() {
|
2015-03-10 12:28:44 +02:00
|
|
|
try!(write!(w, ": {}", TyParamBounds(bounds)))
|
2015-01-02 08:26:55 -05:00
|
|
|
}
|
2015-03-10 12:28:44 +02:00
|
|
|
if let Some(ref default) = *default {
|
2015-01-02 08:26:55 -05:00
|
|
|
try!(write!(w, " = {}", default));
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2015-03-14 12:05:00 -06:00
|
|
|
fn render_assoc_item(w: &mut fmt::Formatter, meth: &clean::Item,
|
|
|
|
link: AssocItemLink) -> fmt::Result {
|
2015-02-25 22:05:07 +02:00
|
|
|
fn method(w: &mut fmt::Formatter,
|
|
|
|
it: &clean::Item,
|
|
|
|
unsafety: ast::Unsafety,
|
|
|
|
constness: ast::Constness,
|
|
|
|
abi: abi::Abi,
|
|
|
|
g: &clean::Generics,
|
|
|
|
selfty: &clean::SelfTy,
|
|
|
|
d: &clean::FnDecl,
|
|
|
|
link: AssocItemLink)
|
|
|
|
-> fmt::Result {
|
2015-02-06 00:51:38 -08:00
|
|
|
use syntax::abi::Abi;
|
|
|
|
|
2015-04-06 17:56:35 -07:00
|
|
|
let name = it.name.as_ref().unwrap();
|
|
|
|
let anchor = format!("#{}.{}", shortty(it), name);
|
|
|
|
let href = match link {
|
2015-03-14 12:05:00 -06:00
|
|
|
AssocItemLink::Anchor => anchor,
|
|
|
|
AssocItemLink::GotoSource(did) => {
|
2015-04-06 17:56:35 -07:00
|
|
|
href(did).map(|p| format!("{}{}", p.0, anchor)).unwrap_or(anchor)
|
|
|
|
}
|
|
|
|
};
|
2015-02-25 22:05:07 +02:00
|
|
|
write!(w, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
|
2014-09-25 02:01:42 -07:00
|
|
|
{generics}{decl}{where_clause}",
|
2015-02-25 22:05:07 +02:00
|
|
|
UnsafetySpace(unsafety),
|
|
|
|
ConstnessSpace(constness),
|
2015-02-06 00:51:38 -08:00
|
|
|
match abi {
|
|
|
|
Abi::Rust => String::new(),
|
|
|
|
a => format!("extern {} ", a.to_string())
|
|
|
|
},
|
2015-04-06 17:56:35 -07:00
|
|
|
href = href,
|
|
|
|
name = name,
|
2013-09-18 22:18:38 -07:00
|
|
|
generics = *g,
|
2014-09-25 02:01:42 -07:00
|
|
|
decl = Method(selfty, d),
|
|
|
|
where_clause = WhereClause(g))
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
match meth.inner {
|
|
|
|
clean::TyMethodItem(ref m) => {
|
2015-02-25 22:05:07 +02:00
|
|
|
method(w, meth, m.unsafety, ast::Constness::NotConst,
|
|
|
|
m.abi, &m.generics, &m.self_, &m.decl, link)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
clean::MethodItem(ref m) => {
|
2015-02-25 22:05:07 +02:00
|
|
|
method(w, meth, m.unsafety, m.constness,
|
|
|
|
m.abi, &m.generics, &m.self_, &m.decl,
|
2015-04-06 17:56:35 -07:00
|
|
|
link)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2015-03-15 19:35:25 -06:00
|
|
|
clean::AssociatedConstItem(ref ty, ref default) => {
|
2015-04-30 09:37:13 -07:00
|
|
|
assoc_const(w, meth, ty, default.as_ref())
|
2015-03-15 19:35:25 -06:00
|
|
|
}
|
2015-03-10 12:28:44 +02:00
|
|
|
clean::AssociatedTypeItem(ref bounds, ref default) => {
|
|
|
|
assoc_type(w, meth, bounds, default)
|
2014-11-20 11:22:09 -08:00
|
|
|
}
|
2015-03-14 12:05:00 -06:00
|
|
|
_ => panic!("render_assoc_item called on non-associated-item")
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn item_struct(w: &mut fmt::Formatter, it: &clean::Item,
|
2014-01-30 11:30:21 -08:00
|
|
|
s: &clean::Struct) -> fmt::Result {
|
2014-02-20 01:14:51 -08:00
|
|
|
try!(write!(w, "<pre class='rust struct'>"));
|
2015-02-13 00:47:03 +09:00
|
|
|
try!(render_attributes(w, it));
|
2014-03-05 15:28:08 -08:00
|
|
|
try!(render_struct(w,
|
|
|
|
it,
|
|
|
|
Some(&s.generics),
|
|
|
|
s.struct_type,
|
2015-02-01 21:53:25 -05:00
|
|
|
&s.fields,
|
2014-03-05 15:28:08 -08:00
|
|
|
"",
|
|
|
|
true));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</pre>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, it));
|
2014-04-19 22:24:52 -07:00
|
|
|
let mut fields = s.fields.iter().filter(|f| {
|
|
|
|
match f.inner {
|
|
|
|
clean::StructFieldItem(clean::HiddenStructField) => false,
|
|
|
|
clean::StructFieldItem(clean::TypedStructField(..)) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}).peekable();
|
2014-11-29 16:41:21 -05:00
|
|
|
if let doctree::Plain = s.struct_type {
|
|
|
|
if fields.peek().is_some() {
|
|
|
|
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<table>"));
|
|
|
|
for field in fields {
|
2015-04-13 11:55:00 -07:00
|
|
|
try!(write!(w, "<tr class='stab {stab}'>
|
|
|
|
<td id='structfield.{name}'>\
|
|
|
|
<code>{name}</code></td><td>",
|
|
|
|
stab = field.stability_class(),
|
2015-02-01 21:53:25 -05:00
|
|
|
name = field.name.as_ref().unwrap()));
|
2014-11-29 16:41:21 -05:00
|
|
|
try!(document(w, field));
|
|
|
|
try!(write!(w, "</td></tr>"));
|
2013-09-30 16:31:35 -07:00
|
|
|
}
|
2014-11-29 16:41:21 -05:00
|
|
|
try!(write!(w, "</table>"));
|
2013-09-30 16:31:35 -07:00
|
|
|
}
|
|
|
|
}
|
2015-03-14 12:05:00 -06:00
|
|
|
render_assoc_items(w, it.def_id, AssocItemRender::All)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn item_enum(w: &mut fmt::Formatter, it: &clean::Item,
|
|
|
|
e: &clean::Enum) -> fmt::Result {
|
2015-02-13 00:47:03 +09:00
|
|
|
try!(write!(w, "<pre class='rust enum'>"));
|
|
|
|
try!(render_attributes(w, it));
|
|
|
|
try!(write!(w, "{}enum {}{}{}",
|
2014-01-30 11:30:21 -08:00
|
|
|
VisSpace(it.visibility),
|
2015-02-01 21:53:25 -05:00
|
|
|
it.name.as_ref().unwrap(),
|
2014-09-25 02:01:42 -07:00
|
|
|
e.generics,
|
|
|
|
WhereClause(&e.generics)));
|
2015-03-24 16:53:34 -07:00
|
|
|
if e.variants.is_empty() && !e.variants_stripped {
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, " {{}}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
} else {
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, " {{\n"));
|
2015-01-31 12:20:46 -05:00
|
|
|
for v in &e.variants {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " "));
|
2015-02-01 21:53:25 -05:00
|
|
|
let name = v.name.as_ref().unwrap();
|
2013-09-18 22:18:38 -07:00
|
|
|
match v.inner {
|
|
|
|
clean::VariantItem(ref var) => {
|
|
|
|
match var.kind {
|
2014-02-19 10:07:49 -08:00
|
|
|
clean::CLikeVariant => try!(write!(w, "{}", name)),
|
2013-09-18 22:18:38 -07:00
|
|
|
clean::TupleVariant(ref tys) => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "{}(", name));
|
2013-09-18 22:18:38 -07:00
|
|
|
for (i, ty) in tys.iter().enumerate() {
|
2014-01-30 11:30:21 -08:00
|
|
|
if i > 0 {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ", "))
|
2014-01-30 11:30:21 -08:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "{}", *ty));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ")"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
clean::StructVariant(ref s) => {
|
2014-03-05 15:28:08 -08:00
|
|
|
try!(render_struct(w,
|
|
|
|
v,
|
|
|
|
None,
|
|
|
|
s.struct_type,
|
2015-02-01 21:53:25 -05:00
|
|
|
&s.fields,
|
2014-03-05 15:28:08 -08:00
|
|
|
" ",
|
|
|
|
false));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ",\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-10-13 20:37:43 -07:00
|
|
|
|
|
|
|
if e.variants_stripped {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " // some variants omitted\n"));
|
2013-10-13 20:37:43 -07:00
|
|
|
}
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, "}}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</pre>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, it));
|
2015-03-24 16:54:09 -07:00
|
|
|
if !e.variants.is_empty() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>"));
|
2015-01-31 12:20:46 -05:00
|
|
|
for variant in &e.variants {
|
2015-04-13 11:55:00 -07:00
|
|
|
try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>",
|
2015-02-01 21:53:25 -05:00
|
|
|
name = variant.name.as_ref().unwrap()));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, variant));
|
2013-10-18 22:00:08 -07:00
|
|
|
match variant.inner {
|
|
|
|
clean::VariantItem(ref var) => {
|
|
|
|
match var.kind {
|
|
|
|
clean::StructVariant(ref s) => {
|
2015-01-30 12:26:44 -08:00
|
|
|
let fields = s.fields.iter().filter(|f| {
|
2014-04-19 22:24:52 -07:00
|
|
|
match f.inner {
|
|
|
|
clean::StructFieldItem(ref t) => match *t {
|
|
|
|
clean::HiddenStructField => false,
|
|
|
|
clean::TypedStructField(..) => true,
|
|
|
|
},
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
});
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h3 class='fields'>Fields</h3>\n
|
2014-01-30 11:30:21 -08:00
|
|
|
<table>"));
|
2014-04-19 22:24:52 -07:00
|
|
|
for field in fields {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<tr><td \
|
2014-01-30 11:30:21 -08:00
|
|
|
id='variant.{v}.field.{f}'>\
|
|
|
|
<code>{f}</code></td><td>",
|
2015-02-01 21:53:25 -05:00
|
|
|
v = variant.name.as_ref().unwrap(),
|
|
|
|
f = field.name.as_ref().unwrap()));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, field));
|
|
|
|
try!(write!(w, "</td></tr>"));
|
2013-10-18 22:00:08 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</table>"));
|
2013-10-18 22:00:08 -07:00
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</td></tr>"));
|
2013-09-30 16:31:35 -07:00
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</table>"));
|
2013-09-30 16:31:35 -07:00
|
|
|
|
|
|
|
}
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_items(w, it.def_id, AssocItemRender::All));
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-02-13 00:47:03 +09:00
|
|
|
fn render_attributes(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
|
|
|
|
for attr in &it.attrs {
|
|
|
|
match *attr {
|
|
|
|
clean::Word(ref s) if *s == "must_use" => {
|
|
|
|
try!(write!(w, "#[{}]\n", s));
|
|
|
|
}
|
|
|
|
clean::NameValue(ref k, ref v) if *k == "must_use" => {
|
|
|
|
try!(write!(w, "#[{} = \"{}\"]\n", k, v));
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn render_struct(w: &mut fmt::Formatter, it: &clean::Item,
|
2013-09-18 22:18:38 -07:00
|
|
|
g: Option<&clean::Generics>,
|
|
|
|
ty: doctree::StructType,
|
|
|
|
fields: &[clean::Item],
|
2013-09-30 11:44:25 -07:00
|
|
|
tab: &str,
|
2014-01-30 11:30:21 -08:00
|
|
|
structhead: bool) -> fmt::Result {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "{}{}{}",
|
2014-01-30 11:30:21 -08:00
|
|
|
VisSpace(it.visibility),
|
|
|
|
if structhead {"struct "} else {""},
|
2015-02-01 21:53:25 -05:00
|
|
|
it.name.as_ref().unwrap()));
|
2013-09-18 22:18:38 -07:00
|
|
|
match g {
|
2014-09-25 02:01:42 -07:00
|
|
|
Some(g) => try!(write!(w, "{}{}", *g, WhereClause(g))),
|
2013-09-18 22:18:38 -07:00
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
match ty {
|
|
|
|
doctree::Plain => {
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, " {{\n{}", tab));
|
2014-04-19 22:24:52 -07:00
|
|
|
let mut fields_stripped = false;
|
2015-01-31 12:20:46 -05:00
|
|
|
for field in fields {
|
2013-09-18 22:18:38 -07:00
|
|
|
match field.inner {
|
2014-04-19 22:24:52 -07:00
|
|
|
clean::StructFieldItem(clean::HiddenStructField) => {
|
|
|
|
fields_stripped = true;
|
|
|
|
}
|
|
|
|
clean::StructFieldItem(clean::TypedStructField(ref ty)) => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " {}{}: {},\n{}",
|
2014-01-30 11:30:21 -08:00
|
|
|
VisSpace(field.visibility),
|
2015-02-01 21:53:25 -05:00
|
|
|
field.name.as_ref().unwrap(),
|
2014-04-19 22:24:52 -07:00
|
|
|
*ty,
|
2014-01-30 11:30:21 -08:00
|
|
|
tab));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-04-19 22:24:52 -07:00
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-10-13 20:37:43 -07:00
|
|
|
|
|
|
|
if fields_stripped {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " // some fields omitted\n{}", tab));
|
2013-10-13 20:37:43 -07:00
|
|
|
}
|
2014-05-28 09:24:28 -07:00
|
|
|
try!(write!(w, "}}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
doctree::Tuple | doctree::Newtype => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "("));
|
2013-09-18 22:18:38 -07:00
|
|
|
for (i, field) in fields.iter().enumerate() {
|
2014-01-30 11:30:21 -08:00
|
|
|
if i > 0 {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ", "));
|
2014-01-30 11:30:21 -08:00
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
match field.inner {
|
2014-04-19 22:24:52 -07:00
|
|
|
clean::StructFieldItem(clean::HiddenStructField) => {
|
|
|
|
try!(write!(w, "_"))
|
|
|
|
}
|
|
|
|
clean::StructFieldItem(clean::TypedStructField(ref ty)) => {
|
|
|
|
try!(write!(w, "{}{}", VisSpace(field.visibility), *ty))
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ");"));
|
2014-01-30 11:30:21 -08:00
|
|
|
}
|
|
|
|
doctree::Unit => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ";"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-04-06 17:56:35 -07:00
|
|
|
#[derive(Copy, Clone)]
|
2015-03-14 12:05:00 -06:00
|
|
|
enum AssocItemLink {
|
2015-04-06 17:56:35 -07:00
|
|
|
Anchor,
|
|
|
|
GotoSource(ast::DefId),
|
|
|
|
}
|
|
|
|
|
2015-03-14 12:05:00 -06:00
|
|
|
enum AssocItemRender<'a> {
|
2015-04-13 16:23:32 -07:00
|
|
|
All,
|
|
|
|
DerefFor { trait_: &'a clean::Type, type_: &'a clean::Type },
|
|
|
|
}
|
|
|
|
|
2015-03-14 12:05:00 -06:00
|
|
|
fn render_assoc_items(w: &mut fmt::Formatter,
|
|
|
|
it: ast::DefId,
|
|
|
|
what: AssocItemRender) -> fmt::Result {
|
2015-04-13 16:23:32 -07:00
|
|
|
let c = cache();
|
|
|
|
let v = match c.impls.get(&it) {
|
|
|
|
Some(v) => v,
|
2015-04-06 19:21:52 -07:00
|
|
|
None => return Ok(()),
|
|
|
|
};
|
2015-04-13 16:23:32 -07:00
|
|
|
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| {
|
|
|
|
i.impl_.trait_.is_none()
|
|
|
|
});
|
2015-03-24 16:54:09 -07:00
|
|
|
if !non_trait.is_empty() {
|
2015-04-13 16:23:32 -07:00
|
|
|
let render_header = match what {
|
2015-03-14 12:05:00 -06:00
|
|
|
AssocItemRender::All => {
|
2015-04-13 16:23:32 -07:00
|
|
|
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
|
|
|
|
true
|
|
|
|
}
|
2015-03-14 12:05:00 -06:00
|
|
|
AssocItemRender::DerefFor { trait_, type_ } => {
|
2015-04-13 16:23:32 -07:00
|
|
|
try!(write!(w, "<h2 id='deref-methods'>Methods from \
|
|
|
|
{}<Target={}></h2>", trait_, type_));
|
|
|
|
false
|
|
|
|
}
|
|
|
|
};
|
2015-04-06 19:21:52 -07:00
|
|
|
for i in &non_trait {
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_impl(w, i, AssocItemLink::Anchor, render_header));
|
2015-04-06 19:21:52 -07:00
|
|
|
}
|
|
|
|
}
|
2015-03-14 12:05:00 -06:00
|
|
|
if let AssocItemRender::DerefFor { .. } = what {
|
2015-04-13 16:23:32 -07:00
|
|
|
return Ok(())
|
|
|
|
}
|
2015-03-24 16:54:09 -07:00
|
|
|
if !traits.is_empty() {
|
2015-04-13 16:23:32 -07:00
|
|
|
let deref_impl = traits.iter().find(|t| {
|
|
|
|
match *t.impl_.trait_.as_ref().unwrap() {
|
|
|
|
clean::ResolvedPath { did, .. } => {
|
|
|
|
Some(did) == c.deref_trait_did
|
|
|
|
}
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if let Some(impl_) = deref_impl {
|
|
|
|
try!(render_deref_methods(w, impl_));
|
|
|
|
}
|
2015-04-06 19:21:52 -07:00
|
|
|
try!(write!(w, "<h2 id='implementations'>Trait \
|
|
|
|
Implementations</h2>"));
|
2015-06-03 12:38:42 +02:00
|
|
|
let (derived, manual): (Vec<_>, Vec<&Impl>) = traits.iter().partition(|i| {
|
2015-04-13 16:23:32 -07:00
|
|
|
i.impl_.derived
|
|
|
|
});
|
2015-04-06 19:21:52 -07:00
|
|
|
for i in &manual {
|
|
|
|
let did = i.trait_did().unwrap();
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_impl(w, i, AssocItemLink::GotoSource(did), true));
|
2015-04-06 19:21:52 -07:00
|
|
|
}
|
2015-03-24 16:54:09 -07:00
|
|
|
if !derived.is_empty() {
|
2015-04-06 19:21:52 -07:00
|
|
|
try!(write!(w, "<h3 id='derived_implementations'>\
|
|
|
|
Derived Implementations \
|
|
|
|
</h3>"));
|
|
|
|
for i in &derived {
|
|
|
|
let did = i.trait_did().unwrap();
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_impl(w, i, AssocItemLink::GotoSource(did), true));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-12-05 18:19:06 -08:00
|
|
|
}
|
2014-04-28 20:36:08 -07:00
|
|
|
}
|
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-04-13 16:23:32 -07:00
|
|
|
fn render_deref_methods(w: &mut fmt::Formatter, impl_: &Impl) -> fmt::Result {
|
|
|
|
let deref_type = impl_.impl_.trait_.as_ref().unwrap();
|
|
|
|
let target = impl_.impl_.items.iter().filter_map(|item| {
|
|
|
|
match item.inner {
|
2015-05-21 14:17:37 +02:00
|
|
|
clean::TypedefItem(ref t, true) => Some(&t.type_),
|
2015-04-13 16:23:32 -07:00
|
|
|
_ => None,
|
|
|
|
}
|
2015-05-21 14:17:37 +02:00
|
|
|
}).next().expect("Expected associated type binding");
|
2015-03-14 12:05:00 -06:00
|
|
|
let what = AssocItemRender::DerefFor { trait_: deref_type, type_: target };
|
2015-04-13 16:23:32 -07:00
|
|
|
match *target {
|
2015-03-14 12:05:00 -06:00
|
|
|
clean::ResolvedPath { did, .. } => render_assoc_items(w, did, what),
|
2015-04-13 16:23:32 -07:00
|
|
|
_ => {
|
|
|
|
if let Some(prim) = target.primitive_type() {
|
|
|
|
if let Some(c) = cache().primitive_locations.get(&prim) {
|
|
|
|
let did = ast::DefId { krate: *c, node: prim.to_node_id() };
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_items(w, did, what));
|
2015-04-13 16:23:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2014-05-03 02:08:58 -07:00
|
|
|
}
|
2015-04-13 16:23:32 -07:00
|
|
|
}
|
|
|
|
|
2015-04-26 21:03:38 +02:00
|
|
|
// Render_header is false when we are rendering a `Deref` impl and true
|
|
|
|
// otherwise. If render_header is false, we will avoid rendering static
|
|
|
|
// methods, since they are not accessible for the type implementing `Deref`
|
2015-03-14 12:05:00 -06:00
|
|
|
fn render_impl(w: &mut fmt::Formatter, i: &Impl, link: AssocItemLink,
|
2015-04-13 16:23:32 -07:00
|
|
|
render_header: bool) -> fmt::Result {
|
|
|
|
if render_header {
|
2015-07-18 02:02:57 -04:00
|
|
|
try!(write!(w, "<h3 class='impl'><code>{}</code></h3>", i.impl_));
|
2015-04-13 16:23:32 -07:00
|
|
|
if let Some(ref dox) = i.dox {
|
|
|
|
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(dox)));
|
|
|
|
}
|
2013-09-30 17:04:14 -07:00
|
|
|
}
|
2013-10-21 11:33:04 -07:00
|
|
|
|
2015-04-06 17:56:35 -07:00
|
|
|
fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item,
|
2015-04-26 21:03:38 +02:00
|
|
|
link: AssocItemLink, render_static: bool) -> fmt::Result {
|
2014-11-20 11:22:09 -08:00
|
|
|
match item.inner {
|
|
|
|
clean::MethodItem(..) | clean::TyMethodItem(..) => {
|
2015-04-26 21:03:38 +02:00
|
|
|
// Only render when the method is not static or we allow static methods
|
|
|
|
if !is_static_method(item) || render_static {
|
|
|
|
try!(write!(w, "<h4 id='method.{}' class='{}'><code>",
|
|
|
|
*item.name.as_ref().unwrap(),
|
|
|
|
shortty(item)));
|
2015-03-14 12:05:00 -06:00
|
|
|
try!(render_assoc_item(w, item, link));
|
2015-04-26 21:03:38 +02:00
|
|
|
try!(write!(w, "</code></h4>\n"));
|
|
|
|
}
|
2014-11-20 11:22:09 -08:00
|
|
|
}
|
2015-05-21 14:17:37 +02:00
|
|
|
clean::TypedefItem(ref tydef, _) => {
|
2014-11-20 11:22:09 -08:00
|
|
|
let name = item.name.as_ref().unwrap();
|
2015-04-13 11:55:00 -07:00
|
|
|
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
|
2014-11-20 11:22:09 -08:00
|
|
|
*name,
|
2015-04-13 11:55:00 -07:00
|
|
|
shortty(item)));
|
2014-11-20 11:22:09 -08:00
|
|
|
try!(write!(w, "type {} = {}", name, tydef.type_));
|
|
|
|
try!(write!(w, "</code></h4>\n"));
|
|
|
|
}
|
2015-03-15 19:35:25 -06:00
|
|
|
clean::AssociatedConstItem(ref ty, ref default) => {
|
|
|
|
let name = item.name.as_ref().unwrap();
|
|
|
|
try!(write!(w, "<h4 id='assoc_const.{}' class='{}'><code>",
|
2015-04-30 09:37:13 -07:00
|
|
|
*name, shortty(item)));
|
|
|
|
try!(assoc_const(w, item, ty, default.as_ref()));
|
|
|
|
try!(write!(w, "</code></h4>\n"));
|
|
|
|
}
|
|
|
|
clean::ConstantItem(ref c) => {
|
|
|
|
let name = item.name.as_ref().unwrap();
|
|
|
|
try!(write!(w, "<h4 id='assoc_const.{}' class='{}'><code>",
|
|
|
|
*name, shortty(item)));
|
|
|
|
try!(assoc_const(w, item, &c.type_, Some(&c.expr)));
|
2015-03-15 19:35:25 -06:00
|
|
|
try!(write!(w, "</code></h4>\n"));
|
|
|
|
}
|
2015-03-10 12:28:44 +02:00
|
|
|
clean::AssociatedTypeItem(ref bounds, ref default) => {
|
2015-01-02 08:26:55 -05:00
|
|
|
let name = item.name.as_ref().unwrap();
|
2015-04-13 11:55:00 -07:00
|
|
|
try!(write!(w, "<h4 id='assoc_type.{}' class='{}'><code>",
|
2015-01-02 08:26:55 -05:00
|
|
|
*name,
|
2015-04-13 11:55:00 -07:00
|
|
|
shortty(item)));
|
2015-03-10 12:28:44 +02:00
|
|
|
try!(assoc_type(w, item, bounds, default));
|
2015-01-02 08:26:55 -05:00
|
|
|
try!(write!(w, "</code></h4>\n"));
|
|
|
|
}
|
2014-12-20 00:09:35 -08:00
|
|
|
_ => panic!("can't make docs for trait item with name {:?}", item.name)
|
2014-11-20 11:22:09 -08:00
|
|
|
}
|
2015-04-26 21:03:38 +02:00
|
|
|
|
|
|
|
return if let AssocItemLink::Anchor = link {
|
|
|
|
if is_static_method(item) && !render_static {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
document(w, item)
|
|
|
|
}
|
2015-04-13 11:55:00 -07:00
|
|
|
} else {
|
|
|
|
Ok(())
|
2015-04-26 21:03:38 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
fn is_static_method(item: &clean::Item) -> bool {
|
|
|
|
match item.inner {
|
|
|
|
clean::MethodItem(ref method) => method.self_ == SelfTy::SelfStatic,
|
|
|
|
clean::TyMethodItem(ref method) => method.self_ == SelfTy::SelfStatic,
|
|
|
|
_ => false
|
|
|
|
}
|
2013-10-21 11:33:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-20 11:22:09 -08:00
|
|
|
try!(write!(w, "<div class='impl-items'>"));
|
2015-06-10 17:22:20 +01:00
|
|
|
for trait_item in &i.impl_.items {
|
2015-04-26 21:03:38 +02:00
|
|
|
try!(doctraititem(w, trait_item, link, render_header));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-10-21 11:33:04 -07:00
|
|
|
|
2015-03-14 12:05:00 -06:00
|
|
|
fn render_default_items(w: &mut fmt::Formatter,
|
|
|
|
did: ast::DefId,
|
|
|
|
t: &clean::Trait,
|
2015-04-26 21:03:38 +02:00
|
|
|
i: &clean::Impl,
|
|
|
|
render_static: bool) -> fmt::Result {
|
2015-01-31 12:20:46 -05:00
|
|
|
for trait_item in &t.items {
|
2015-03-10 12:28:44 +02:00
|
|
|
let n = trait_item.name.clone();
|
2014-08-04 13:56:56 -07:00
|
|
|
match i.items.iter().find(|m| { m.name == n }) {
|
2014-05-03 02:08:58 -07:00
|
|
|
Some(..) => continue,
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
2015-04-26 21:03:38 +02:00
|
|
|
try!(doctraititem(w, trait_item, AssocItemLink::GotoSource(did), render_static));
|
2014-05-03 02:08:58 -07:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2013-10-21 11:33:04 -07:00
|
|
|
// If we've implemented a trait, then also emit documentation for all
|
|
|
|
// default methods which weren't overridden in the implementation block.
|
2014-11-20 11:22:09 -08:00
|
|
|
// FIXME: this also needs to be done for associated types, whenever defaults
|
|
|
|
// for them work.
|
2015-04-06 15:10:55 -07:00
|
|
|
if let Some(clean::ResolvedPath { did, .. }) = i.impl_.trait_ {
|
|
|
|
if let Some(t) = cache().traits.get(&did) {
|
2015-04-26 21:03:38 +02:00
|
|
|
try!(render_default_items(w, did, t, &i.impl_, render_header));
|
|
|
|
|
2013-10-21 11:33:04 -07:00
|
|
|
}
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "</div>"));
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn item_typedef(w: &mut fmt::Formatter, it: &clean::Item,
|
2014-01-30 11:30:21 -08:00
|
|
|
t: &clean::Typedef) -> fmt::Result {
|
2015-05-25 23:05:35 +02:00
|
|
|
try!(write!(w, "<pre class='rust typedef'>type {}{}{where_clause} = {type_};</pre>",
|
2015-02-01 21:53:25 -05:00
|
|
|
it.name.as_ref().unwrap(),
|
2014-01-30 11:30:21 -08:00
|
|
|
t.generics,
|
2015-05-25 23:05:35 +02:00
|
|
|
where_clause = WhereClause(&t.generics),
|
|
|
|
type_ = t.type_));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
document(w, it)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2015-01-20 15:45:07 -08:00
|
|
|
impl<'a> fmt::Display for Sidebar<'a> {
|
2014-02-05 23:55:13 +11:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
let cx = self.cx;
|
|
|
|
let it = self.item;
|
2015-03-05 16:35:43 +09:00
|
|
|
let parentlen = cx.current.len() - if it.is_mod() {1} else {0};
|
|
|
|
|
2015-03-05 23:10:15 +09:00
|
|
|
// the sidebar is designed to display sibling functions, modules and
|
|
|
|
// other miscellaneous informations. since there are lots of sibling
|
|
|
|
// items (and that causes quadratic growth in large modules),
|
|
|
|
// we refactor common parts into a shared JavaScript file per module.
|
|
|
|
// still, we don't move everything into JS because we want to preserve
|
|
|
|
// as much HTML as possible in order to allow non-JS-enabled browsers
|
|
|
|
// to navigate the documentation (though slightly inefficiently).
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<p class='location'>"));
|
2015-03-05 16:35:43 +09:00
|
|
|
for (i, name) in cx.current.iter().take(parentlen).enumerate() {
|
2014-01-30 11:30:21 -08:00
|
|
|
if i > 0 {
|
2014-08-17 19:28:20 +01:00
|
|
|
try!(write!(fmt, "::<wbr>"));
|
2014-01-30 11:30:21 -08:00
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<a href='{}index.html'>{}</a>",
|
2015-01-26 21:21:15 -05:00
|
|
|
&cx.root_path[..(cx.current.len() - i - 1) * 3],
|
2014-01-30 11:30:21 -08:00
|
|
|
*name));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "</p>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2015-03-05 16:35:43 +09:00
|
|
|
// sidebar refers to the enclosing module, not this module
|
|
|
|
let relpath = if shortty(it) == ItemType::Module { "../" } else { "" };
|
|
|
|
try!(write!(fmt,
|
|
|
|
"<script>window.sidebarCurrent = {{\
|
|
|
|
name: '{name}', \
|
|
|
|
ty: '{ty}', \
|
|
|
|
relpath: '{path}'\
|
|
|
|
}};</script>",
|
|
|
|
name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""),
|
|
|
|
ty = shortty(it).to_static_str(),
|
|
|
|
path = relpath));
|
|
|
|
if parentlen == 0 {
|
|
|
|
// there is no sidebar-items.js beyond the crate root path
|
|
|
|
// FIXME maybe dynamic crate loading can be merged here
|
|
|
|
} else {
|
2015-03-07 23:01:31 +09:00
|
|
|
try!(write!(fmt, "<script defer src=\"{path}sidebar-items.js\"></script>",
|
2015-03-05 16:35:43 +09:00
|
|
|
path = relpath));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-20 15:45:07 -08:00
|
|
|
impl<'a> fmt::Display for Source<'a> {
|
2014-02-05 23:55:13 +11:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
let Source(s) = *self;
|
2014-06-05 23:18:51 -07:00
|
|
|
let lines = s.lines().count();
|
2013-09-27 15:12:23 -07:00
|
|
|
let mut cols = 0;
|
|
|
|
let mut tmp = lines;
|
|
|
|
while tmp > 0 {
|
|
|
|
cols += 1;
|
|
|
|
tmp /= 10;
|
|
|
|
}
|
2014-12-20 03:56:47 +09:00
|
|
|
try!(write!(fmt, "<pre class=\"line-numbers\">"));
|
2015-01-26 15:46:12 -05:00
|
|
|
for i in 1..lines + 1 {
|
2014-12-20 03:56:47 +09:00
|
|
|
try!(write!(fmt, "<span id=\"{0}\">{0:1$}</span>\n", i, cols));
|
2013-09-27 15:12:23 -07:00
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "</pre>"));
|
2015-02-01 21:53:25 -05:00
|
|
|
try!(write!(fmt, "{}", highlight::highlight(s, None, None)));
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-27 15:12:23 -07:00
|
|
|
}
|
2013-09-26 11:09:47 -07:00
|
|
|
}
|
2014-02-16 21:40:26 -08:00
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn item_macro(w: &mut fmt::Formatter, it: &clean::Item,
|
2014-02-16 21:40:26 -08:00
|
|
|
t: &clean::Macro) -> fmt::Result {
|
2015-02-01 21:53:25 -05:00
|
|
|
try!(w.write_str(&highlight::highlight(&t.source,
|
2014-12-12 10:59:41 -08:00
|
|
|
Some("macro"),
|
2015-02-01 21:53:25 -05:00
|
|
|
None)));
|
2014-02-16 21:40:26 -08:00
|
|
|
document(w, it)
|
|
|
|
}
|
2014-05-28 19:53:37 -07:00
|
|
|
|
|
|
|
fn item_primitive(w: &mut fmt::Formatter,
|
|
|
|
it: &clean::Item,
|
2014-09-11 17:07:49 +12:00
|
|
|
_p: &clean::PrimitiveType) -> fmt::Result {
|
2014-05-28 19:53:37 -07:00
|
|
|
try!(document(w, it));
|
2015-03-14 12:05:00 -06:00
|
|
|
render_assoc_items(w, it.def_id, AssocItemRender::All)
|
2014-05-28 19:53:37 -07:00
|
|
|
}
|
2014-05-29 13:50:47 -07:00
|
|
|
|
2014-08-04 14:30:06 -07:00
|
|
|
fn get_basic_keywords() -> &'static str {
|
|
|
|
"rust, rustlang, rust-lang"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn make_item_keywords(it: &clean::Item) -> String {
|
2014-10-14 23:05:01 -07:00
|
|
|
format!("{}, {}", get_basic_keywords(), it.name.as_ref().unwrap())
|
2014-08-04 14:30:06 -07:00
|
|
|
}
|
2014-11-14 14:20:57 -08:00
|
|
|
|
2015-02-26 01:03:06 +02:00
|
|
|
fn get_index_search_type(item: &clean::Item,
|
|
|
|
parent: Option<String>) -> Option<IndexItemFunctionType> {
|
|
|
|
let decl = match item.inner {
|
|
|
|
clean::FunctionItem(ref f) => &f.decl,
|
|
|
|
clean::MethodItem(ref m) => &m.decl,
|
|
|
|
clean::TyMethodItem(ref m) => &m.decl,
|
|
|
|
_ => return None
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut inputs = Vec::new();
|
|
|
|
|
|
|
|
// Consider `self` an argument as well.
|
|
|
|
if let Some(name) = parent {
|
2015-07-08 08:33:13 -07:00
|
|
|
inputs.push(Type { name: Some(name.to_ascii_lowercase()) });
|
2015-02-26 01:03:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
inputs.extend(&mut decl.inputs.values.iter().map(|arg| {
|
|
|
|
get_index_type(&arg.type_)
|
|
|
|
}));
|
|
|
|
|
|
|
|
let output = match decl.output {
|
|
|
|
clean::FunctionRetTy::Return(ref return_type) => Some(get_index_type(return_type)),
|
|
|
|
_ => None
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(IndexItemFunctionType { inputs: inputs, output: output })
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_index_type(clean_type: &clean::Type) -> Type {
|
2015-07-08 08:33:13 -07:00
|
|
|
Type { name: get_index_type_name(clean_type).map(|s| s.to_ascii_lowercase()) }
|
2015-02-26 01:03:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_index_type_name(clean_type: &clean::Type) -> Option<String> {
|
|
|
|
match *clean_type {
|
|
|
|
clean::ResolvedPath { ref path, .. } => {
|
|
|
|
let segments = &path.segments;
|
|
|
|
Some(segments[segments.len() - 1].name.clone())
|
|
|
|
},
|
|
|
|
clean::Generic(ref s) => Some(s.clone()),
|
|
|
|
clean::Primitive(ref p) => Some(format!("{:?}", p)),
|
|
|
|
clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_),
|
|
|
|
// FIXME: add all from clean::Type.
|
|
|
|
_ => None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-14 14:20:57 -08:00
|
|
|
pub fn cache() -> Arc<Cache> {
|
|
|
|
CACHE_KEY.with(|c| c.borrow().clone())
|
|
|
|
}
|