// 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::fmt; use std::local_data; use std::rt::io; use syntax::ast; use syntax::ast_util; use clean; use html::render::{cache_key, current_location_key}; pub struct VisSpace(Option); pub struct PuritySpace(ast::purity); pub struct Method<'self>(&'self clean::SelfTy, &'self clean::FnDecl); impl fmt::Default for clean::Generics { fn fmt(g: &clean::Generics, f: &mut fmt::Formatter) { if g.lifetimes.len() == 0 && g.type_params.len() == 0 { return } f.buf.write("<".as_bytes()); for (i, life) in g.lifetimes.iter().enumerate() { if i > 0 { f.buf.write(", ".as_bytes()); } write!(f.buf, "{}", *life); } if g.type_params.len() > 0 { if g.lifetimes.len() > 0 { f.buf.write(", ".as_bytes()); } for (i, tp) in g.type_params.iter().enumerate() { if i > 0 { f.buf.write(", ".as_bytes()) } f.buf.write(tp.name.as_bytes()); if tp.bounds.len() > 0 { f.buf.write(": ".as_bytes()); for (i, bound) in tp.bounds.iter().enumerate() { if i > 0 { f.buf.write(" + ".as_bytes()); } write!(f.buf, "{}", *bound); } } } } f.buf.write(">".as_bytes()); } } impl fmt::Default for clean::Lifetime { fn fmt(l: &clean::Lifetime, f: &mut fmt::Formatter) { f.buf.write("'".as_bytes()); f.buf.write(l.as_bytes()); } } impl fmt::Default for clean::TyParamBound { fn fmt(bound: &clean::TyParamBound, f: &mut fmt::Formatter) { match *bound { clean::RegionBound => { f.buf.write("'static".as_bytes()) } clean::TraitBound(ref ty) => { write!(f.buf, "{}", *ty); } } } } impl fmt::Default for clean::Path { fn fmt(path: &clean::Path, f: &mut fmt::Formatter) { if path.global { f.buf.write("::".as_bytes()) } for (i, seg) in path.segments.iter().enumerate() { if i > 0 { f.buf.write("::".as_bytes()) } f.buf.write(seg.name.as_bytes()); if seg.lifetime.is_some() || seg.types.len() > 0 { f.buf.write("<".as_bytes()); match seg.lifetime { Some(ref lifetime) => write!(f.buf, "{}", *lifetime), None => {} } for (i, ty) in seg.types.iter().enumerate() { if i > 0 || seg.lifetime.is_some() { f.buf.write(", ".as_bytes()); } write!(f.buf, "{}", *ty); } f.buf.write(">".as_bytes()); } } } } fn resolved_path(w: &mut io::Writer, id: ast::NodeId, path: &clean::Path, print_all: bool) { // The generics will get written to both the title and link let mut generics = ~""; let last = path.segments.last(); if last.lifetime.is_some() || last.types.len() > 0 { generics.push_str("<"); match last.lifetime { Some(ref lifetime) => generics.push_str(format!("{}", *lifetime)), None => {} } for (i, ty) in last.types.iter().enumerate() { if i > 0 || last.lifetime.is_some() { generics.push_str(", "); } generics.push_str(format!("{}", *ty)); } generics.push_str(">"); } // Did someone say rightward-drift? do local_data::get(current_location_key) |loc| { let loc = loc.unwrap(); if print_all { let mut root = match path.segments[0].name.as_slice() { "super" => ~"../", "self" => ~"", _ => "../".repeat(loc.len() - 1), }; let amt = path.segments.len() - 1; for seg in path.segments.slice_to(amt).iter() { if "super" == seg.name || "self" == seg.name { write!(w, "{}::", seg.name); } else { root.push_str(seg.name); root.push_str("/"); write!(w, "{}::", root, seg.name); } } } do local_data::get(cache_key) |cache| { do cache.unwrap().read |cache| { match cache.paths.find(&id) { // This is a documented path, link to it! Some(&(ref fqp, shortty)) => { let fqn = fqp.connect("::"); let same = loc.iter().zip(fqp.iter()) .take_while(|&(a, b)| *a == *b).len(); let mut url = ~""; if "super" == path.segments[0].name { url.push_str("../"); } else if "self" != path.segments[0].name { url.push_str("../".repeat(loc.len() - same)); } if same < fqp.len() { let remaining = fqp.slice_from(same); let to_link = remaining.slice_to(remaining.len() - 1); for component in to_link.iter() { url.push_str(*component); url.push_str("/"); } } match shortty { "mod" => { url.push_str(*fqp.last()); url.push_str("/index.html"); } _ => { url.push_str(shortty); url.push_str("."); url.push_str(*fqp.last()); url.push_str(".html"); } } write!(w, "{}{}", shortty, url, fqn, last.name, generics); } None => { if print_all { let amt = path.segments.len() - 1; for seg in path.segments.iter().take(amt) { write!(w, "{}::", seg.name); } } write!(w, "{}{}", last.name, generics); } }; } } } } impl fmt::Default for clean::Type { fn fmt(g: &clean::Type, f: &mut fmt::Formatter) { match *g { clean::TyParamBinder(id) | clean::Generic(id) => { do local_data::get(cache_key) |cache| { do cache.unwrap().read |m| { f.buf.write(m.typarams.get(&id).as_bytes()); } } } clean::ResolvedPath{id, typarams: ref typarams, path: ref path} => { resolved_path(f.buf, id, path, false); match *typarams { Some(ref params) => { f.buf.write("<".as_bytes()); for (i, param) in params.iter().enumerate() { if i > 0 { f.buf.write(", ".as_bytes()) } write!(f.buf, "{}", *param); } f.buf.write(">".as_bytes()); } None => {} } } // XXX: this should be a link clean::External(ref a, _) => { write!(f.buf, "{}", *a); } clean::Self(*) => f.buf.write("Self".as_bytes()), clean::Primitive(prim) => { let s = match prim { ast::ty_int(ast::ty_i) => "int", ast::ty_int(ast::ty_i8) => "i8", ast::ty_int(ast::ty_i16) => "i16", ast::ty_int(ast::ty_i32) => "i32", ast::ty_int(ast::ty_i64) => "i64", ast::ty_uint(ast::ty_u) => "uint", ast::ty_uint(ast::ty_u8) => "u8", ast::ty_uint(ast::ty_u16) => "u16", ast::ty_uint(ast::ty_u32) => "u32", ast::ty_uint(ast::ty_u64) => "u64", ast::ty_float(ast::ty_f) => "float", ast::ty_float(ast::ty_f32) => "f32", ast::ty_float(ast::ty_f64) => "f64", ast::ty_str => "str", ast::ty_bool => "bool", ast::ty_char => "char", }; f.buf.write(s.as_bytes()); } clean::Closure(ref decl) => { f.buf.write(match decl.sigil { ast::BorrowedSigil => "&", ast::ManagedSigil => "@", ast::OwnedSigil => "~", }.as_bytes()); match decl.region { Some(ref region) => write!(f.buf, "{} ", *region), None => {} } write!(f.buf, "{}{}fn{}", PuritySpace(decl.purity), match decl.onceness { ast::Once => "once ", ast::Many => "", }, decl.decl); // XXX: where are bounds and lifetimes printed?! } clean::BareFunction(ref decl) => { write!(f.buf, "{}{}fn{}{}", PuritySpace(decl.purity), match decl.abi { ~"" | ~"\"Rust\"" => ~"", ref s => " " + *s + " ", }, decl.generics, decl.decl); } clean::Tuple(ref typs) => { f.buf.write("(".as_bytes()); for (i, typ) in typs.iter().enumerate() { if i > 0 { f.buf.write(", ".as_bytes()) } write!(f.buf, "{}", *typ); } f.buf.write(")".as_bytes()); } clean::Vector(ref t) => write!(f.buf, "[{}]", **t), clean::FixedVector(ref t, ref s) => { write!(f.buf, "[{}, ..{}]", **t, *s); } clean::String => f.buf.write("str".as_bytes()), clean::Bool => f.buf.write("bool".as_bytes()), clean::Unit => f.buf.write("()".as_bytes()), clean::Bottom => f.buf.write("!".as_bytes()), clean::Unique(ref t) => write!(f.buf, "~{}", **t), clean::Managed(m, ref t) => { write!(f.buf, "@{}{}", match m { clean::Mutable => "mut ", clean::Immutable => "", }, **t) } clean::RawPointer(m, ref t) => { write!(f.buf, "*{}{}", match m { clean::Mutable => "mut ", clean::Immutable => "", }, **t) } clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => { let lt = match *l { Some(ref l) => format!("{} ", *l), _ => ~"" }; write!(f.buf, "&{}{}{}", lt, match mutability { clean::Mutable => "mut ", clean::Immutable => "", }, **ty); } } } } impl fmt::Default for clean::FnDecl { fn fmt(d: &clean::FnDecl, f: &mut fmt::Formatter) { let mut args = ~""; for (i, input) in d.inputs.iter().enumerate() { if i > 0 { args.push_str(", "); } if input.name.len() > 0 { args.push_str(format!("{}: ", input.name)); } args.push_str(format!("{}", input.type_)); } write!(f.buf, "({args}){arrow, select, yes{ -> {ret}} other{}}", args = args, arrow = match d.output { clean::Unit => "no", _ => "yes" }, ret = d.output); } } impl<'self> fmt::Default for Method<'self> { fn fmt(m: &Method<'self>, f: &mut fmt::Formatter) { let Method(selfty, d) = *m; let mut args = ~""; match *selfty { clean::SelfStatic => {}, clean::SelfValue => args.push_str("self"), clean::SelfOwned => args.push_str("~self"), clean::SelfManaged(clean::Mutable) => args.push_str("@mut self"), clean::SelfManaged(clean::Immutable) => args.push_str("@self"), clean::SelfBorrowed(Some(ref lt), clean::Immutable) => { args.push_str(format!("&{} self", *lt)); } clean::SelfBorrowed(Some(ref lt), clean::Mutable) => { args.push_str(format!("&{} mut self", *lt)); } clean::SelfBorrowed(None, clean::Mutable) => { args.push_str("&mut self"); } clean::SelfBorrowed(None, clean::Immutable) => { args.push_str("&self"); } } for (i, input) in d.inputs.iter().enumerate() { if i > 0 || args.len() > 0 { args.push_str(", "); } if input.name.len() > 0 { args.push_str(format!("{}: ", input.name)); } args.push_str(format!("{}", input.type_)); } write!(f.buf, "({args}){arrow, select, yes{ -> {ret}} other{}}", args = args, arrow = match d.output { clean::Unit => "no", _ => "yes" }, ret = d.output); } } impl fmt::Default for VisSpace { fn fmt(v: &VisSpace, f: &mut fmt::Formatter) { match **v { Some(ast::public) => { write!(f.buf, "pub "); } Some(ast::private) => { write!(f.buf, "priv "); } Some(ast::inherited) | None => {} } } } impl fmt::Default for PuritySpace { fn fmt(p: &PuritySpace, f: &mut fmt::Formatter) { match **p { ast::unsafe_fn => write!(f.buf, "unsafe "), ast::extern_fn => write!(f.buf, "extern "), ast::impure_fn => {} } } } impl fmt::Default for clean::ViewPath { fn fmt(v: &clean::ViewPath, f: &mut fmt::Formatter) { match *v { clean::SimpleImport(ref name, ref src) => { if *name == src.path.segments.last().name { write!(f.buf, "use {};", *src); } else { write!(f.buf, "use {} = {};", *name, *src); } } clean::GlobImport(ref src) => { write!(f.buf, "use {}::*;", *src); } clean::ImportList(ref src, ref names) => { write!(f.buf, "use {}::\\{", *src); for (i, n) in names.iter().enumerate() { if i > 0 { write!(f.buf, ", "); } write!(f.buf, "{}", *n); } write!(f.buf, "\\};"); } } } } impl fmt::Default for clean::ImportSource { fn fmt(v: &clean::ImportSource, f: &mut fmt::Formatter) { match v.did { Some(did) if ast_util::is_local(did) => { resolved_path(f.buf, did.node, &v.path, true); } _ => { for (i, seg) in v.path.segments.iter().enumerate() { if i > 0 { write!(f.buf, "::") } write!(f.buf, "{}", seg.name); } } } } } impl fmt::Default for clean::ViewListIdent { fn fmt(v: &clean::ViewListIdent, f: &mut fmt::Formatter) { match v.source { Some(did) if ast_util::is_local(did) => { let path = clean::Path { global: false, segments: ~[clean::PathSegment { name: v.name.clone(), lifetime: None, types: ~[], }] }; resolved_path(f.buf, did.node, &path, false); } _ => write!(f.buf, "{}", v.name), } } }