// 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 or the MIT license // , 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, // type id => all implementations for that type impls: HashMap, // path id => (full qualified path, shortty) -- used to generate urls paths: HashMap, // trait id => method name => dox traits: HashMap>, // trait id => implementors of the trait implementors: HashMap, 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, } local_data_key!(pub cache_key: RWArc) 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 { // 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(&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::(); 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, port: &SharedPort, chan: &SharedChan, prog_chan: &SharedChan) { #[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); } } } _ => {} } } _ => {} } } } 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, "

"); 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, "{}::", trail, component.as_slice()); } write!(fmt.buf, "{}

", 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, "
{}
", 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::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, ""); } curty = myty; write!(w, "

{}

\n", 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}>{}", s.as_slice(), tag=tag); } } write!(w, " ", 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, ""); } clean::Import(ref imports) => { for import in imports.iter() { write!(w, "", VisSpace(myitem.visibility), *import); } } } } _ => { if myitem.name.is_none() { loop } write!(w, " ", *myitem.name.get_ref(), Markdown(shorter(myitem.doc_value())), class = shortty(myitem), href = item_path(myitem), title = full_path(cx, myitem)); } } } write!(w, "
{}static {}: {} = {} {} 
extern mod {}", name.as_slice()); match *src { Some(ref src) => write!(w, " = \"{}\"", src.as_slice()), None => {} } write!(w, ";
{}{}
{} {}
"); } fn item_function(w: &mut io::Writer, it: &clean::Item, f: &clean::Function) { write!(w, "
{vis}{purity}fn {name}{generics}{decl}
", 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, "
{}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, "
"); // Trait documentation document(w, it); fn meth(w: &mut io::Writer, m: &clean::TraitMethod) { write!(w, "

", *m.item().name.get_ref()); render_method(w, m.item(), false); write!(w, "

"); document(w, m.item()); } // Output the documentation for each function individually if required.len() > 0 { write!(w, "

Required Methods

"); for m in required.iter() { meth(w, *m); } write!(w, "
"); } if provided.len() > 0 { write!(w, "

Provided Methods

"); for m in provided.iter() { meth(w, *m); } write!(w, "
"); } do local_data::get(cache_key) |cache| { do cache.unwrap().read |cache| { match cache.implementors.find(&it.id) { Some(implementors) => { write!(w, "

Implementors

    "); for i in implementors.iter() { match *i { PathType(ref ty) => { write!(w, "
  • {}
  • ", *ty); } OtherType(ref generics, ref trait_, ref for_) => { write!(w, "
  • impl{} {} for {}
  • ", *generics, *trait_, *for_); } } } write!(w, "
"); } 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{{name}} 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, "
");
    render_struct(w, it, Some(&s.generics), s.struct_type, s.fields, "");
    write!(w, "
"); document(w, it); render_methods(w, it); } fn item_enum(w: &mut io::Writer, it: &clean::Item, e: &clean::Enum) { write!(w, "
{}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, "
"); 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, "

Methods

"); for &i in non_trait.iter() { render_impl(w, i); } } if traits.len() > 0 { write!(w, "

Trait \ Implementations

"); for &i in traits.iter() { render_impl(w, i); } } } None => {} } } } } fn render_impl(w: &mut io::Writer, i: &clean::Impl) { write!(w, "

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, "{}

", i.for_); write!(w, "
"); for meth in i.methods.iter() { write!(w, "

", *meth.name.get_ref()); render_method(w, meth, false); write!(w, "

\n"); match meth.doc_value() { Some(s) => { write!(w, "
{}
", 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, "
{}
", Markdown(s.as_slice())); } None => {} } } None => {} } } } } write!(w, "
"); } fn item_typedef(w: &mut io::Writer, it: &clean::Item, t: &clean::Typedef) { write!(w, "
type {}{} = {};
", 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, "

"); 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, "{}", cx.root_path.slice_to((cx.current.len() - i - 1) * 3), *name); } write!(fmt.buf, "

"); 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, "

{}

", short, longty); for item in items.iter() { let class = if cur.name.get_ref() == item && short == shortty(cur) { "current" } else { "" }; write!(w, "{name}
", ty = short, class = class, curty = shortty(cur), name = item.as_slice()); } write!(w, "
"); } 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; }