This slurps up everything inside of an 'extern' block into the enclosing module in order to document them. The documentation must be on the items themselves, and they'll show up next to everything else on the module index pages. Closes #5953
1204 lines
42 KiB
Rust
1204 lines
42 KiB
Rust
// 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;
|
|
use html::format::{VisSpace, Method, PuritySpace};
|
|
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
|
|
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"));
|
|
|
|
{
|
|
let dst = dst.push("search-index.js");
|
|
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(*) |
|
|
clean::TypedefItem(*) | clean::TraitItem(*) |
|
|
clean::FunctionItem(*) | clean::ModuleItem(*) |
|
|
clean::VariantItem(*) => {
|
|
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));
|
|
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*
|
|
local_data::set(cache_key, cache);
|
|
|
|
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,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
chan.send(Process(self, item));
|
|
let mut jobs = 1;
|
|
loop {
|
|
match prog_port.recv() {
|
|
JobNew => jobs += 1,
|
|
JobDone => jobs -= 1,
|
|
}
|
|
|
|
if jobs == 0 { break }
|
|
}
|
|
|
|
for _ in range(0, WORKERS) {
|
|
chan.send(Die);
|
|
}
|
|
}
|
|
|
|
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 {
|
|
// modules are special because they add a namespace. We also need to
|
|
// recurse into the items of the module as well.
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Things which don't have names (like impls) don't get special
|
|
// pages dedicated to them.
|
|
_ 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);
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
clean::StructItem(s) => {
|
|
let mut it = s.fields.move_iter();
|
|
do self.recurse(name) |this| {
|
|
for item in it {
|
|
f(this, item);
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
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",
|
|
clean::ForeignFunctionItem(*) => "ffi",
|
|
clean::ForeignStaticItem(*) => "ffs",
|
|
}
|
|
}
|
|
|
|
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) | clean::ForeignFunctionItem(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),
|
|
clean::VariantItem(*) => item_variant(fmt.buf, it.cx, it.item),
|
|
clean::StructFieldItem(*) => item_struct_field(fmt.buf, it.cx,
|
|
it.item),
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
debug2!("{:?}", items);
|
|
let mut indices = vec::from_fn(items.len(), |i| i);
|
|
|
|
fn lt(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> bool {
|
|
if shortty(i1) == shortty(i2) {
|
|
return i1.name < i2.name;
|
|
}
|
|
match (&i1.inner, &i2.inner) {
|
|
(&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => {
|
|
match (&a.inner, &b.inner) {
|
|
(&clean::ExternMod(*), _) => true,
|
|
(_, &clean::ExternMod(*)) => false,
|
|
_ => idx1 < idx2,
|
|
}
|
|
}
|
|
(&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::ForeignFunctionItem(*), _) => true,
|
|
(_, &clean::ForeignFunctionItem(*)) => false,
|
|
(&clean::ForeignStaticItem(*), _) => true,
|
|
(_, &clean::ForeignStaticItem(*)) => false,
|
|
(&clean::TraitItem(*), _) => true,
|
|
(_, &clean::TraitItem(*)) => false,
|
|
(&clean::FunctionItem(*), _) => true,
|
|
(_, &clean::FunctionItem(*)) => false,
|
|
(&clean::TypedefItem(*), _) => true,
|
|
(_, &clean::TypedefItem(*)) => false,
|
|
_ => idx1 < idx2,
|
|
}
|
|
}
|
|
|
|
debug2!("{:?}", indices);
|
|
do sort::quick_sort(indices) |&i1, &i2| {
|
|
lt(&items[i1], &items[i2], i1, i2)
|
|
}
|
|
|
|
debug2!("{:?}", indices);
|
|
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",
|
|
clean::ForeignFunctionItem(*) => "Foreign Functions",
|
|
clean::ForeignStaticItem(*) => "Foreign Statics",
|
|
});
|
|
}
|
|
|
|
match myitem.inner {
|
|
clean::StaticItem(ref s) | clean::ForeignStaticItem(ref s) => {
|
|
struct Initializer<'self>(&'self str);
|
|
impl<'self> fmt::Default for Initializer<'self> {
|
|
fn fmt(s: &Initializer<'self>, f: &mut fmt::Formatter) {
|
|
if s.len() == 0 { return; }
|
|
write!(f.buf, "<code> = </code>");
|
|
let tag = if s.contains("\n") { "pre" } else { "code" };
|
|
write!(f.buf, "<{tag}>{}</{tag}>",
|
|
s.as_slice(), tag=tag);
|
|
}
|
|
}
|
|
|
|
write!(w, "
|
|
<tr>
|
|
<td><code>{}static {}: {}</code>{}</td>
|
|
<td class='docblock'>{} </td>
|
|
</tr>
|
|
",
|
|
VisSpace(myitem.visibility),
|
|
*myitem.name.get_ref(),
|
|
s.type_,
|
|
Initializer(s.expr),
|
|
Markdown(blank(myitem.doc_value())));
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
if myitem.name.is_none() { loop }
|
|
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) {
|
|
write!(w, "<pre class='fn'>{vis}{purity}fn {name}{generics}{decl}</pre>",
|
|
vis = VisSpace(it.visibility),
|
|
purity = PuritySpace(f.purity),
|
|
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}' class='fnname'>{name}</a>}
|
|
other{<span class='fnname'>{name}</span>}
|
|
}{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 = format!("<a name='variant.{0}'>{0}</a>",
|
|
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, " {}<a name='field.{name}'>{name}</a>: \
|
|
{},\n{}",
|
|
VisSpace(field.visibility),
|
|
ty.type_,
|
|
tab,
|
|
name = field.name.get_ref().as_slice());
|
|
}
|
|
_ => 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;
|
|
}
|
|
|
|
fn item_variant(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
|
|
write!(w, "<DOCTYPE html><html><head>\
|
|
<meta http-equiv='refresh' content='0; \
|
|
url=../enum.{}.html\\#variant.{}'>\
|
|
</head><body></body></html>",
|
|
*cx.current.last(),
|
|
it.name.get_ref().as_slice());
|
|
}
|
|
|
|
fn item_struct_field(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
|
|
write!(w, "<DOCTYPE html><html><head>\
|
|
<meta http-equiv='refresh' content='0; \
|
|
url=../struct.{}.html\\#field.{}'>\
|
|
</head><body></body></html>",
|
|
*cx.current.last(),
|
|
it.name.get_ref().as_slice());
|
|
}
|