about summary refs log tree commit diff
path: root/src/librustdoc/html/render.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/html/render.rs')
-rw-r--r--src/librustdoc/html/render.rs1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
new file mode 100644
index 00000000000..b004061e04a
--- /dev/null
+++ b/src/librustdoc/html/render.rs
@@ -0,0 +1,1108 @@
+// 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};
+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
+    write(cx.dst.push("jquery.js"), include_str!("static/jquery-2.0.3.min.js"));
+    write(cx.dst.push("main.js"), include_str!("static/main.js"));
+    write(cx.dst.push("main.css"), include_str!("static/main.css"));
+    write(cx.dst.push("normalize.css"), include_str!("static/normalize.css"));
+    write(cx.dst.push("index.html"), format!("
+        <DOCTYPE html><html><head>
+            <meta http-equiv='refresh'
+                  content=\"0; url={}/index.html\">
+        </head><body></body></html>
+    ", crate.name));
+
+    {
+        mkdir(&cx.dst.push(crate.name));
+        let dst = cx.dst.push(crate.name).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(*) => {
+                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));
+            do task.spawn_with(cache.clone()) |cache| {
+                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,
+                    }
+                }
+            }
+        }
+
+        let watcher_chan = chan.clone();
+        let (done_port, done_chan) = comm::stream();
+        do task::spawn {
+            let mut jobs = 0;
+            loop {
+                match prog_port.recv() {
+                    JobNew => jobs += 1,
+                    JobDone => jobs -= 1,
+                }
+
+                if jobs == 0 { break }
+            }
+
+            for _ in range(0, WORKERS) {
+                watcher_chan.send(Die);
+            }
+            done_chan.send(());
+        }
+
+        prog_chan.send(JobNew);
+        chan.send(Process(self, item));
+        done_port.recv();
+    }
+
+    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 {
+            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);
+                    }
+                }
+            }
+            _ 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);
+            }
+            _ => {}
+        }
+    }
+}
+
+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);
+    let mut indices = vec::from_fn(items.len(), |i| i);
+
+    fn lt(i1: &clean::Item, i2: &clean::Item) -> bool {
+        if shortty(i1) == shortty(i2) {
+            return i1.name < i2.name;
+        }
+        match (&i1.inner, &i2.inner) {
+            (&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,
+            _ => false,
+        }
+    }
+
+    do sort::quick_sort(indices) |&i1, &i2| {
+        lt(&items[i1], &items[i2])
+    }
+
+    let mut curty = "";
+    for &idx in indices.iter() {
+        let myitem = &items[idx];
+        if myitem.name.is_none() { loop }
+
+        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>
+                        <td><code>{}: {} = </code>{}</td>
+                        <td class='docblock'>{}&nbsp;</td>
+                    </tr>
+                ",
+                *myitem.name.get_ref(),
+                s.type_,
+                Initializer(s.expr),
+                Markdown(blank(myitem.doc_value())));
+            }
+
+            _ => {
+                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}fn {name}{generics}{decl}</pre>",
+           vis = VisSpace(it.visibility),
+           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;
+}