Files
rust/src/librustdoc/html/render.rs
Alex Crichton ca697d3705 rustdoc: Generate documentation for foreign items
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
2013-09-26 11:57:25 -07:00

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'>{}&nbsp;</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());
}