diff options
Diffstat (limited to 'src/librustdoc/html/render.rs')
| -rw-r--r-- | src/librustdoc/html/render.rs | 1108 |
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'>{} </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; +} |
