2013-09-18 22:18:38 -07:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
use std::cell::Cell;
|
|
|
|
use std::comm::{SharedPort, SharedChan};
|
|
|
|
use std::comm;
|
|
|
|
use std::fmt;
|
|
|
|
use std::hashmap::HashMap;
|
|
|
|
use std::local_data;
|
|
|
|
use std::rt::io::buffered::BufferedWriter;
|
|
|
|
use std::rt::io::file::{FileInfo, DirectoryInfo};
|
|
|
|
use std::rt::io::file;
|
|
|
|
use std::rt::io;
|
|
|
|
use std::task;
|
|
|
|
use std::unstable::finally::Finally;
|
|
|
|
use std::util;
|
|
|
|
use std::vec;
|
|
|
|
|
|
|
|
use extra::arc::RWArc;
|
|
|
|
use extra::json::ToJson;
|
|
|
|
use extra::sort;
|
|
|
|
|
|
|
|
use syntax::ast;
|
|
|
|
|
|
|
|
use clean;
|
|
|
|
use doctree;
|
|
|
|
use fold::DocFolder;
|
2013-09-23 20:38:17 -07:00
|
|
|
use html::format::{VisSpace, Method, PuritySpace};
|
2013-09-18 22:18:38 -07:00
|
|
|
use html::layout;
|
|
|
|
use html::markdown::Markdown;
|
|
|
|
|
|
|
|
#[deriving(Clone)]
|
|
|
|
pub struct Context {
|
|
|
|
current: ~[~str],
|
|
|
|
root_path: ~str,
|
|
|
|
dst: Path,
|
|
|
|
layout: layout::Layout,
|
|
|
|
sidebar: HashMap<~str, ~[~str]>,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Implementor {
|
|
|
|
PathType(clean::Type),
|
|
|
|
OtherType(clean::Generics, /* trait */ clean::Type, /* for */ clean::Type),
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Cache {
|
|
|
|
// typaram id => name of that typaram
|
|
|
|
typarams: HashMap<ast::NodeId, ~str>,
|
|
|
|
// type id => all implementations for that type
|
|
|
|
impls: HashMap<ast::NodeId, ~[clean::Impl]>,
|
|
|
|
// path id => (full qualified path, shortty) -- used to generate urls
|
|
|
|
paths: HashMap<ast::NodeId, (~[~str], &'static str)>,
|
|
|
|
// trait id => method name => dox
|
|
|
|
traits: HashMap<ast::NodeId, HashMap<~str, ~str>>,
|
|
|
|
// trait id => implementors of the trait
|
|
|
|
implementors: HashMap<ast::NodeId, ~[Implementor]>,
|
|
|
|
|
|
|
|
priv stack: ~[~str],
|
|
|
|
priv parent_stack: ~[ast::NodeId],
|
|
|
|
priv search_index: ~[IndexItem],
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Item<'self> { cx: &'self Context, item: &'self clean::Item, }
|
|
|
|
struct Sidebar<'self> { cx: &'self Context, item: &'self clean::Item, }
|
|
|
|
|
|
|
|
struct IndexItem {
|
|
|
|
ty: &'static str,
|
|
|
|
name: ~str,
|
|
|
|
path: ~str,
|
|
|
|
desc: ~str,
|
|
|
|
parent: Option<ast::NodeId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
local_data_key!(pub cache_key: RWArc<Cache>)
|
|
|
|
local_data_key!(pub current_location_key: ~[~str])
|
|
|
|
|
|
|
|
/// Generates the documentation for `crate` into the directory `dst`
|
|
|
|
pub fn run(mut crate: clean::Crate, dst: Path) {
|
|
|
|
let mut cx = Context {
|
|
|
|
dst: dst,
|
|
|
|
current: ~[],
|
|
|
|
root_path: ~"",
|
|
|
|
sidebar: HashMap::new(),
|
|
|
|
layout: layout::Layout {
|
|
|
|
logo: ~"",
|
|
|
|
favicon: ~"",
|
|
|
|
crate: crate.name.clone(),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
mkdir(&cx.dst);
|
|
|
|
|
|
|
|
match crate.module.get_ref().doc_list() {
|
|
|
|
Some(attrs) => {
|
|
|
|
for attr in attrs.iter() {
|
|
|
|
match *attr {
|
|
|
|
clean::NameValue(~"html_favicon_url", ref s) => {
|
|
|
|
cx.layout.favicon = s.to_owned();
|
|
|
|
}
|
|
|
|
clean::NameValue(~"html_logo_url", ref s) => {
|
|
|
|
cx.layout.logo = s.to_owned();
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Crawl the crate to build various caches used for the output
|
|
|
|
let mut cache = Cache {
|
|
|
|
impls: HashMap::new(),
|
|
|
|
typarams: HashMap::new(),
|
|
|
|
paths: HashMap::new(),
|
|
|
|
traits: HashMap::new(),
|
|
|
|
implementors: HashMap::new(),
|
|
|
|
stack: ~[],
|
|
|
|
parent_stack: ~[],
|
|
|
|
search_index: ~[],
|
|
|
|
};
|
|
|
|
cache.stack.push(crate.name.clone());
|
|
|
|
crate = cache.fold_crate(crate);
|
|
|
|
|
|
|
|
// Add all the static files
|
2013-09-22 20:09:42 -07:00
|
|
|
let dst = cx.dst.push(crate.name);
|
|
|
|
mkdir(&dst);
|
|
|
|
write(dst.push("jquery.js"), include_str!("static/jquery-2.0.3.min.js"));
|
|
|
|
write(dst.push("main.js"), include_str!("static/main.js"));
|
|
|
|
write(dst.push("main.css"), include_str!("static/main.css"));
|
|
|
|
write(dst.push("normalize.css"), include_str!("static/normalize.css"));
|
2013-09-18 22:18:38 -07:00
|
|
|
|
|
|
|
{
|
2013-09-22 20:09:42 -07:00
|
|
|
let dst = dst.push("search-index.js");
|
2013-09-18 22:18:38 -07:00
|
|
|
let mut w = BufferedWriter::new(dst.open_writer(io::CreateOrTruncate));
|
|
|
|
let w = &mut w as &mut io::Writer;
|
|
|
|
write!(w, "var searchIndex = [");
|
|
|
|
for (i, item) in cache.search_index.iter().enumerate() {
|
|
|
|
if i > 0 { write!(w, ","); }
|
|
|
|
write!(w, "\\{ty:\"{}\",name:\"{}\",path:\"{}\",desc:{}",
|
|
|
|
item.ty, item.name, item.path,
|
|
|
|
item.desc.to_json().to_str())
|
|
|
|
match item.parent {
|
|
|
|
Some(id) => { write!(w, ",parent:'{}'", id); }
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
write!(w, "\\}");
|
|
|
|
}
|
|
|
|
write!(w, "];");
|
|
|
|
write!(w, "var allPaths = \\{");
|
|
|
|
for (i, (&id, &(ref fqp, short))) in cache.paths.iter().enumerate() {
|
|
|
|
if i > 0 { write!(w, ","); }
|
|
|
|
write!(w, "'{}':\\{type:'{}',name:'{}'\\}", id, short, *fqp.last());
|
|
|
|
}
|
|
|
|
write!(w, "\\};");
|
|
|
|
w.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now render the whole crate.
|
|
|
|
cx.crate(crate, cache);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write(dst: Path, contents: &str) {
|
|
|
|
let mut w = dst.open_writer(io::CreateOrTruncate);
|
|
|
|
w.write(contents.as_bytes());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mkdir(path: &Path) {
|
|
|
|
do io::io_error::cond.trap(|err| {
|
|
|
|
error2!("Couldn't create directory `{}`: {}",
|
|
|
|
path.to_str(), err.desc);
|
|
|
|
fail!()
|
|
|
|
}).inside {
|
|
|
|
if !path.is_dir() {
|
|
|
|
file::mkdir(path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'self> DocFolder for Cache {
|
|
|
|
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
|
|
|
|
// Register any generics to their corresponding string. This is used
|
|
|
|
// when pretty-printing types
|
|
|
|
match item.inner {
|
|
|
|
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),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Propagate a trait methods' documentation to all implementors of the
|
|
|
|
// trait
|
|
|
|
match item.inner {
|
|
|
|
clean::TraitItem(ref t) => {
|
|
|
|
let mut dox = HashMap::new();
|
|
|
|
for meth in t.methods.iter() {
|
|
|
|
let it = meth.item();
|
|
|
|
match it.doc_value() {
|
|
|
|
None => {}
|
|
|
|
Some(s) => {
|
|
|
|
dox.insert(it.name.get_ref().to_owned(),
|
|
|
|
s.to_owned());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.traits.insert(item.id, dox);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all the implementors of traits.
|
|
|
|
match item.inner {
|
|
|
|
clean::ImplItem(ref i) => {
|
|
|
|
match i.trait_ {
|
|
|
|
Some(clean::ResolvedPath{ id, _ }) => {
|
|
|
|
let v = do self.implementors.find_or_insert_with(id) |_|{
|
|
|
|
~[]
|
|
|
|
};
|
|
|
|
match i.for_ {
|
|
|
|
clean::ResolvedPath{_} => {
|
|
|
|
v.unshift(PathType(i.for_.clone()));
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
v.push(OtherType(i.generics.clone(),
|
|
|
|
i.trait_.get_ref().clone(),
|
|
|
|
i.for_.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(*) | None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Index this method for searching later on
|
|
|
|
match item.name {
|
|
|
|
Some(ref s) => {
|
|
|
|
let parent = match item.inner {
|
|
|
|
clean::TyMethodItem(*) | clean::VariantItem(*) => {
|
|
|
|
Some((Some(*self.parent_stack.last()),
|
|
|
|
self.stack.slice_to(self.stack.len() - 1)))
|
|
|
|
|
|
|
|
}
|
|
|
|
clean::MethodItem(*) => {
|
|
|
|
if self.parent_stack.len() == 0 {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some((Some(*self.parent_stack.last()),
|
|
|
|
self.stack.as_slice()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Some((None, self.stack.as_slice()))
|
|
|
|
};
|
|
|
|
match parent {
|
|
|
|
Some((parent, path)) => {
|
|
|
|
self.search_index.push(IndexItem {
|
|
|
|
ty: shortty(&item),
|
|
|
|
name: s.to_owned(),
|
|
|
|
path: path.connect("::"),
|
|
|
|
desc: shorter(item.doc_value()).to_owned(),
|
|
|
|
parent: parent,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 {
|
|
|
|
self.stack.push(n.to_owned());
|
|
|
|
true
|
|
|
|
} else { false }
|
|
|
|
} else { false };
|
|
|
|
match item.inner {
|
|
|
|
clean::StructItem(*) | clean::EnumItem(*) |
|
2013-09-24 13:56:52 -07:00
|
|
|
clean::TypedefItem(*) | clean::TraitItem(*) |
|
|
|
|
clean::FunctionItem(*) | clean::ModuleItem(*) |
|
|
|
|
clean::VariantItem(*) => {
|
2013-09-18 22:18:38 -07:00
|
|
|
self.paths.insert(item.id, (self.stack.clone(), shortty(&item)));
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maintain the parent stack
|
|
|
|
let parent_pushed = match item.inner {
|
|
|
|
clean::TraitItem(*) | clean::EnumItem(*) => {
|
|
|
|
self.parent_stack.push(item.id); true
|
|
|
|
}
|
|
|
|
clean::ImplItem(ref i) => {
|
|
|
|
match i.for_ {
|
|
|
|
clean::ResolvedPath{ id, _ } => {
|
|
|
|
self.parent_stack.push(id); true
|
|
|
|
}
|
|
|
|
_ => 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) => {
|
|
|
|
match item.inner {
|
|
|
|
clean::ImplItem(i) => {
|
|
|
|
match i.for_ {
|
|
|
|
clean::ResolvedPath { id, _ } => {
|
|
|
|
let v = do self.impls.find_or_insert_with(id) |_| {
|
|
|
|
~[]
|
|
|
|
};
|
|
|
|
v.push(i);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
_ => Some(item),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i => i,
|
|
|
|
};
|
|
|
|
|
|
|
|
if pushed { self.stack.pop(); }
|
|
|
|
if parent_pushed { self.parent_stack.pop(); }
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'self> Cache {
|
|
|
|
fn generics(&mut self, generics: &clean::Generics) {
|
|
|
|
for typ in generics.type_params.iter() {
|
|
|
|
self.typarams.insert(typ.id, typ.name.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Context {
|
|
|
|
fn recurse<T>(&mut self, s: ~str, f: &fn(&mut Context) -> T) -> T {
|
|
|
|
// Recurse in the directory structure and change the "root path" to make
|
|
|
|
// sure it always points to the top (relatively)
|
|
|
|
if s.len() == 0 {
|
|
|
|
fail2!("what {:?}", self);
|
|
|
|
}
|
|
|
|
let next = self.dst.push(s);
|
|
|
|
let prev = util::replace(&mut self.dst, next);
|
|
|
|
self.root_path.push_str("../");
|
|
|
|
self.current.push(s);
|
|
|
|
|
|
|
|
mkdir(&self.dst);
|
|
|
|
let ret = f(self);
|
|
|
|
|
|
|
|
// Go back to where we were at
|
|
|
|
self.dst = prev;
|
|
|
|
let len = self.root_path.len();
|
|
|
|
self.root_path.truncate(len - 3);
|
|
|
|
self.current.pop();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Processes
|
|
|
|
fn crate(self, mut crate: clean::Crate, cache: Cache) {
|
|
|
|
enum Work {
|
|
|
|
Die,
|
|
|
|
Process(Context, clean::Item),
|
|
|
|
}
|
|
|
|
enum Progress { JobNew, JobDone }
|
|
|
|
static WORKERS: int = 10;
|
|
|
|
|
|
|
|
let mut item = match crate.module.take() {
|
|
|
|
Some(i) => i,
|
|
|
|
None => return
|
|
|
|
};
|
|
|
|
item.name = Some(crate.name);
|
|
|
|
|
|
|
|
let (port, chan) = comm::stream::<Work>();
|
|
|
|
let port = SharedPort::new(port);
|
|
|
|
let chan = SharedChan::new(chan);
|
|
|
|
let (prog_port, prog_chan) = comm::stream();
|
|
|
|
let prog_chan = SharedChan::new(prog_chan);
|
|
|
|
let cache = RWArc::new(cache);
|
|
|
|
|
|
|
|
for i in range(0, WORKERS) {
|
|
|
|
let port = port.clone();
|
|
|
|
let chan = chan.clone();
|
|
|
|
let prog_chan = prog_chan.clone();
|
|
|
|
|
|
|
|
let mut task = task::task();
|
|
|
|
task.unlinked(); // we kill things manually
|
|
|
|
task.name(format!("worker{}", i));
|
2013-09-23 16:55:48 -07:00
|
|
|
task.spawn_with(cache.clone(),
|
|
|
|
|cache| worker(cache, &port, &chan, &prog_chan));
|
|
|
|
|
|
|
|
fn worker(cache: RWArc<Cache>,
|
|
|
|
port: &SharedPort<Work>,
|
|
|
|
chan: &SharedChan<Work>,
|
|
|
|
prog_chan: &SharedChan<Progress>) {
|
|
|
|
#[fixed_stack_segment]; // we hit markdown FFI *a lot*
|
2013-09-18 22:18:38 -07:00
|
|
|
local_data::set(cache_key, cache);
|
2013-09-23 16:55:48 -07:00
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
loop {
|
|
|
|
match port.recv() {
|
|
|
|
Process(cx, item) => {
|
|
|
|
let mut cx = cx;
|
|
|
|
let item = Cell::new(item);
|
|
|
|
do (|| {
|
|
|
|
do cx.item(item.take()) |cx, item| {
|
|
|
|
prog_chan.send(JobNew);
|
|
|
|
chan.send(Process(cx.clone(), item));
|
|
|
|
}
|
|
|
|
}).finally {
|
|
|
|
// If we fail, everything else should still get
|
|
|
|
// completed
|
|
|
|
prog_chan.send(JobDone);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Die => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-23 16:55:48 -07:00
|
|
|
chan.send(Process(self, item));
|
|
|
|
let mut jobs = 1;
|
|
|
|
loop {
|
|
|
|
match prog_port.recv() {
|
|
|
|
JobNew => jobs += 1,
|
|
|
|
JobDone => jobs -= 1,
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-09-23 16:55:48 -07:00
|
|
|
if jobs == 0 { break }
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-09-23 16:55:48 -07:00
|
|
|
for _ in range(0, WORKERS) {
|
|
|
|
chan.send(Die);
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn item(&mut self, item: clean::Item, f: &fn(&mut Context, clean::Item)) {
|
|
|
|
fn render(w: io::file::FileWriter, cx: &mut Context, it: &clean::Item,
|
|
|
|
pushname: bool) {
|
|
|
|
// A little unfortunate that this is done like this, but it sure
|
|
|
|
// does make formatting *a lot* nicer.
|
|
|
|
local_data::set(current_location_key, cx.current.clone());
|
|
|
|
|
|
|
|
let mut title = cx.current.connect("::");
|
|
|
|
if pushname {
|
|
|
|
if title.len() > 0 { title.push_str("::"); }
|
|
|
|
title.push_str(*it.name.get_ref());
|
|
|
|
}
|
|
|
|
title.push_str(" - Rust");
|
|
|
|
let page = layout::Page {
|
|
|
|
ty: shortty(it),
|
|
|
|
root_path: cx.root_path,
|
|
|
|
title: title,
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
layout::render(&mut writer as &mut io::Writer, &cx.layout, &page,
|
|
|
|
&Sidebar{ cx: cx, item: it },
|
|
|
|
&Item{ cx: cx, item: it });
|
|
|
|
writer.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
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-09-18 22:18:38 -07:00
|
|
|
clean::ModuleItem(*) => {
|
|
|
|
let name = item.name.get_ref().to_owned();
|
|
|
|
let item = Cell::new(item);
|
|
|
|
do self.recurse(name) |this| {
|
|
|
|
let item = item.take();
|
|
|
|
let dst = this.dst.push("index.html");
|
|
|
|
let writer = dst.open_writer(io::CreateOrTruncate);
|
|
|
|
render(writer.unwrap(), this, &item, false);
|
|
|
|
|
|
|
|
let m = match item.inner {
|
|
|
|
clean::ModuleItem(m) => m,
|
|
|
|
_ => unreachable!()
|
|
|
|
};
|
|
|
|
this.sidebar = build_sidebar(&m);
|
|
|
|
for item in m.items.move_iter() {
|
|
|
|
f(this, item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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() => {
|
|
|
|
let dst = self.dst.push(item_path(&item));
|
|
|
|
let writer = dst.open_writer(io::CreateOrTruncate);
|
|
|
|
render(writer.unwrap(), self, &item, true);
|
2013-09-24 13:56:52 -07:00
|
|
|
|
|
|
|
// recurse if necessary
|
|
|
|
let name = item.name.get_ref().clone();
|
|
|
|
match item.inner {
|
|
|
|
clean::EnumItem(e) => {
|
|
|
|
let mut it = e.variants.move_iter();
|
|
|
|
do self.recurse(name) |this| {
|
|
|
|
for item in it {
|
|
|
|
f(this, item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
2013-09-24 13:56:52 -07:00
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn shortty(item: &clean::Item) -> &'static str {
|
|
|
|
match item.inner {
|
|
|
|
clean::ModuleItem(*) => "mod",
|
|
|
|
clean::StructItem(*) => "struct",
|
|
|
|
clean::EnumItem(*) => "enum",
|
|
|
|
clean::FunctionItem(*) => "fn",
|
|
|
|
clean::TypedefItem(*) => "typedef",
|
|
|
|
clean::StaticItem(*) => "static",
|
|
|
|
clean::TraitItem(*) => "trait",
|
|
|
|
clean::ImplItem(*) => "impl",
|
|
|
|
clean::ViewItemItem(*) => "viewitem",
|
|
|
|
clean::TyMethodItem(*) => "tymethod",
|
|
|
|
clean::MethodItem(*) => "method",
|
|
|
|
clean::StructFieldItem(*) => "structfield",
|
|
|
|
clean::VariantItem(*) => "variant",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'self> Item<'self> {
|
|
|
|
fn ismodule(&self) -> bool {
|
|
|
|
match self.item.inner {
|
|
|
|
clean::ModuleItem(*) => true, _ => false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'self> fmt::Default for Item<'self> {
|
|
|
|
fn fmt(it: &Item<'self>, fmt: &mut fmt::Formatter) {
|
|
|
|
// Write the breadcrumb trail header for the top
|
|
|
|
write!(fmt.buf, "<h1 class='fqn'>");
|
|
|
|
match it.item.inner {
|
|
|
|
clean::ModuleItem(*) => write!(fmt.buf, "Module "),
|
|
|
|
clean::FunctionItem(*) => write!(fmt.buf, "Function "),
|
|
|
|
clean::TraitItem(*) => write!(fmt.buf, "Trait "),
|
|
|
|
clean::StructItem(*) => write!(fmt.buf, "Struct "),
|
|
|
|
clean::EnumItem(*) => write!(fmt.buf, "Enum "),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
let cur = it.cx.current.as_slice();
|
|
|
|
let amt = if it.ismodule() { cur.len() - 1 } else { cur.len() };
|
|
|
|
for (i, component) in cur.iter().enumerate().take(amt) {
|
|
|
|
let mut trail = ~"";
|
|
|
|
for _ in range(0, cur.len() - i - 1) {
|
|
|
|
trail.push_str("../");
|
|
|
|
}
|
|
|
|
write!(fmt.buf, "<a href='{}index.html'>{}</a>::",
|
|
|
|
trail, component.as_slice());
|
|
|
|
}
|
|
|
|
write!(fmt.buf, "<a class='{}' href=''>{}</a></h1>",
|
|
|
|
shortty(it.item), it.item.name.get_ref().as_slice());
|
|
|
|
|
|
|
|
match it.item.inner {
|
|
|
|
clean::ModuleItem(ref m) => item_module(fmt.buf, it.cx,
|
|
|
|
it.item, m.items),
|
|
|
|
clean::FunctionItem(ref f) => item_function(fmt.buf, it.item, f),
|
|
|
|
clean::TraitItem(ref t) => item_trait(fmt.buf, it.item, t),
|
|
|
|
clean::StructItem(ref s) => item_struct(fmt.buf, it.item, s),
|
|
|
|
clean::EnumItem(ref e) => item_enum(fmt.buf, it.item, e),
|
|
|
|
clean::TypedefItem(ref t) => item_typedef(fmt.buf, it.item, t),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_path(item: &clean::Item) -> ~str {
|
|
|
|
match item.inner {
|
|
|
|
clean::ModuleItem(*) => *item.name.get_ref() + "/index.html",
|
|
|
|
_ => shortty(item) + "." + *item.name.get_ref() + ".html"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn full_path(cx: &Context, item: &clean::Item) -> ~str {
|
|
|
|
let mut s = cx.current.connect("::");
|
|
|
|
s.push_str("::");
|
|
|
|
s.push_str(item.name.get_ref().as_slice());
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 => ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn document(w: &mut io::Writer, item: &clean::Item) {
|
|
|
|
match item.doc_value() {
|
|
|
|
Some(s) => {
|
|
|
|
write!(w, "<div class='docblock'>{}</div>", Markdown(s));
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_module(w: &mut io::Writer, cx: &Context,
|
|
|
|
item: &clean::Item, items: &[clean::Item]) {
|
|
|
|
document(w, item);
|
2013-09-24 13:55:22 -07:00
|
|
|
debug2!("{:?}", items);
|
2013-09-18 22:18:38 -07:00
|
|
|
let mut indices = vec::from_fn(items.len(), |i| i);
|
|
|
|
|
2013-09-24 13:55:22 -07:00
|
|
|
fn lt(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> bool {
|
2013-09-18 22:18:38 -07:00
|
|
|
if shortty(i1) == shortty(i2) {
|
|
|
|
return i1.name < i2.name;
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
(&clean::ExternMod(*), _) => true,
|
|
|
|
(_, &clean::ExternMod(*)) => false,
|
|
|
|
_ => idx1 < idx2,
|
|
|
|
}
|
|
|
|
}
|
2013-09-18 22:18:38 -07:00
|
|
|
(&clean::ViewItemItem(*), _) => true,
|
|
|
|
(_, &clean::ViewItemItem(*)) => false,
|
|
|
|
(&clean::ModuleItem(*), _) => true,
|
|
|
|
(_, &clean::ModuleItem(*)) => false,
|
|
|
|
(&clean::StructItem(*), _) => true,
|
|
|
|
(_, &clean::StructItem(*)) => false,
|
|
|
|
(&clean::EnumItem(*), _) => true,
|
|
|
|
(_, &clean::EnumItem(*)) => false,
|
|
|
|
(&clean::StaticItem(*), _) => true,
|
|
|
|
(_, &clean::StaticItem(*)) => false,
|
|
|
|
(&clean::TraitItem(*), _) => true,
|
|
|
|
(_, &clean::TraitItem(*)) => false,
|
|
|
|
(&clean::FunctionItem(*), _) => true,
|
|
|
|
(_, &clean::FunctionItem(*)) => false,
|
|
|
|
(&clean::TypedefItem(*), _) => true,
|
|
|
|
(_, &clean::TypedefItem(*)) => false,
|
2013-09-24 13:55:22 -07:00
|
|
|
_ => idx1 < idx2,
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:55:22 -07:00
|
|
|
debug2!("{:?}", indices);
|
2013-09-18 22:18:38 -07:00
|
|
|
do sort::quick_sort(indices) |&i1, &i2| {
|
2013-09-24 13:55:22 -07:00
|
|
|
lt(&items[i1], &items[i2], i1, i2)
|
2013-09-18 22:18:38 -07:00
|
|
|
}
|
|
|
|
|
2013-09-24 13:55:22 -07:00
|
|
|
debug2!("{:?}", indices);
|
2013-09-18 22:18:38 -07:00
|
|
|
let mut curty = "";
|
|
|
|
for &idx in indices.iter() {
|
|
|
|
let myitem = &items[idx];
|
|
|
|
|
|
|
|
let myty = shortty(myitem);
|
|
|
|
if myty != curty {
|
|
|
|
if curty != "" {
|
|
|
|
write!(w, "</table>");
|
|
|
|
}
|
|
|
|
curty = myty;
|
|
|
|
write!(w, "<h2>{}</h2>\n<table>", match myitem.inner {
|
|
|
|
clean::ModuleItem(*) => "Modules",
|
|
|
|
clean::StructItem(*) => "Structs",
|
|
|
|
clean::EnumItem(*) => "Enums",
|
|
|
|
clean::FunctionItem(*) => "Functions",
|
|
|
|
clean::TypedefItem(*) => "Type Definitions",
|
|
|
|
clean::StaticItem(*) => "Statics",
|
|
|
|
clean::TraitItem(*) => "Traits",
|
|
|
|
clean::ImplItem(*) => "Implementations",
|
|
|
|
clean::ViewItemItem(*) => "Reexports",
|
|
|
|
clean::TyMethodItem(*) => "Type Methods",
|
|
|
|
clean::MethodItem(*) => "Methods",
|
|
|
|
clean::StructFieldItem(*) => "Struct Fields",
|
|
|
|
clean::VariantItem(*) => "Variants",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
match myitem.inner {
|
|
|
|
clean::StaticItem(ref s) => {
|
|
|
|
struct Initializer<'self>(&'self str);
|
|
|
|
impl<'self> fmt::Default for Initializer<'self> {
|
|
|
|
fn fmt(s: &Initializer<'self>, f: &mut fmt::Formatter) {
|
|
|
|
let tag = if s.contains("\n") { "pre" } else { "code" };
|
|
|
|
write!(f.buf, "<{tag}>{}</{tag}>",
|
|
|
|
s.as_slice(), tag=tag);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
write!(w, "
|
|
|
|
<tr>
|
2013-09-24 13:56:52 -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_,
|
|
|
|
Initializer(s.expr),
|
|
|
|
Markdown(blank(myitem.doc_value())));
|
|
|
|
}
|
|
|
|
|
2013-09-24 13:56:52 -07:00
|
|
|
clean::ViewItemItem(ref item) => {
|
|
|
|
match item.inner {
|
|
|
|
clean::ExternMod(ref name, ref src, _, _) => {
|
|
|
|
write!(w, "<tr><td><code>extern mod {}",
|
|
|
|
name.as_slice());
|
|
|
|
match *src {
|
|
|
|
Some(ref src) => write!(w, " = \"{}\"",
|
|
|
|
src.as_slice()),
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
write!(w, ";</code></td></tr>");
|
|
|
|
}
|
|
|
|
|
|
|
|
clean::Import(ref imports) => {
|
|
|
|
for import in imports.iter() {
|
|
|
|
write!(w, "<tr><td><code>{}{}</code></td></tr>",
|
|
|
|
VisSpace(myitem.visibility),
|
|
|
|
*import);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-09-18 22:18:38 -07:00
|
|
|
_ => {
|
2013-09-24 13:56:52 -07:00
|
|
|
if myitem.name.is_none() { loop }
|
2013-09-18 22:18:38 -07:00
|
|
|
write!(w, "
|
|
|
|
<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),
|
|
|
|
title = full_path(cx, myitem));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(w, "</table>");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_function(w: &mut io::Writer, it: &clean::Item, f: &clean::Function) {
|
2013-09-23 20:38:17 -07:00
|
|
|
write!(w, "<pre class='fn'>{vis}{purity}fn {name}{generics}{decl}</pre>",
|
2013-09-18 22:18:38 -07:00
|
|
|
vis = VisSpace(it.visibility),
|
2013-09-23 20:38:17 -07:00
|
|
|
purity = PuritySpace(f.purity),
|
2013-09-18 22:18:38 -07:00
|
|
|
name = it.name.get_ref().as_slice(),
|
|
|
|
generics = f.generics,
|
|
|
|
decl = f.decl);
|
|
|
|
document(w, it);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_trait(w: &mut io::Writer, it: &clean::Item, t: &clean::Trait) {
|
|
|
|
let mut parents = ~"";
|
|
|
|
if t.parents.len() > 0 {
|
|
|
|
parents.push_str(": ");
|
|
|
|
for (i, p) in t.parents.iter().enumerate() {
|
|
|
|
if i > 0 { parents.push_str(" + "); }
|
|
|
|
parents.push_str(format!("{}", *p));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the trait definition
|
|
|
|
write!(w, "<pre class='trait'>{}trait {}{}{} ",
|
|
|
|
VisSpace(it.visibility),
|
|
|
|
it.name.get_ref().as_slice(),
|
|
|
|
t.generics,
|
|
|
|
parents);
|
|
|
|
let required = t.methods.iter().filter(|m| m.is_req()).to_owned_vec();
|
|
|
|
let provided = t.methods.iter().filter(|m| !m.is_req()).to_owned_vec();
|
|
|
|
|
|
|
|
if t.methods.len() == 0 {
|
|
|
|
write!(w, "\\{ \\}");
|
|
|
|
} else {
|
|
|
|
write!(w, "\\{\n");
|
|
|
|
for m in required.iter() {
|
|
|
|
write!(w, " ");
|
|
|
|
render_method(w, m.item(), true);
|
|
|
|
write!(w, ";\n");
|
|
|
|
}
|
|
|
|
if required.len() > 0 && provided.len() > 0 {
|
|
|
|
w.write("\n".as_bytes());
|
|
|
|
}
|
|
|
|
for m in provided.iter() {
|
|
|
|
write!(w, " ");
|
|
|
|
render_method(w, m.item(), true);
|
|
|
|
write!(w, " \\{ ... \\}\n");
|
|
|
|
}
|
|
|
|
write!(w, "\\}");
|
|
|
|
}
|
|
|
|
write!(w, "</pre>");
|
|
|
|
|
|
|
|
// Trait documentation
|
|
|
|
document(w, it);
|
|
|
|
|
|
|
|
fn meth(w: &mut io::Writer, m: &clean::TraitMethod) {
|
|
|
|
write!(w, "<h3 id='fn.{}' class='method'><code>",
|
|
|
|
*m.item().name.get_ref());
|
|
|
|
render_method(w, m.item(), false);
|
|
|
|
write!(w, "</code></h3>");
|
|
|
|
document(w, m.item());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the documentation for each function individually
|
|
|
|
if required.len() > 0 {
|
|
|
|
write!(w, "
|
|
|
|
<h2 id='required-methods'>Required Methods</h2>
|
|
|
|
<div class='methods'>
|
|
|
|
");
|
|
|
|
for m in required.iter() {
|
|
|
|
meth(w, *m);
|
|
|
|
}
|
|
|
|
write!(w, "</div>");
|
|
|
|
}
|
|
|
|
if provided.len() > 0 {
|
|
|
|
write!(w, "
|
|
|
|
<h2 id='provided-methods'>Provided Methods</h2>
|
|
|
|
<div class='methods'>
|
|
|
|
");
|
|
|
|
for m in provided.iter() {
|
|
|
|
meth(w, *m);
|
|
|
|
}
|
|
|
|
write!(w, "</div>");
|
|
|
|
}
|
|
|
|
|
|
|
|
do local_data::get(cache_key) |cache| {
|
|
|
|
do cache.unwrap().read |cache| {
|
|
|
|
match cache.implementors.find(&it.id) {
|
|
|
|
Some(implementors) => {
|
|
|
|
write!(w, "
|
|
|
|
<h2 id='implementors'>Implementors</h2>
|
|
|
|
<ul class='item-list'>
|
|
|
|
");
|
|
|
|
for i in implementors.iter() {
|
|
|
|
match *i {
|
|
|
|
PathType(ref ty) => {
|
|
|
|
write!(w, "<li><code>{}</code></li>", *ty);
|
|
|
|
}
|
|
|
|
OtherType(ref generics, ref trait_, ref for_) => {
|
|
|
|
write!(w, "<li><code>impl{} {} for {}</code></li>",
|
|
|
|
*generics, *trait_, *for_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(w, "</ul>");
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_method(w: &mut io::Writer, meth: &clean::Item, withlink: bool) {
|
|
|
|
fn fun(w: &mut io::Writer, it: &clean::Item, purity: ast::purity,
|
|
|
|
g: &clean::Generics, selfty: &clean::SelfTy, d: &clean::FnDecl,
|
|
|
|
withlink: bool) {
|
|
|
|
write!(w, "{}fn {withlink, select,
|
|
|
|
true{<a href='\\#fn.{name}'>{name}</a>}
|
|
|
|
other{{name}}
|
|
|
|
}{generics}{decl}",
|
|
|
|
match purity {
|
|
|
|
ast::unsafe_fn => "unsafe ",
|
|
|
|
_ => "",
|
|
|
|
},
|
|
|
|
name = it.name.get_ref().as_slice(),
|
|
|
|
generics = *g,
|
|
|
|
decl = Method(selfty, d),
|
|
|
|
withlink = if withlink {"true"} else {"false"});
|
|
|
|
}
|
|
|
|
match meth.inner {
|
|
|
|
clean::TyMethodItem(ref m) => {
|
|
|
|
fun(w, meth, m.purity, &m.generics, &m.self_, &m.decl, withlink);
|
|
|
|
}
|
|
|
|
clean::MethodItem(ref m) => {
|
|
|
|
fun(w, meth, m.purity, &m.generics, &m.self_, &m.decl, withlink);
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_struct(w: &mut io::Writer, it: &clean::Item, s: &clean::Struct) {
|
|
|
|
write!(w, "<pre class='struct'>");
|
|
|
|
render_struct(w, it, Some(&s.generics), s.struct_type, s.fields, "");
|
|
|
|
write!(w, "</pre>");
|
|
|
|
|
|
|
|
document(w, it);
|
|
|
|
render_methods(w, it);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_enum(w: &mut io::Writer, it: &clean::Item, e: &clean::Enum) {
|
|
|
|
write!(w, "<pre class='enum'>{}enum {}{}",
|
|
|
|
VisSpace(it.visibility),
|
|
|
|
it.name.get_ref().as_slice(),
|
|
|
|
e.generics);
|
|
|
|
if e.variants.len() == 0 {
|
|
|
|
write!(w, " \\{\\}");
|
|
|
|
} else {
|
|
|
|
write!(w, " \\{\n");
|
|
|
|
for v in e.variants.iter() {
|
|
|
|
let name = v.name.get_ref().as_slice();
|
|
|
|
match v.inner {
|
|
|
|
clean::VariantItem(ref var) => {
|
|
|
|
match var.kind {
|
|
|
|
clean::CLikeVariant => write!(w, " {},\n", name),
|
|
|
|
clean::TupleVariant(ref tys) => {
|
|
|
|
write!(w, " {}(", name);
|
|
|
|
for (i, ty) in tys.iter().enumerate() {
|
|
|
|
if i > 0 { write!(w, ", ") }
|
|
|
|
write!(w, "{}", *ty);
|
|
|
|
}
|
|
|
|
write!(w, "),\n");
|
|
|
|
}
|
|
|
|
clean::StructVariant(ref s) => {
|
|
|
|
render_struct(w, v, None, s.struct_type, s.fields,
|
|
|
|
" ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(w, "\\}");
|
|
|
|
}
|
|
|
|
write!(w, "</pre>");
|
|
|
|
|
|
|
|
document(w, it);
|
|
|
|
render_methods(w, it);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_struct(w: &mut io::Writer, it: &clean::Item,
|
|
|
|
g: Option<&clean::Generics>,
|
|
|
|
ty: doctree::StructType,
|
|
|
|
fields: &[clean::Item],
|
|
|
|
tab: &str) {
|
|
|
|
write!(w, "{}struct {}",
|
|
|
|
VisSpace(it.visibility),
|
|
|
|
it.name.get_ref().as_slice());
|
|
|
|
match g {
|
|
|
|
Some(g) => write!(w, "{}", *g),
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
match ty {
|
|
|
|
doctree::Plain => {
|
|
|
|
write!(w, " \\{\n");
|
|
|
|
for field in fields.iter() {
|
|
|
|
match field.inner {
|
|
|
|
clean::StructFieldItem(ref ty) => {
|
|
|
|
write!(w, " {}{}: {},\n{}",
|
|
|
|
VisSpace(field.visibility),
|
|
|
|
field.name.get_ref().as_slice(),
|
|
|
|
ty.type_,
|
|
|
|
tab);
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(w, "\\}");
|
|
|
|
}
|
|
|
|
doctree::Tuple | doctree::Newtype => {
|
|
|
|
write!(w, "(");
|
|
|
|
for (i, field) in fields.iter().enumerate() {
|
|
|
|
if i > 0 { write!(w, ", ") }
|
|
|
|
match field.inner {
|
|
|
|
clean::StructFieldItem(ref field) => {
|
|
|
|
write!(w, "{}", field.type_);
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(w, ");");
|
|
|
|
}
|
|
|
|
doctree::Unit => { write!(w, ";"); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_methods(w: &mut io::Writer, it: &clean::Item) {
|
|
|
|
do local_data::get(cache_key) |cache| {
|
|
|
|
let cache = cache.unwrap();
|
|
|
|
do cache.read |c| {
|
|
|
|
match c.impls.find(&it.id) {
|
|
|
|
Some(v) => {
|
|
|
|
let mut non_trait = v.iter().filter(|i| i.trait_.is_none());
|
|
|
|
let non_trait = non_trait.to_owned_vec();
|
|
|
|
let mut traits = v.iter().filter(|i| i.trait_.is_some());
|
|
|
|
let traits = traits.to_owned_vec();
|
|
|
|
|
|
|
|
if non_trait.len() > 0 {
|
|
|
|
write!(w, "<h2 id='methods'>Methods</h2>");
|
|
|
|
for &i in non_trait.iter() {
|
|
|
|
render_impl(w, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if traits.len() > 0 {
|
|
|
|
write!(w, "<h2 id='implementations'>Trait \
|
|
|
|
Implementations</h2>");
|
|
|
|
for &i in traits.iter() {
|
|
|
|
render_impl(w, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_impl(w: &mut io::Writer, i: &clean::Impl) {
|
|
|
|
write!(w, "<h3 class='impl'><code>impl{} ", i.generics);
|
|
|
|
let trait_id = match i.trait_ {
|
|
|
|
Some(ref ty) => {
|
|
|
|
write!(w, "{} for ", *ty);
|
|
|
|
match *ty {
|
|
|
|
clean::ResolvedPath { id, _ } => Some(id),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => None
|
|
|
|
};
|
|
|
|
write!(w, "{}</code></h3>", i.for_);
|
|
|
|
write!(w, "<div class='methods'>");
|
|
|
|
for meth in i.methods.iter() {
|
|
|
|
write!(w, "<h4 id='fn.{}' class='method'><code>",
|
|
|
|
*meth.name.get_ref());
|
|
|
|
render_method(w, meth, false);
|
|
|
|
write!(w, "</code></h4>\n");
|
|
|
|
match meth.doc_value() {
|
|
|
|
Some(s) => {
|
|
|
|
write!(w, "<div class='docblock'>{}</div>", Markdown(s));
|
|
|
|
loop
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No documentation? Attempt to slurp in the trait's documentation
|
|
|
|
let trait_id = match trait_id { Some(id) => id, None => loop };
|
|
|
|
do local_data::get(cache_key) |cache| {
|
|
|
|
do cache.unwrap().read |cache| {
|
|
|
|
let name = meth.name.get_ref().as_slice();
|
|
|
|
match cache.traits.find(&trait_id) {
|
|
|
|
Some(m) => {
|
|
|
|
match m.find_equiv(&name) {
|
|
|
|
Some(s) => {
|
|
|
|
write!(w, "<div class='docblock'>{}</div>",
|
|
|
|
Markdown(s.as_slice()));
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write!(w, "</div>");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_typedef(w: &mut io::Writer, it: &clean::Item, t: &clean::Typedef) {
|
|
|
|
write!(w, "<pre class='typedef'>type {}{} = {};</pre>",
|
|
|
|
it.name.get_ref().as_slice(),
|
|
|
|
t.generics,
|
|
|
|
t.type_);
|
|
|
|
|
|
|
|
document(w, it);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'self> fmt::Default for Sidebar<'self> {
|
|
|
|
fn fmt(s: &Sidebar<'self>, fmt: &mut fmt::Formatter) {
|
|
|
|
let cx = s.cx;
|
|
|
|
let it = s.item;
|
|
|
|
write!(fmt.buf, "<p class='location'>");
|
|
|
|
let len = cx.current.len() - if it.is_mod() {1} else {0};
|
|
|
|
for (i, name) in cx.current.iter().take(len).enumerate() {
|
|
|
|
if i > 0 { write!(fmt.buf, "&\\#8203;::") }
|
|
|
|
write!(fmt.buf, "<a href='{}index.html'>{}</a>",
|
|
|
|
cx.root_path.slice_to((cx.current.len() - i - 1) * 3), *name);
|
|
|
|
}
|
|
|
|
write!(fmt.buf, "</p>");
|
|
|
|
|
|
|
|
fn block(w: &mut io::Writer, short: &str, longty: &str,
|
|
|
|
cur: &clean::Item, cx: &Context) {
|
|
|
|
let items = match cx.sidebar.find_equiv(&short) {
|
|
|
|
Some(items) => items.as_slice(),
|
|
|
|
None => return
|
|
|
|
};
|
|
|
|
write!(w, "<div class='block {}'><h2>{}</h2>", short, longty);
|
|
|
|
for item in items.iter() {
|
|
|
|
let class = if cur.name.get_ref() == item &&
|
|
|
|
short == shortty(cur) { "current" } else { "" };
|
|
|
|
write!(w, "<a class='{ty} {class}' href='{curty, select,
|
|
|
|
mod{../}
|
|
|
|
other{}
|
|
|
|
}{ty, select,
|
|
|
|
mod{{name}/index.html}
|
|
|
|
other{#.{name}.html}
|
|
|
|
}'>{name}</a><br/>",
|
|
|
|
ty = short,
|
|
|
|
class = class,
|
|
|
|
curty = shortty(cur),
|
|
|
|
name = item.as_slice());
|
|
|
|
}
|
|
|
|
write!(w, "</div>");
|
|
|
|
}
|
|
|
|
|
|
|
|
block(fmt.buf, "mod", "Modules", it, cx);
|
|
|
|
block(fmt.buf, "struct", "Structs", it, cx);
|
|
|
|
block(fmt.buf, "enum", "Enums", it, cx);
|
|
|
|
block(fmt.buf, "trait", "Traits", it, cx);
|
|
|
|
block(fmt.buf, "fn", "Functions", it, cx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
for item in m.items.iter() {
|
|
|
|
let short = shortty(item);
|
|
|
|
let myname = match item.name {
|
|
|
|
None => loop,
|
|
|
|
Some(ref s) => s.to_owned(),
|
|
|
|
};
|
|
|
|
let v = map.find_or_insert_with(short.to_owned(), |_| ~[]);
|
|
|
|
v.push(myname);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (_, items) in map.mut_iter() {
|
|
|
|
sort::quick_sort(*items, |i1, i2| i1 < i2);
|
|
|
|
}
|
|
|
|
return map;
|
|
|
|
}
|