2014-01-30 19:29:35 +01:00
|
|
|
// Copyright 2013-2014 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,
|
|
|
|
//! and then it is shared among the various rendering tasks. The cache is meant
|
|
|
|
//! to be a fairly large structure not implementing `Clone` (because it's shared
|
|
|
|
//! among tasks). The context, however, should be a lightweight structure. This
|
|
|
|
//! is cloned per-task and contains information about what is currently being
|
|
|
|
//! 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.
|
|
|
|
//! These tasks are not parallelized (they haven't been a bottleneck yet), and
|
|
|
|
//! both occur before the crate is rendered.
|
|
|
|
|
2014-04-02 16:54:22 -07:00
|
|
|
use collections::{HashMap, HashSet};
|
2013-09-18 22:18:38 -07:00
|
|
|
use std::fmt;
|
2014-03-16 01:08:56 -07:00
|
|
|
use std::io::{fs, File, BufferedWriter, MemWriter, BufferedReader};
|
2014-04-02 16:54:22 -07:00
|
|
|
use std::io;
|
|
|
|
use std::str;
|
2014-05-22 16:57:53 -07:00
|
|
|
use std::string::String;
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-01-30 15:04:47 -05:00
|
|
|
use sync::Arc;
|
2014-02-21 14:18:39 -08:00
|
|
|
use serialize::json::ToJson;
|
2013-09-18 22:18:38 -07:00
|
|
|
use syntax::ast;
|
2014-05-09 13:52:17 -07:00
|
|
|
use syntax::ast_util;
|
2013-09-26 12:53:06 -07:00
|
|
|
use syntax::attr;
|
2014-01-10 14:02:36 -08:00
|
|
|
use syntax::parse::token::InternedString;
|
2014-02-28 14:34:26 -08:00
|
|
|
use rustc::util::nodemap::NodeSet;
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
use clean;
|
|
|
|
use doctree;
|
|
|
|
use fold::DocFolder;
|
2014-04-06 18:04:40 -07:00
|
|
|
use html::format::{VisSpace, Method, FnStyleSpace};
|
2014-05-09 13:52:17 -07:00
|
|
|
use html::highlight;
|
|
|
|
use html::item_type::{ItemType, shortty};
|
|
|
|
use html::item_type;
|
2013-09-18 22:18:38 -07:00
|
|
|
use html::layout;
|
|
|
|
use html::markdown::Markdown;
|
2014-05-09 13:52:17 -07:00
|
|
|
use html::markdown;
|
2013-09-18 22:18:38 -07: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).
|
2013-09-18 22:18:38 -07:00
|
|
|
#[deriving(Clone)]
|
|
|
|
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,
|
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.
|
2014-03-28 10:27:24 -07:00
|
|
|
pub dst: Path,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This describes the layout of each page, and is not modified after
|
|
|
|
/// creation of the context (contains info like the favicon)
|
2014-03-28 10:27:24 -07:00
|
|
|
pub layout: layout::Layout,
|
2013-10-03 10:24:40 -07:00
|
|
|
/// This map is a list of what should be displayed on the sidebar of the
|
|
|
|
/// current page. The key is the section header (traits, modules,
|
|
|
|
/// functions), and the value is the list of containers belonging to this
|
|
|
|
/// header. This map will change depending on the surrounding context of the
|
|
|
|
/// page.
|
2014-05-22 16:57:53 -07:00
|
|
|
pub sidebar: HashMap<String, Vec<String>>,
|
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,
|
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 {
|
|
|
|
generics: clean::Generics,
|
|
|
|
trait_: clean::Type,
|
|
|
|
for_: clean::Type,
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
/// rendering tasks.
|
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-05-23 00:42:33 -07:00
|
|
|
pub impls: HashMap<ast::DefId, Vec<(clean::Impl, Option<String>)>>,
|
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.
|
2014-03-28 10:27:24 -07:00
|
|
|
pub extern_locations: HashMap<ast::CrateNum, 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.
|
|
|
|
pub primitive_locations: HashMap<clean::Primitive, ast::CrateNum>,
|
|
|
|
|
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,
|
|
|
|
public_items: NodeSet,
|
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
|
2013-09-27 15:12:23 -07:00
|
|
|
dst: Path,
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
2013-12-09 23:16:18 -08:00
|
|
|
struct Item<'a> { cx: &'a Context, item: &'a clean::Item, }
|
|
|
|
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>,
|
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
|
|
|
|
2013-12-05 18:19:06 -08:00
|
|
|
local_data_key!(pub cache_key: Arc<Cache>)
|
2014-05-22 16:57:53 -07:00
|
|
|
local_data_key!(pub current_location_key: Vec<String> )
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
/// Generates the documentation for `crate` into the directory `dst`
|
2014-02-05 22:15:24 +01:00
|
|
|
pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
|
2013-09-18 22:18:38 -07:00
|
|
|
let mut cx = Context {
|
|
|
|
dst: dst,
|
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
|
|
|
sidebar: HashMap::new(),
|
|
|
|
layout: layout::Layout {
|
2014-05-25 03:17:19 -07:00
|
|
|
logo: "".to_string(),
|
|
|
|
favicon: "".to_string(),
|
2014-02-05 22:15:24 +01:00
|
|
|
krate: krate.name.clone(),
|
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-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
|
2014-02-05 22:15:24 +01:00
|
|
|
match krate.module.as_ref().map(|m| m.doc_list().unwrap_or(&[])) {
|
2013-09-18 22:18:38 -07:00
|
|
|
Some(attrs) => {
|
|
|
|
for attr in attrs.iter() {
|
|
|
|
match *attr {
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::NameValue(ref x, ref s)
|
|
|
|
if "html_favicon_url" == x.as_slice() => {
|
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)
|
|
|
|
if "html_logo_url" == x.as_slice() => {
|
2014-05-25 03:17:19 -07:00
|
|
|
cx.layout.logo = s.to_string();
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::Word(ref x)
|
|
|
|
if "html_no_source" == x.as_slice() => {
|
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-05-09 13:52:17 -07:00
|
|
|
let analysis = ::analysiskey.get();
|
|
|
|
let public_items = analysis.as_ref().map(|a| a.public_items.clone());
|
2014-05-03 02:08:58 -07:00
|
|
|
let public_items = public_items.unwrap_or(NodeSet::new());
|
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-05-09 13:52:17 -07:00
|
|
|
let paths = a.external_paths.borrow_mut().take_unwrap();
|
|
|
|
paths.move_iter().map(|(k, (v, t))| {
|
|
|
|
(k, (v, match t {
|
|
|
|
clean::TypeStruct => item_type::Struct,
|
|
|
|
clean::TypeEnum => item_type::Enum,
|
|
|
|
clean::TypeFunction => item_type::Function,
|
|
|
|
clean::TypeTrait => item_type::Trait,
|
|
|
|
clean::TypeModule => item_type::Module,
|
|
|
|
clean::TypeStatic => item_type::Static,
|
|
|
|
clean::TypeVariant => item_type::Variant,
|
|
|
|
}))
|
|
|
|
}).collect()
|
|
|
|
}).unwrap_or(HashMap::new());
|
2014-04-28 20:36:08 -07:00
|
|
|
let mut cache = Cache {
|
|
|
|
impls: HashMap::new(),
|
2014-05-23 20:17:27 -07:00
|
|
|
external_paths: paths.iter().map(|(&k, &(ref v, _))| (k, v.clone()))
|
|
|
|
.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-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(),
|
2014-05-03 02:08:58 -07:00
|
|
|
traits: analysis.as_ref().map(|a| {
|
|
|
|
a.external_traits.borrow_mut().take_unwrap()
|
|
|
|
}).unwrap_or(HashMap::new()),
|
|
|
|
typarams: analysis.as_ref().map(|a| {
|
|
|
|
a.external_typarams.borrow_mut().take_unwrap()
|
|
|
|
}).unwrap_or(HashMap::new()),
|
2014-05-26 21:51:59 -07:00
|
|
|
inlined: analysis.as_ref().map(|a| {
|
|
|
|
a.inlined.borrow_mut().take_unwrap()
|
|
|
|
}).unwrap_or(HashSet::new()),
|
2014-04-28 20:36:08 -07:00
|
|
|
};
|
2014-02-05 22:15:24 +01:00
|
|
|
cache.stack.push(krate.name.clone());
|
|
|
|
krate = cache.fold_crate(krate);
|
2014-04-09 15:52:31 +09:00
|
|
|
|
2014-05-28 19:53:37 -07:00
|
|
|
// Cache where all our extern crates are located
|
2014-05-27 17:12:48 -07:00
|
|
|
for &(n, ref e) in krate.externs.iter() {
|
|
|
|
cache.extern_locations.insert(n, extern_location(e, &cx.dst));
|
|
|
|
let did = ast::DefId { krate: n, node: ast::CRATE_NODE_ID };
|
|
|
|
cache.paths.insert(did, (vec![e.name.to_string()], item_type::Module));
|
|
|
|
}
|
|
|
|
|
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() {
|
|
|
|
for &prim in e.primitives.iter() {
|
|
|
|
cache.primitive_locations.insert(prim, n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for &prim in krate.primitives.iter() {
|
|
|
|
cache.primitive_locations.insert(prim, ast::LOCAL_CRATE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
cache_key.replace(Some(cache.clone()));
|
2014-05-27 17:52:40 -07:00
|
|
|
current_location_key.replace(Some(Vec::new()));
|
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
|
2014-05-27 17:15:10 -07:00
|
|
|
cx.krate(krate)
|
2014-05-27 17:12:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn build_index(krate: &clean::Crate, cache: &mut Cache) -> io::IoResult<String> {
|
|
|
|
// 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.
|
2014-05-27 17:12:48 -07:00
|
|
|
for &(pid, ref item) in orphan_methods.iter() {
|
2014-05-09 13:52:17 -07:00
|
|
|
let did = ast_util::local_def(pid);
|
|
|
|
match paths.find(&did) {
|
2014-03-07 17:26:06 +08:00
|
|
|
Some(&(ref fqp, _)) => {
|
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(),
|
2014-05-12 13:44:59 -07:00
|
|
|
path: fqp.slice_to(fqp.len() - 1).connect("::")
|
2014-05-25 03:17:19 -07:00
|
|
|
.to_string(),
|
|
|
|
desc: shorter(item.doc_value()).to_string(),
|
2014-05-22 22:00:18 -07:00
|
|
|
parent: Some(did),
|
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.
|
2014-05-27 17:12:48 -07: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
|
|
|
|
let mut w = MemWriter::new();
|
|
|
|
try!(write!(&mut w, r#"searchIndex['{}'] = \{"items":["#, krate.name));
|
|
|
|
|
|
|
|
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;
|
|
|
|
if lastpath.as_slice() == item.path.as_slice() {
|
|
|
|
path = "";
|
|
|
|
} else {
|
|
|
|
lastpath = item.path.to_string();
|
|
|
|
path = item.path.as_slice();
|
|
|
|
};
|
2014-04-10 02:24:00 +09:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
if i > 0 {
|
|
|
|
try!(write!(&mut w, ","));
|
|
|
|
}
|
|
|
|
try!(write!(&mut w, r#"[{:u},"{}","{}",{}"#,
|
|
|
|
item.ty, item.name, path,
|
|
|
|
item.desc.to_json().to_str()));
|
|
|
|
match item.parent {
|
|
|
|
Some(nodeid) => {
|
|
|
|
let pathid = *nodeid_to_pathid.find(&nodeid).unwrap();
|
|
|
|
try!(write!(&mut w, ",{}", pathid));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-27 17:12:48 -07:00
|
|
|
None => {}
|
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() {
|
|
|
|
let &(ref fqp, short) = cache.paths.find(&did).unwrap();
|
|
|
|
if i > 0 {
|
|
|
|
try!(write!(&mut w, ","));
|
2014-03-16 01:08:56 -07:00
|
|
|
}
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write!(&mut w, r#"[{:u},"{}"]"#,
|
|
|
|
short, *fqp.last().unwrap()));
|
|
|
|
}
|
2014-04-10 02:24:00 +09:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
try!(write!(&mut w, r"]\};"));
|
2014-03-16 01:08:56 -07:00
|
|
|
|
2014-05-27 17:12:48 -07:00
|
|
|
Ok(str::from_utf8(w.unwrap().as_slice()).unwrap().to_string())
|
|
|
|
}
|
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,
|
|
|
|
search_index: String) -> io::IoResult<()> {
|
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"),
|
|
|
|
include_bin!("static/jquery-2.1.0.min.js")));
|
|
|
|
try!(write(cx.dst.join("main.js"), include_bin!("static/main.js")));
|
|
|
|
try!(write(cx.dst.join("main.css"), include_bin!("static/main.css")));
|
|
|
|
try!(write(cx.dst.join("normalize.css"),
|
|
|
|
include_bin!("static/normalize.css")));
|
|
|
|
try!(write(cx.dst.join("FiraSans-Regular.woff"),
|
|
|
|
include_bin!("static/FiraSans-Regular.woff")));
|
|
|
|
try!(write(cx.dst.join("FiraSans-Medium.woff"),
|
|
|
|
include_bin!("static/FiraSans-Medium.woff")));
|
|
|
|
try!(write(cx.dst.join("Heuristica-Regular.woff"),
|
|
|
|
include_bin!("static/Heuristica-Regular.woff")));
|
|
|
|
try!(write(cx.dst.join("Heuristica-Italic.woff"),
|
|
|
|
include_bin!("static/Heuristica-Italic.woff")));
|
|
|
|
try!(write(cx.dst.join("Heuristica-Bold.woff"),
|
|
|
|
include_bin!("static/Heuristica-Bold.woff")));
|
|
|
|
|
|
|
|
fn collect(path: &Path, krate: &str,
|
|
|
|
key: &str) -> io::IoResult<Vec<String>> {
|
|
|
|
let mut ret = Vec::new();
|
|
|
|
if path.exists() {
|
|
|
|
for line in BufferedReader::new(File::open(path)).lines() {
|
|
|
|
let line = try!(line);
|
|
|
|
if !line.as_slice().starts_with(key) {
|
|
|
|
continue
|
2014-03-16 01:08:56 -07:00
|
|
|
}
|
2014-05-27 17:12:48 -07:00
|
|
|
if line.as_slice().starts_with(
|
|
|
|
format!("{}['{}']", key, krate).as_slice()) {
|
|
|
|
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");
|
|
|
|
let all_indexes = try!(collect(&dst, krate.name.as_slice(),
|
|
|
|
"searchIndex"));
|
|
|
|
let mut w = try!(File::create(&dst));
|
|
|
|
try!(writeln!(&mut w, r"var searchIndex = \{\};"));
|
|
|
|
try!(writeln!(&mut w, "{}", search_index));
|
|
|
|
for index in all_indexes.iter() {
|
|
|
|
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));
|
|
|
|
for (&did, imps) in cache.implementors.iter() {
|
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...
|
|
|
|
let &(ref remote_path, remote_item_type) = match cache.paths.find(&did) {
|
|
|
|
Some(p) => p,
|
|
|
|
None => continue,
|
|
|
|
};
|
2014-05-27 17:12:48 -07:00
|
|
|
|
|
|
|
let mut mydst = dst.clone();
|
|
|
|
for part in remote_path.slice_to(remote_path.len() - 1).iter() {
|
|
|
|
mydst.push(part.as_slice());
|
|
|
|
try!(mkdir(&mydst));
|
|
|
|
}
|
|
|
|
mydst.push(format!("{}.{}.js",
|
|
|
|
remote_item_type.to_static_str(),
|
|
|
|
*remote_path.get(remote_path.len() - 1)));
|
|
|
|
let all_implementors = try!(collect(&mydst, krate.name.as_slice(),
|
|
|
|
"implementors"));
|
|
|
|
|
|
|
|
try!(mkdir(&mydst.dir_path()));
|
|
|
|
let mut f = BufferedWriter::new(try!(File::create(&mydst)));
|
|
|
|
try!(writeln!(&mut f, r"(function() \{var implementors = \{\};"));
|
|
|
|
|
|
|
|
for implementor in all_implementors.iter() {
|
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));
|
2014-05-27 17:12:48 -07:00
|
|
|
for imp in imps.iter() {
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(write!(&mut f, r#""impl{} {} for {}","#,
|
|
|
|
imp.generics, imp.trait_, imp.for_));
|
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;
|
|
|
|
}
|
|
|
|
"));
|
|
|
|
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,
|
|
|
|
krate: clean::Crate) -> io::IoResult<clean::Crate> {
|
|
|
|
info!("emitting source files");
|
|
|
|
let dst = cx.dst.join("src");
|
|
|
|
try!(mkdir(&dst));
|
|
|
|
let dst = dst.join(krate.name.as_slice());
|
|
|
|
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.
|
2014-05-02 16:03:09 -07:00
|
|
|
fn write(dst: Path, contents: &[u8]) -> io::IoResult<()> {
|
|
|
|
File::create(&dst).write(contents)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Makes a directory on the filesystem, failing the task if an error occurs and
|
|
|
|
/// skipping if the directory already exists.
|
2014-01-30 11:30:21 -08:00
|
|
|
fn mkdir(path: &Path) -> io::IoResult<()> {
|
|
|
|
if !path.exists() {
|
|
|
|
fs::mkdir(path, io::UserRWX)
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
/// static HTML tree.
|
|
|
|
// FIXME (#9639): The closure should deal with &[u8] instead of &str
|
2013-11-19 17:36:32 -08:00
|
|
|
fn clean_srcpath(src: &[u8], f: |&str|) {
|
2013-12-03 19:15:12 -08:00
|
|
|
let p = Path::new(src);
|
2013-09-26 17:21:59 -07:00
|
|
|
if p.as_vec() != bytes!(".") {
|
2013-11-23 11:18:51 +01:00
|
|
|
for c in p.str_components().map(|x|x.unwrap()) {
|
2013-09-26 17:21:59 -07:00
|
|
|
if ".." == c {
|
|
|
|
f("up");
|
|
|
|
} else {
|
|
|
|
f(c.as_slice())
|
|
|
|
}
|
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
|
2013-10-05 19:49:32 -07:00
|
|
|
let local_location = dst.join(e.name.as_slice());
|
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
|
|
|
|
for attr in e.attrs.iter() {
|
|
|
|
match *attr {
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::List(ref x, ref list) if "doc" == x.as_slice() => {
|
2013-10-02 15:39:32 -07:00
|
|
|
for attr in list.iter() {
|
|
|
|
match *attr {
|
2014-05-12 13:44:59 -07:00
|
|
|
clean::NameValue(ref x, ref s)
|
|
|
|
if "html_root_url" == x.as_slice() => {
|
|
|
|
if s.as_slice().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
|
|
|
|
.include_sources = match self.emit_source(item.source
|
|
|
|
.filename
|
|
|
|
.as_slice()) {
|
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.
|
2014-01-30 11:30:21 -08:00
|
|
|
fn emit_source(&mut self, filename: &str) -> io::IoResult<()> {
|
2013-12-03 19:15:12 -08:00
|
|
|
let p = Path::new(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.
|
|
|
|
let contents = match File::open(&p).read_to_end() {
|
|
|
|
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)
|
|
|
|
};
|
2014-03-26 09:24:16 -07:00
|
|
|
let contents = str::from_utf8(contents.as_slice()).unwrap();
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2014-03-18 08:59:44 +08:00
|
|
|
// Remove the utf-8 BOM if any
|
|
|
|
let contents = if contents.starts_with("\ufeff") {
|
|
|
|
contents.as_slice().slice_from(3)
|
|
|
|
} else {
|
|
|
|
contents.as_slice()
|
|
|
|
};
|
|
|
|
|
2013-09-27 15:12:23 -07:00
|
|
|
// Create the intermediate directories
|
|
|
|
let mut cur = self.dst.clone();
|
2014-05-22 16:57:53 -07:00
|
|
|
let mut root_path = String::from_str("../../");
|
2013-11-21 15:42:55 -08:00
|
|
|
clean_srcpath(p.dirname(), |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
|
|
|
|
2014-05-07 21:53:11 -07:00
|
|
|
cur.push(Vec::from_slice(p.filename().expect("source has no filename"))
|
|
|
|
.append(bytes!(".html")));
|
2014-02-19 10:07:49 -08:00
|
|
|
let mut w = BufferedWriter::new(try!(File::create(&cur)));
|
2013-09-27 15:12:23 -07:00
|
|
|
|
2014-02-07 19:45:48 -08:00
|
|
|
let title = format!("{} -- source", cur.filename_display());
|
2013-09-27 15:12:23 -07:00
|
|
|
let page = layout::Page {
|
2014-05-16 10:45:16 -07:00
|
|
|
title: title.as_slice(),
|
2013-09-27 15:12:23 -07:00
|
|
|
ty: "source",
|
2014-04-02 16:54:22 -07:00
|
|
|
root_path: root_path.as_slice(),
|
2013-09-27 15:12:23 -07:00
|
|
|
};
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(layout::render(&mut w as &mut Writer, &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-01-10 15:10:52 -08:00
|
|
|
self.privmod = prev || 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),
|
|
|
|
clean::TypedefItem(ref t) => self.generics(&t.generics),
|
|
|
|
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
|
|
|
|
match item.inner {
|
|
|
|
clean::TraitItem(ref t) => {
|
2014-05-03 02:08:58 -07:00
|
|
|
self.traits.insert(item.def_id, t.clone());
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all the implementors of traits.
|
|
|
|
match item.inner {
|
|
|
|
clean::ImplItem(ref i) => {
|
|
|
|
match i.trait_ {
|
2014-05-21 16:41:58 -07:00
|
|
|
Some(clean::ResolvedPath{ did, .. }) => {
|
2014-05-03 02:08:58 -07:00
|
|
|
let v = self.implementors.find_or_insert_with(did, |_| {
|
|
|
|
Vec::new()
|
|
|
|
});
|
2014-05-27 17:52:40 -07:00
|
|
|
v.push(Implementor {
|
|
|
|
generics: i.generics.clone(),
|
|
|
|
trait_: i.trait_.get_ref().clone(),
|
|
|
|
for_: i.for_.clone(),
|
|
|
|
});
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-11-28 12:22:53 -08:00
|
|
|
Some(..) | None => {}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Index this method for searching later on
|
|
|
|
match item.name {
|
|
|
|
Some(ref s) => {
|
|
|
|
let parent = match item.inner {
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::TyMethodItem(..) |
|
|
|
|
clean::StructFieldItem(..) |
|
|
|
|
clean::VariantItem(..) => {
|
2014-03-07 17:26:06 +08:00
|
|
|
(Some(*self.parent_stack.last().unwrap()),
|
|
|
|
Some(self.stack.slice_to(self.stack.len() - 1)))
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-11-28 12:22:53 -08:00
|
|
|
clean::MethodItem(..) => {
|
2013-09-18 22:18:38 -07:00
|
|
|
if self.parent_stack.len() == 0 {
|
2014-03-07 17:26:06 +08:00
|
|
|
(None, None)
|
2013-09-18 22:18:38 -07:00
|
|
|
} else {
|
2013-12-23 15:08:23 +01:00
|
|
|
let last = self.parent_stack.last().unwrap();
|
2014-05-22 22:00:18 -07:00
|
|
|
let did = *last;
|
2014-05-09 13:52:17 -07:00
|
|
|
let path = match self.paths.find(&did) {
|
2014-04-09 16:49:31 +09:00
|
|
|
Some(&(_, item_type::Trait)) =>
|
2014-03-07 17:26:06 +08:00
|
|
|
Some(self.stack.slice_to(self.stack.len() - 1)),
|
|
|
|
// 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-04-09 16:49:31 +09:00
|
|
|
Some(&(ref fqp, item_type::Struct)) |
|
|
|
|
Some(&(ref fqp, item_type::Enum)) =>
|
2014-03-07 17:26:06 +08:00
|
|
|
Some(fqp.slice_to(fqp.len() - 1)),
|
|
|
|
Some(..) => Some(self.stack.as_slice()),
|
|
|
|
None => None
|
2013-09-27 10:45:09 -07:00
|
|
|
};
|
2014-03-07 17:26:06 +08:00
|
|
|
(Some(*last), path)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-03-07 17:26:06 +08:00
|
|
|
_ => (None, Some(self.stack.as_slice()))
|
2013-09-18 22:18:38 -07:00
|
|
|
};
|
|
|
|
match parent {
|
2014-03-07 17:26:06 +08:00
|
|
|
(parent, Some(path)) if !self.privmod => {
|
2013-09-18 22:18:38 -07:00
|
|
|
self.search_index.push(IndexItem {
|
|
|
|
ty: shortty(&item),
|
2014-05-25 03:17:19 -07:00
|
|
|
name: s.to_string(),
|
|
|
|
path: path.connect("::").to_string(),
|
|
|
|
desc: shorter(item.doc_value()).to_string(),
|
2013-09-18 22:18:38 -07:00
|
|
|
parent: parent,
|
|
|
|
});
|
|
|
|
}
|
2014-03-07 17:26:06 +08:00
|
|
|
(Some(parent), None) if !self.privmod => {
|
2014-05-22 22:00:18 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep track of the fully qualified path for this item.
|
|
|
|
let pushed = if item.name.is_some() {
|
|
|
|
let n = item.name.get_ref();
|
|
|
|
if n.len() > 0 {
|
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-05-03 02:08:58 -07:00
|
|
|
self.paths.insert(item.def_id, (stack, item_type::Enum));
|
2014-02-16 14:35:13 -08:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => 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
|
|
|
use clean::{Primitive, Vector, ResolvedPath, BorrowedRef};
|
|
|
|
use clean::{FixedVector, Slice, Tuple, PrimitiveTuple};
|
|
|
|
|
|
|
|
// extract relevant documentation for this impl
|
|
|
|
let dox = match attrs.move_iter().find(|a| {
|
|
|
|
match *a {
|
|
|
|
clean::NameValue(ref x, _)
|
|
|
|
if "doc" == x.as_slice() => {
|
|
|
|
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_ {
|
|
|
|
ResolvedPath { did, .. } => Some(did),
|
|
|
|
|
|
|
|
// References to primitives are picked up as well to
|
|
|
|
// recognize implementations for &str, this may not
|
|
|
|
// be necessary in a DST world.
|
|
|
|
Primitive(p) |
|
|
|
|
BorrowedRef { type_: box Primitive(p), ..} =>
|
|
|
|
{
|
|
|
|
Some(ast_util::local_def(p.to_node_id()))
|
|
|
|
}
|
|
|
|
|
|
|
|
// In a DST world, we may only need
|
|
|
|
// Vector/FixedVector, but for now we also pick up
|
|
|
|
// borrowed references
|
|
|
|
Vector(..) | FixedVector(..) |
|
|
|
|
BorrowedRef{ type_: box Vector(..), .. } |
|
|
|
|
BorrowedRef{ type_: box FixedVector(..), .. } =>
|
|
|
|
{
|
|
|
|
Some(ast_util::local_def(Slice.to_node_id()))
|
|
|
|
}
|
|
|
|
|
|
|
|
Tuple(..) => {
|
|
|
|
let id = PrimitiveTuple.to_node_id();
|
|
|
|
Some(ast_util::local_def(id))
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
match did {
|
|
|
|
Some(did) => {
|
2014-05-23 00:42:33 -07:00
|
|
|
let v = self.impls.find_or_insert_with(did, |_| {
|
2014-03-05 15:28:08 -08:00
|
|
|
Vec::new()
|
2013-11-21 15:42:55 -08:00
|
|
|
});
|
2014-05-28 19:53:37 -07:00
|
|
|
v.push((i, dox));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-28 19:53:37 -07:00
|
|
|
None => {}
|
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) {
|
|
|
|
for typ in generics.type_params.iter() {
|
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-05-22 16:57:53 -07:00
|
|
|
fn recurse<T>(&mut self, s: String, f: |&mut Context| -> T) -> T {
|
2013-09-18 22:18:38 -07:00
|
|
|
if s.len() == 0 {
|
2013-10-21 13:08:31 -07:00
|
|
|
fail!("what {:?}", self);
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-09-26 17:21:59 -07:00
|
|
|
let prev = self.dst.clone();
|
2013-10-05 19:49:32 -07:00
|
|
|
self.dst.push(s.as_slice());
|
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.
|
2014-05-27 17:15:10 -07:00
|
|
|
fn krate(self, mut krate: clean::Crate) -> io::IoResult<()> {
|
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-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-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-10-03 10:24:40 -07:00
|
|
|
/// Non-parellelized version of rendering an item. This will take the input
|
|
|
|
/// 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.
|
2014-01-30 11:30:21 -08:00
|
|
|
fn item(&mut self, item: clean::Item,
|
|
|
|
f: |&mut Context, clean::Item|) -> io::IoResult<()> {
|
2014-05-29 13:50:47 -07:00
|
|
|
fn render(w: io::File, cx: &Context, it: &clean::Item,
|
2014-01-30 11:30:21 -08:00
|
|
|
pushname: bool) -> io::IoResult<()> {
|
2013-12-17 11:19:14 -05:00
|
|
|
info!("Rendering an item to {}", w.path().display());
|
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-04-28 20:36:08 -07:00
|
|
|
current_location_key.replace(Some(cx.current.clone()));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-05-19 23:19:56 -07:00
|
|
|
let mut title = cx.current.connect("::");
|
2013-09-18 22:18:38 -07:00
|
|
|
if pushname {
|
2014-04-02 16:54:22 -07:00
|
|
|
if title.len() > 0 {
|
|
|
|
title.push_str("::");
|
|
|
|
}
|
2014-05-12 13:44:59 -07:00
|
|
|
title.push_str(it.name.get_ref().as_slice());
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
title.push_str(" - Rust");
|
|
|
|
let page = layout::Page {
|
2014-04-09 16:49:31 +09:00
|
|
|
ty: shortty(it).to_static_str(),
|
2014-04-02 16:54:22 -07:00
|
|
|
root_path: cx.root_path.as_slice(),
|
|
|
|
title: title.as_slice(),
|
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
|
|
|
|
// write sycall all the time.
|
|
|
|
let mut writer = BufferedWriter::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 {
|
|
|
|
let mut url = "../".repeat(cx.current.len());
|
|
|
|
match cache_key.get().unwrap().paths.find(&it.def_id) {
|
|
|
|
Some(&(ref names, _)) => {
|
|
|
|
for name in names.slice_to(names.len() - 1).iter() {
|
|
|
|
url.push_str(name.as_slice());
|
|
|
|
url.push_str("/");
|
|
|
|
}
|
|
|
|
url.push_str(item_path(it).as_slice());
|
|
|
|
try!(layout::redirect(&mut writer, url.as_slice()));
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
writer.flush()
|
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-05-29 13:50:47 -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 {
|
|
|
|
self.render_redirect_pages = ignore_private_module(&item);
|
|
|
|
}
|
|
|
|
|
2014-05-25 03:17:19 -07:00
|
|
|
let name = item.name.get_ref().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| {
|
2013-12-04 21:28:47 -08: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!()
|
|
|
|
};
|
|
|
|
this.sidebar = build_sidebar(&m);
|
|
|
|
for item in m.items.move_iter() {
|
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() => {
|
2013-10-05 19:49:32 -07: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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
fn href(&self) -> Option<String> {
|
|
|
|
// 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.
|
2014-05-23 20:17:27 -07:00
|
|
|
if ast_util::is_local(self.item.def_id) {
|
|
|
|
let mut path = Vec::new();
|
|
|
|
clean_srcpath(self.item.source.filename.as_bytes(), |component| {
|
2014-05-25 03:10:11 -07:00
|
|
|
path.push(component.to_string());
|
2014-05-23 20:17:27 -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)
|
|
|
|
};
|
|
|
|
Some(format!("{root}src/{krate}/{path}.html\\#{href}",
|
|
|
|
root = self.cx.root_path,
|
|
|
|
krate = self.cx.layout.krate,
|
|
|
|
path = path.connect("/"),
|
|
|
|
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-05-23 20:17:27 -07:00
|
|
|
let cache = cache_key.get().unwrap();
|
|
|
|
let path = cache.external_paths.get(&self.item.def_id);
|
|
|
|
let root = match *cache.extern_locations.get(&self.item.def_id.krate) {
|
2014-05-25 03:10:11 -07:00
|
|
|
Remote(ref s) => s.to_string(),
|
2014-05-27 22:52:00 +10:00
|
|
|
Local => self.cx.root_path.clone(),
|
2014-05-23 20:17:27 -07:00
|
|
|
Unknown => return None,
|
|
|
|
};
|
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,
|
|
|
|
path = path.slice_to(path.len() - 1).connect("/"),
|
|
|
|
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-01-30 00:46:37 +11:00
|
|
|
impl<'a> fmt::Show 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-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "\n<h1 class='fqn'>"));
|
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 {
|
|
|
|
let cur = self.cx.current.as_slice();
|
|
|
|
let amt = if self.ismodule() { cur.len() - 1 } else { cur.len() };
|
|
|
|
for (i, component) in cur.iter().enumerate().take(amt) {
|
|
|
|
try!(write!(fmt, "<a href='{}index.html'>{}</a>::",
|
|
|
|
"../".repeat(cur.len() - i - 1),
|
|
|
|
component.as_slice()));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<a class='{}' href=''>{}</a>",
|
|
|
|
shortty(self.item), self.item.name.get_ref().as_slice()));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2014-04-25 17:34:32 +09:00
|
|
|
// Write stability attributes
|
2014-05-21 00:05:45 -07:00
|
|
|
match attr::find_stability_generic(self.item.attrs.iter()) {
|
|
|
|
Some((ref stability, _)) => {
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt,
|
2014-04-25 17:34:32 +09:00
|
|
|
"<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
|
|
|
|
lvl = stability.level.to_str(),
|
|
|
|
reason = match stability.text {
|
|
|
|
Some(ref s) => (*s).clone(),
|
|
|
|
None => InternedString::new(""),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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-05-24 11:56:38 -07:00
|
|
|
match self.href() {
|
2014-05-23 20:17:27 -07:00
|
|
|
Some(l) => {
|
|
|
|
try!(write!(fmt,
|
|
|
|
"<a class='source' id='src-{}' \
|
|
|
|
href='{}'>[src]</a>",
|
|
|
|
self.item.def_id.node, l));
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2014-04-12 21:39:12 +02: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) => {
|
2014-05-10 14:05:06 -07:00
|
|
|
item_module(fmt, self.cx, self.item, m.items.as_slice())
|
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),
|
|
|
|
clean::TypedefItem(ref t) => item_typedef(fmt, self.item, t),
|
|
|
|
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-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-05-27 20:44:58 -07:00
|
|
|
format!("{}/index.html", item.name.get_ref())
|
2014-05-12 13:44:59 -07:00
|
|
|
}
|
|
|
|
_ => {
|
2014-05-27 20:44:58 -07:00
|
|
|
format!("{}.{}.html",
|
|
|
|
shortty(item).to_static_str(),
|
|
|
|
*item.name.get_ref())
|
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 {
|
2014-05-19 23:19:56 -07:00
|
|
|
let mut s = cx.current.connect("::");
|
2013-09-18 22:18:38 -07:00
|
|
|
s.push_str("::");
|
|
|
|
s.push_str(item.name.get_ref().as_slice());
|
2014-05-12 13:44:59 -07:00
|
|
|
return s
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn blank<'a>(s: Option<&'a str>) -> &'a str {
|
|
|
|
match s {
|
|
|
|
Some(s) => s,
|
|
|
|
None => ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn shorter<'a>(s: Option<&'a str>) -> &'a str {
|
|
|
|
match s {
|
|
|
|
Some(s) => match s.find_str("\n\n") {
|
|
|
|
Some(pos) => s.slice_to(pos),
|
|
|
|
None => s,
|
|
|
|
},
|
|
|
|
None => ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result {
|
2013-09-18 22:18:38 -07:00
|
|
|
match item.doc_value() {
|
|
|
|
Some(s) => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
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-05-29 13:50:47 -07:00
|
|
|
let mut indices = range(0, items.len()).filter(|i| {
|
|
|
|
!ignore_private_module(&items[*i])
|
|
|
|
}).collect::<Vec<uint>>();
|
2013-09-18 22:18:38 -07:00
|
|
|
|
2013-12-20 14:42:00 +11:00
|
|
|
fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> Ordering {
|
2013-09-18 22:18:38 -07:00
|
|
|
if shortty(i1) == shortty(i2) {
|
2013-12-20 14:42:00 +11:00
|
|
|
return i1.name.cmp(&i2.name);
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
match (&i1.inner, &i2.inner) {
|
2013-09-24 13:55:22 -07:00
|
|
|
(&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => {
|
|
|
|
match (&a.inner, &b.inner) {
|
2014-03-07 15:57:45 +08:00
|
|
|
(&clean::ExternCrate(..), _) => Less,
|
|
|
|
(_, &clean::ExternCrate(..)) => Greater,
|
2013-12-20 14:42:00 +11:00
|
|
|
_ => idx1.cmp(&idx2),
|
2013-09-24 13:55:22 -07:00
|
|
|
}
|
|
|
|
}
|
2013-12-20 14:42:00 +11:00
|
|
|
(&clean::ViewItemItem(..), _) => Less,
|
|
|
|
(_, &clean::ViewItemItem(..)) => Greater,
|
2014-05-28 19:53:37 -07:00
|
|
|
(&clean::PrimitiveItem(..), _) => Less,
|
|
|
|
(_, &clean::PrimitiveItem(..)) => Greater,
|
2013-12-20 14:42:00 +11:00
|
|
|
(&clean::ModuleItem(..), _) => Less,
|
|
|
|
(_, &clean::ModuleItem(..)) => Greater,
|
2014-02-16 21:40:26 -08:00
|
|
|
(&clean::MacroItem(..), _) => Less,
|
|
|
|
(_, &clean::MacroItem(..)) => Greater,
|
2013-12-20 14:42:00 +11:00
|
|
|
(&clean::StructItem(..), _) => Less,
|
|
|
|
(_, &clean::StructItem(..)) => Greater,
|
|
|
|
(&clean::EnumItem(..), _) => Less,
|
|
|
|
(_, &clean::EnumItem(..)) => Greater,
|
|
|
|
(&clean::StaticItem(..), _) => Less,
|
|
|
|
(_, &clean::StaticItem(..)) => Greater,
|
|
|
|
(&clean::ForeignFunctionItem(..), _) => Less,
|
|
|
|
(_, &clean::ForeignFunctionItem(..)) => Greater,
|
|
|
|
(&clean::ForeignStaticItem(..), _) => Less,
|
|
|
|
(_, &clean::ForeignStaticItem(..)) => Greater,
|
|
|
|
(&clean::TraitItem(..), _) => Less,
|
|
|
|
(_, &clean::TraitItem(..)) => Greater,
|
|
|
|
(&clean::FunctionItem(..), _) => Less,
|
|
|
|
(_, &clean::FunctionItem(..)) => Greater,
|
|
|
|
(&clean::TypedefItem(..), _) => Less,
|
|
|
|
(_, &clean::TypedefItem(..)) => Greater,
|
|
|
|
_ => idx1.cmp(&idx2),
|
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
|
|
|
|
2013-10-21 13:08:31 -07:00
|
|
|
debug!("{:?}", indices);
|
2014-04-09 16:49:31 +09:00
|
|
|
let mut curty = None;
|
2013-09-18 22:18:38 -07:00
|
|
|
for &idx in indices.iter() {
|
|
|
|
let myitem = &items[idx];
|
|
|
|
|
2014-04-09 16:49:31 +09:00
|
|
|
let myty = Some(shortty(myitem));
|
2013-09-18 22:18:38 -07:00
|
|
|
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-03-04 11:24:20 -08:00
|
|
|
let (short, name) = match myitem.inner {
|
|
|
|
clean::ModuleItem(..) => ("modules", "Modules"),
|
|
|
|
clean::StructItem(..) => ("structs", "Structs"),
|
|
|
|
clean::EnumItem(..) => ("enums", "Enums"),
|
|
|
|
clean::FunctionItem(..) => ("functions", "Functions"),
|
|
|
|
clean::TypedefItem(..) => ("types", "Type Definitions"),
|
|
|
|
clean::StaticItem(..) => ("statics", "Statics"),
|
|
|
|
clean::TraitItem(..) => ("traits", "Traits"),
|
|
|
|
clean::ImplItem(..) => ("impls", "Implementations"),
|
|
|
|
clean::ViewItemItem(..) => ("reexports", "Reexports"),
|
|
|
|
clean::TyMethodItem(..) => ("tymethods", "Type Methods"),
|
|
|
|
clean::MethodItem(..) => ("methods", "Methods"),
|
|
|
|
clean::StructFieldItem(..) => ("fields", "Struct Fields"),
|
|
|
|
clean::VariantItem(..) => ("variants", "Variants"),
|
|
|
|
clean::ForeignFunctionItem(..) => ("ffi-fns", "Foreign Functions"),
|
|
|
|
clean::ForeignStaticItem(..) => ("ffi-statics", "Foreign Statics"),
|
|
|
|
clean::MacroItem(..) => ("macros", "Macros"),
|
2014-05-28 19:53:37 -07:00
|
|
|
clean::PrimitiveItem(..) => ("primitives", "Primitive Types"),
|
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-03-12 23:09:03 +11: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
|
|
|
}
|
|
|
|
|
|
|
|
match myitem.inner {
|
2013-09-26 11:57:25 -07:00
|
|
|
clean::StaticItem(ref s) | clean::ForeignStaticItem(ref s) => {
|
2014-05-02 17:08:11 -07:00
|
|
|
struct Initializer<'a>(&'a str, Item<'a>);
|
2014-01-30 00:46:37 +11:00
|
|
|
impl<'a> fmt::Show for Initializer<'a> {
|
2014-02-05 23:55:13 +11:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
2014-05-02 17:08:11 -07:00
|
|
|
let Initializer(s, item) = *self;
|
2014-01-30 11:30:21 -08:00
|
|
|
if s.len() == 0 { return Ok(()); }
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(f, "<code> = </code>"));
|
2014-05-02 17:08:11 -07:00
|
|
|
if s.contains("\n") {
|
2014-05-29 09:58:09 -07:00
|
|
|
match item.href() {
|
|
|
|
Some(url) => {
|
|
|
|
write!(f, "<a href='{}'>[definition]</a>",
|
|
|
|
url)
|
|
|
|
}
|
|
|
|
None => Ok(()),
|
|
|
|
}
|
2014-05-02 17:08:11 -07:00
|
|
|
} else {
|
2014-05-10 14:05:06 -07:00
|
|
|
write!(f, "<code>{}</code>", s.as_slice())
|
2014-05-02 17:08:11 -07:00
|
|
|
}
|
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
|
|
|
<tr>
|
2013-09-26 11:57:25 -07:00
|
|
|
<td><code>{}static {}: {}</code>{}</td>
|
2013-09-18 22:18:38 -07:00
|
|
|
<td class='docblock'>{} </td>
|
|
|
|
</tr>
|
|
|
|
",
|
2013-09-24 13:56:52 -07:00
|
|
|
VisSpace(myitem.visibility),
|
2013-09-18 22:18:38 -07:00
|
|
|
*myitem.name.get_ref(),
|
|
|
|
s.type_,
|
2014-05-12 13:44:59 -07:00
|
|
|
Initializer(s.expr.as_slice(), Item { cx: cx, item: myitem }),
|
2014-01-30 11:30:21 -08:00
|
|
|
Markdown(blank(myitem.doc_value()))));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-09-24 13:56:52 -07:00
|
|
|
clean::ViewItemItem(ref item) => {
|
|
|
|
match item.inner {
|
2014-03-07 15:57:45 +08:00
|
|
|
clean::ExternCrate(ref name, ref src, _) => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<tr><td><code>extern crate {}",
|
2014-01-30 11:30:21 -08:00
|
|
|
name.as_slice()));
|
2013-09-24 13:56:52 -07:00
|
|
|
match *src {
|
2014-02-19 10:07:49 -08:00
|
|
|
Some(ref src) => try!(write!(w, " = \"{}\"",
|
2014-01-30 11:30:21 -08:00
|
|
|
src.as_slice())),
|
2013-09-24 13:56:52 -07:00
|
|
|
None => {}
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, ";</code></td></tr>"));
|
2013-09-24 13:56:52 -07:00
|
|
|
}
|
|
|
|
|
2014-04-26 22:33:45 +09:00
|
|
|
clean::Import(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 }
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "
|
2013-09-18 22:18:38 -07:00
|
|
|
<tr>
|
|
|
|
<td><a class='{class}' href='{href}'
|
|
|
|
title='{title}'>{}</a></td>
|
|
|
|
<td class='docblock short'>{}</td>
|
|
|
|
</tr>
|
|
|
|
",
|
|
|
|
*myitem.name.get_ref(),
|
|
|
|
Markdown(shorter(myitem.doc_value())),
|
|
|
|
class = shortty(myitem),
|
|
|
|
href = item_path(myitem),
|
2014-01-30 11:30:21 -08:00
|
|
|
title = full_path(cx, myitem)));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
write!(w, "</table>")
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
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 {
|
2014-04-06 18:04:40 -07:00
|
|
|
try!(write!(w, "<pre class='rust fn'>{vis}{fn_style}fn \
|
2014-02-20 01:14:51 -08:00
|
|
|
{name}{generics}{decl}</pre>",
|
2013-09-18 22:18:38 -07:00
|
|
|
vis = VisSpace(it.visibility),
|
2014-04-06 18:04:40 -07:00
|
|
|
fn_style = FnStyleSpace(f.fn_style),
|
2013-09-18 22:18:38 -07:00
|
|
|
name = it.name.get_ref().as_slice(),
|
|
|
|
generics = 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-05-22 16:57:53 -07:00
|
|
|
let mut parents = String::new();
|
2013-09-18 22:18:38 -07:00
|
|
|
if t.parents.len() > 0 {
|
|
|
|
parents.push_str(": ");
|
|
|
|
for (i, p) in t.parents.iter().enumerate() {
|
|
|
|
if i > 0 { parents.push_str(" + "); }
|
2014-05-16 10:45:16 -07:00
|
|
|
parents.push_str(format!("{}", *p).as_slice());
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the trait definition
|
2014-02-20 01:14:51 -08:00
|
|
|
try!(write!(w, "<pre class='rust trait'>{}trait {}{}{} ",
|
2014-01-30 11:30:21 -08:00
|
|
|
VisSpace(it.visibility),
|
|
|
|
it.name.get_ref().as_slice(),
|
|
|
|
t.generics,
|
|
|
|
parents));
|
2014-04-09 20:02:26 +10:00
|
|
|
let required = t.methods.iter().filter(|m| m.is_req()).collect::<Vec<&clean::TraitMethod>>();
|
|
|
|
let provided = t.methods.iter().filter(|m| !m.is_req()).collect::<Vec<&clean::TraitMethod>>();
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
if t.methods.len() == 0 {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "\\{ \\}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
} else {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "\\{\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
for m in required.iter() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " "));
|
|
|
|
try!(render_method(w, m.item()));
|
|
|
|
try!(write!(w, ";\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
if required.len() > 0 && provided.len() > 0 {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(w.write("\n".as_bytes()));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
for m in provided.iter() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " "));
|
|
|
|
try!(render_method(w, m.item()));
|
|
|
|
try!(write!(w, " \\{ ... \\}\n"));
|
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
|
|
|
}
|
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
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn meth(w: &mut fmt::Formatter, m: &clean::TraitMethod) -> fmt::Result {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h3 id='{}.{}' class='method'><code>",
|
2014-01-30 11:30:21 -08:00
|
|
|
shortty(m.item()),
|
|
|
|
*m.item().name.get_ref()));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(render_method(w, m.item()));
|
|
|
|
try!(write!(w, "</code></h3>"));
|
|
|
|
try!(document(w, m.item()));
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Output the documentation for each function individually
|
|
|
|
if required.len() > 0 {
|
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
|
|
|
"));
|
2013-09-18 22:18:38 -07:00
|
|
|
for m in required.iter() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(meth(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
|
|
|
}
|
|
|
|
if provided.len() > 0 {
|
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
|
|
|
"));
|
2013-09-18 22:18:38 -07:00
|
|
|
for m in provided.iter() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(meth(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
|
|
|
}
|
|
|
|
|
2014-05-27 17:52:40 -07:00
|
|
|
let cache = cache_key.get().unwrap();
|
|
|
|
try!(write!(w, "
|
|
|
|
<h2 id='implementors'>Implementors</h2>
|
|
|
|
<ul class='item-list' id='implementors-list'>
|
|
|
|
"));
|
|
|
|
match cache.implementors.find(&it.def_id) {
|
2014-04-28 20:36:08 -07:00
|
|
|
Some(implementors) => {
|
|
|
|
for i in implementors.iter() {
|
2014-05-27 17:52:40 -07:00
|
|
|
try!(writeln!(w, "<li><code>impl{} {} for {}</code></li>",
|
|
|
|
i.generics, i.trait_, i.for_));
|
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>"#,
|
2014-05-27 17:52:40 -07:00
|
|
|
root_path = Vec::from_elem(cx.current.len(), "..").connect("/"),
|
|
|
|
path = if ast_util::is_local(it.def_id) {
|
|
|
|
cx.current.connect("/")
|
|
|
|
} else {
|
|
|
|
let path = cache.external_paths.get(&it.def_id);
|
|
|
|
path.slice_to(path.len() - 1).connect("/")
|
|
|
|
},
|
|
|
|
ty = shortty(it).to_static_str(),
|
|
|
|
name = *it.name.get_ref()));
|
2014-04-28 20:36:08 -07:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result {
|
|
|
|
fn fun(w: &mut fmt::Formatter, it: &clean::Item, fn_style: ast::FnStyle,
|
2014-02-16 22:40:08 -08:00
|
|
|
g: &clean::Generics, selfty: &clean::SelfTy,
|
|
|
|
d: &clean::FnDecl) -> fmt::Result {
|
|
|
|
write!(w, "{}fn <a href='\\#{ty}.{name}' class='fnname'>{name}</a>\
|
|
|
|
{generics}{decl}",
|
2014-04-06 18:04:40 -07:00
|
|
|
match fn_style {
|
2014-01-09 15:05:33 +02:00
|
|
|
ast::UnsafeFn => "unsafe ",
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => "",
|
|
|
|
},
|
2013-09-27 10:40:41 -07:00
|
|
|
ty = shortty(it),
|
2013-09-18 22:18:38 -07:00
|
|
|
name = it.name.get_ref().as_slice(),
|
|
|
|
generics = *g,
|
2014-02-16 22:40:08 -08:00
|
|
|
decl = Method(selfty, d))
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
match meth.inner {
|
|
|
|
clean::TyMethodItem(ref m) => {
|
2014-04-06 18:04:40 -07:00
|
|
|
fun(w, meth, m.fn_style, &m.generics, &m.self_, &m.decl)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
clean::MethodItem(ref m) => {
|
2014-04-06 18:04:40 -07:00
|
|
|
fun(w, meth, m.fn_style, &m.generics, &m.self_, &m.decl)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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'>"));
|
2014-03-05 15:28:08 -08:00
|
|
|
try!(render_struct(w,
|
|
|
|
it,
|
|
|
|
Some(&s.generics),
|
|
|
|
s.struct_type,
|
|
|
|
s.fields.as_slice(),
|
|
|
|
"",
|
|
|
|
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();
|
2013-09-30 16:31:35 -07:00
|
|
|
match s.struct_type {
|
2014-04-19 22:24:52 -07:00
|
|
|
doctree::Plain if fields.peek().is_some() => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h2 class='fields'>Fields</h2>\n<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 id='structfield.{name}'>\
|
2014-01-30 11:30:21 -08:00
|
|
|
<code>{name}</code></td><td>",
|
|
|
|
name = field.name.get_ref().as_slice()));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(document(w, field));
|
|
|
|
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
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
render_methods(w, it)
|
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 {
|
2014-02-20 01:14:51 -08:00
|
|
|
try!(write!(w, "<pre class='rust enum'>{}enum {}{}",
|
2014-01-30 11:30:21 -08:00
|
|
|
VisSpace(it.visibility),
|
|
|
|
it.name.get_ref().as_slice(),
|
|
|
|
e.generics));
|
2013-12-16 21:30:02 -08:00
|
|
|
if e.variants.len() == 0 && !e.variants_stripped {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " \\{\\}"));
|
2013-09-18 22:18:38 -07:00
|
|
|
} else {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " \\{\n"));
|
2013-09-18 22:18:38 -07:00
|
|
|
for v in e.variants.iter() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " "));
|
2013-09-30 16:31:35 -07:00
|
|
|
let name = v.name.get_ref().as_slice();
|
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,
|
|
|
|
s.fields.as_slice(),
|
|
|
|
" ",
|
|
|
|
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-02-19 10:07:49 -08: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));
|
2013-09-30 16:31:35 -07:00
|
|
|
if e.variants.len() > 0 {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h2 class='variants'>Variants</h2>\n<table>"));
|
2013-09-30 16:31:35 -07:00
|
|
|
for variant in e.variants.iter() {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<tr><td id='variant.{name}'><code>{name}</code></td><td>",
|
2014-01-30 11:30:21 -08:00
|
|
|
name = variant.name.get_ref().as_slice()));
|
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) => {
|
2014-04-19 22:24:52 -07:00
|
|
|
let mut fields = s.fields.iter().filter(|f| {
|
|
|
|
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>",
|
|
|
|
v = variant.name.get_ref().as_slice(),
|
|
|
|
f = field.name.get_ref().as_slice()));
|
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
|
|
|
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(render_methods(w, it));
|
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 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 {""},
|
|
|
|
it.name.get_ref().as_slice()));
|
2013-09-18 22:18:38 -07:00
|
|
|
match g {
|
2014-02-19 10:07:49 -08:00
|
|
|
Some(g) => try!(write!(w, "{}", *g)),
|
2013-09-18 22:18:38 -07:00
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
match ty {
|
|
|
|
doctree::Plain => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, " \\{\n{}", tab));
|
2014-04-19 22:24:52 -07:00
|
|
|
let mut fields_stripped = false;
|
2013-09-18 22:18:38 -07:00
|
|
|
for field in fields.iter() {
|
|
|
|
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),
|
|
|
|
field.name.get_ref().as_slice(),
|
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-02-19 10:07:49 -08: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
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn render_methods(w: &mut fmt::Formatter, it: &clean::Item) -> fmt::Result {
|
2014-05-23 00:42:33 -07:00
|
|
|
match cache_key.get().unwrap().impls.find(&it.def_id) {
|
2014-04-28 20:36:08 -07:00
|
|
|
Some(v) => {
|
|
|
|
let mut non_trait = v.iter().filter(|p| {
|
|
|
|
p.ref0().trait_.is_none()
|
|
|
|
});
|
2014-05-22 16:57:53 -07:00
|
|
|
let non_trait = non_trait.collect::<Vec<&(clean::Impl, Option<String>)>>();
|
2014-04-28 20:36:08 -07:00
|
|
|
let mut traits = v.iter().filter(|p| {
|
|
|
|
p.ref0().trait_.is_some()
|
|
|
|
});
|
2014-05-22 16:57:53 -07:00
|
|
|
let traits = traits.collect::<Vec<&(clean::Impl, Option<String>)>>();
|
2014-04-28 20:36:08 -07:00
|
|
|
|
|
|
|
if non_trait.len() > 0 {
|
|
|
|
try!(write!(w, "<h2 id='methods'>Methods</h2>"));
|
|
|
|
for &(ref i, ref dox) in non_trait.move_iter() {
|
|
|
|
try!(render_impl(w, i, dox));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if traits.len() > 0 {
|
|
|
|
try!(write!(w, "<h2 id='implementations'>Trait \
|
|
|
|
Implementations</h2>"));
|
|
|
|
let mut any_derived = false;
|
|
|
|
for & &(ref i, ref dox) in traits.iter() {
|
|
|
|
if !i.derived {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(render_impl(w, i, dox));
|
2014-04-28 20:36:08 -07:00
|
|
|
} else {
|
|
|
|
any_derived = 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
|
|
|
if any_derived {
|
|
|
|
try!(write!(w, "<h3 id='derived_implementations'>Derived Implementations \
|
|
|
|
</h3>"));
|
|
|
|
for &(ref i, ref dox) in traits.move_iter() {
|
|
|
|
if i.derived {
|
2014-03-16 20:00:56 -04:00
|
|
|
try!(render_impl(w, i, dox));
|
|
|
|
}
|
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 => {}
|
|
|
|
}
|
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn render_impl(w: &mut fmt::Formatter, i: &clean::Impl,
|
2014-05-22 16:57:53 -07:00
|
|
|
dox: &Option<String>) -> fmt::Result {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h3 class='impl'><code>impl{} ", i.generics));
|
2014-05-03 02:08:58 -07:00
|
|
|
match i.trait_ {
|
|
|
|
Some(ref ty) => try!(write!(w, "{} for ", *ty)),
|
|
|
|
None => {}
|
|
|
|
}
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "{}</code></h3>", i.for_));
|
2013-09-30 17:04:14 -07:00
|
|
|
match *dox {
|
|
|
|
Some(ref dox) => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<div class='docblock'>{}</div>",
|
2014-01-30 11:30:21 -08:00
|
|
|
Markdown(dox.as_slice())));
|
2013-09-30 17:04:14 -07:00
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2013-10-21 11:33:04 -07:00
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn docmeth(w: &mut fmt::Formatter, item: &clean::Item,
|
|
|
|
dox: bool) -> fmt::Result {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<h4 id='method.{}' class='method'><code>",
|
2014-01-30 11:30:21 -08:00
|
|
|
*item.name.get_ref()));
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(render_method(w, item));
|
|
|
|
try!(write!(w, "</code></h4>\n"));
|
2013-10-21 11:33:04 -07:00
|
|
|
match item.doc_value() {
|
2014-05-02 20:16:03 -07:00
|
|
|
Some(s) if dox => {
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<div class='docblock'>{}</div>", Markdown(s)));
|
2014-05-02 20:16:03 -07:00
|
|
|
Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2014-05-02 20:16:03 -07:00
|
|
|
Some(..) | None => Ok(())
|
2013-10-21 11:33:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<div class='methods'>"));
|
2013-10-21 11:33:04 -07:00
|
|
|
for meth in i.methods.iter() {
|
2014-05-02 20:16:03 -07:00
|
|
|
try!(docmeth(w, meth, true));
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-10-21 11:33:04 -07:00
|
|
|
|
2014-05-03 02:08:58 -07:00
|
|
|
fn render_default_methods(w: &mut fmt::Formatter,
|
|
|
|
t: &clean::Trait,
|
|
|
|
i: &clean::Impl) -> fmt::Result {
|
|
|
|
for method in t.methods.iter() {
|
|
|
|
let n = method.item().name.clone();
|
|
|
|
match i.methods.iter().find(|m| { m.name == n }) {
|
|
|
|
Some(..) => continue,
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
try!(docmeth(w, method.item(), false));
|
|
|
|
}
|
|
|
|
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-05-03 02:08:58 -07:00
|
|
|
match i.trait_ {
|
|
|
|
Some(clean::ResolvedPath { did, .. }) => {
|
2014-04-28 20:36:08 -07:00
|
|
|
try!({
|
2014-05-03 02:08:58 -07:00
|
|
|
match cache_key.get().unwrap().traits.find(&did) {
|
|
|
|
Some(t) => try!(render_default_methods(w, t, i)),
|
2013-12-05 18:19:06 -08:00
|
|
|
None => {}
|
|
|
|
}
|
2014-01-30 11:30:21 -08:00
|
|
|
Ok(())
|
2014-04-28 20:36:08 -07:00
|
|
|
})
|
2013-10-21 11:33:04 -07:00
|
|
|
}
|
2014-05-03 02:08:58 -07:00
|
|
|
Some(..) | None => {}
|
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 {
|
2014-02-20 01:14:51 -08:00
|
|
|
try!(write!(w, "<pre class='rust typedef'>type {}{} = {};</pre>",
|
2014-01-30 11:30:21 -08:00
|
|
|
it.name.get_ref().as_slice(),
|
|
|
|
t.generics,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2014-01-30 00:46:37 +11:00
|
|
|
impl<'a> fmt::Show 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;
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<p class='location'>"));
|
2013-09-18 22:18:38 -07:00
|
|
|
let len = cx.current.len() - if it.is_mod() {1} else {0};
|
|
|
|
for (i, name) in cx.current.iter().take(len).enumerate() {
|
2014-01-30 11:30:21 -08:00
|
|
|
if i > 0 {
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "&\\#8203;::"));
|
2014-01-30 11:30:21 -08:00
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<a href='{}index.html'>{}</a>",
|
2014-04-02 16:54:22 -07:00
|
|
|
cx.root_path
|
|
|
|
.as_slice()
|
|
|
|
.slice_to((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
|
|
|
|
2014-05-10 14:05:06 -07:00
|
|
|
fn block(w: &mut fmt::Formatter, short: &str, longty: &str,
|
2014-01-30 11:30:21 -08:00
|
|
|
cur: &clean::Item, cx: &Context) -> fmt::Result {
|
2013-09-18 22:18:38 -07:00
|
|
|
let items = match cx.sidebar.find_equiv(&short) {
|
|
|
|
Some(items) => items.as_slice(),
|
2014-01-30 11:30:21 -08:00
|
|
|
None => return Ok(())
|
2013-09-18 22:18:38 -07:00
|
|
|
};
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<div class='block {}'><h2>{}</h2>", short, longty));
|
2013-09-18 22:18:38 -07:00
|
|
|
for item in items.iter() {
|
2014-04-09 16:49:31 +09:00
|
|
|
let curty = shortty(cur).to_static_str();
|
2014-05-28 19:53:37 -07:00
|
|
|
let class = if cur.name.get_ref() == item && short == curty {
|
|
|
|
"current"
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
};
|
2014-02-19 10:07:49 -08:00
|
|
|
try!(write!(w, "<a class='{ty} {class}' href='{curty, select,
|
2013-09-18 22:18:38 -07:00
|
|
|
mod{../}
|
|
|
|
other{}
|
2013-09-26 13:44:54 -07:00
|
|
|
}{tysel, select,
|
2013-09-18 22:18:38 -07:00
|
|
|
mod{{name}/index.html}
|
|
|
|
other{#.{name}.html}
|
|
|
|
}'>{name}</a><br/>",
|
|
|
|
ty = short,
|
2013-09-26 13:44:54 -07:00
|
|
|
tysel = short,
|
2013-09-18 22:18:38 -07:00
|
|
|
class = class,
|
2014-04-09 16:49:31 +09:00
|
|
|
curty = curty,
|
2014-01-30 11:30:21 -08:00
|
|
|
name = item.as_slice()));
|
2013-09-18 22:18:38 -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
|
|
|
try!(block(fmt, "mod", "Modules", it, cx));
|
|
|
|
try!(block(fmt, "struct", "Structs", it, cx));
|
|
|
|
try!(block(fmt, "enum", "Enums", it, cx));
|
|
|
|
try!(block(fmt, "trait", "Traits", it, cx));
|
|
|
|
try!(block(fmt, "fn", "Functions", it, cx));
|
|
|
|
try!(block(fmt, "macro", "Macros", it, cx));
|
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 build_sidebar(m: &clean::Module) -> HashMap<String, Vec<String>> {
|
2013-09-18 22:18:38 -07:00
|
|
|
let mut map = HashMap::new();
|
|
|
|
for item in m.items.iter() {
|
2014-05-29 13:50:47 -07:00
|
|
|
if ignore_private_module(item) { continue }
|
|
|
|
|
2014-04-09 16:49:31 +09:00
|
|
|
let short = shortty(item).to_static_str();
|
2013-09-18 22:18:38 -07:00
|
|
|
let myname = match item.name {
|
2013-10-01 14:31:03 -07:00
|
|
|
None => continue,
|
2014-05-25 03:17:19 -07:00
|
|
|
Some(ref s) => s.to_string(),
|
2013-09-18 22:18:38 -07:00
|
|
|
};
|
2014-05-25 03:17:19 -07:00
|
|
|
let v = map.find_or_insert_with(short.to_string(), |_| Vec::new());
|
2013-09-18 22:18:38 -07:00
|
|
|
v.push(myname);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (_, items) in map.mut_iter() {
|
2014-03-05 15:28:08 -08:00
|
|
|
items.as_mut_slice().sort();
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
return map;
|
|
|
|
}
|
2013-09-24 13:57:31 -07:00
|
|
|
|
2014-01-30 00:46:37 +11:00
|
|
|
impl<'a> fmt::Show for Source<'a> {
|
2014-02-05 23:55:13 +11:00
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
let Source(s) = *self;
|
2013-11-23 11:18:51 +01:00
|
|
|
let lines = s.lines().len();
|
2013-09-27 15:12:23 -07:00
|
|
|
let mut cols = 0;
|
|
|
|
let mut tmp = lines;
|
|
|
|
while tmp > 0 {
|
|
|
|
cols += 1;
|
|
|
|
tmp /= 10;
|
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<pre class='line-numbers'>"));
|
2013-09-27 15:12:23 -07:00
|
|
|
for i in range(1, lines + 1) {
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "<span id='{0:u}'>{0:1$u}</span>\n", i, cols));
|
2013-09-27 15:12:23 -07:00
|
|
|
}
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(write!(fmt, "</pre>"));
|
|
|
|
try!(write!(fmt, "{}", highlight::highlight(s.as_slice(), 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 {
|
2014-05-10 14:05:06 -07:00
|
|
|
try!(w.write(highlight::highlight(t.source.as_slice(), Some("macro")).as_bytes()));
|
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,
|
|
|
|
_p: &clean::Primitive) -> fmt::Result {
|
|
|
|
try!(document(w, it));
|
|
|
|
render_methods(w, it)
|
|
|
|
}
|
2014-05-29 13:50:47 -07:00
|
|
|
|
|
|
|
fn ignore_private_module(it: &clean::Item) -> bool {
|
|
|
|
match it.inner {
|
|
|
|
clean::ModuleItem(ref m) => {
|
|
|
|
(m.items.len() == 0 && it.doc_value().is_none()) ||
|
|
|
|
it.visibility != Some(ast::Public)
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|