1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! HTML formatting module
13 //! This module contains a large number of `fmt::Show` implementations for
14 //! various types in `rustdoc::clean`. These implementations all currently
15 //! assume that HTML output is desired, although it may be possible to redesign
16 //! them in the future to instead emit any format desired.
27 use html::render::{cache_key, current_location_key};
29 /// Helper to render an optional visibility with a space after it (if the
30 /// visibility is preset)
31 pub struct VisSpace(Option<ast::Visibility>);
32 /// Similarly to VisSpace, this structure is used to render a purity with a
34 pub struct PuritySpace(ast::Purity);
35 /// Wrapper struct for properly emitting a method declaration.
36 pub struct Method<'a>(&'a clean::SelfTy, &'a clean::FnDecl);
39 pub fn get(&self) -> Option<ast::Visibility> {
40 let VisSpace(v) = *self; v
45 pub fn get(&self) -> ast::Purity {
46 let PuritySpace(v) = *self; v
50 impl fmt::Show for clean::Generics {
51 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
53 try!(f.buf.write("<".as_bytes()));
55 for (i, life) in self.lifetimes.iter().enumerate() {
57 try!(f.buf.write(", ".as_bytes()));
59 try!(write!(f.buf, "{}", *life));
62 if self.type_params.len() > 0 {
63 if self.lifetimes.len() > 0 {
64 try!(f.buf.write(", ".as_bytes()));
67 for (i, tp) in self.type_params.iter().enumerate() {
69 try!(f.buf.write(", ".as_bytes()))
71 try!(f.buf.write(tp.name.as_bytes()));
73 if tp.bounds.len() > 0 {
74 try!(f.buf.write(": ".as_bytes()));
75 for (i, bound) in tp.bounds.iter().enumerate() {
77 try!(f.buf.write(" + ".as_bytes()));
79 try!(write!(f.buf, "{}", *bound));
84 try!(f.buf.write(">".as_bytes()));
89 impl fmt::Show for clean::Lifetime {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 try!(f.buf.write("'".as_bytes()));
92 try!(f.buf.write(self.get_ref().as_bytes()));
97 impl fmt::Show for clean::TyParamBound {
98 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 clean::RegionBound => {
101 f.buf.write("'static".as_bytes())
103 clean::TraitBound(ref ty) => {
104 write!(f.buf, "{}", *ty)
110 impl fmt::Show for clean::Path {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113 try!(f.buf.write("::".as_bytes()))
115 for (i, seg) in self.segments.iter().enumerate() {
117 try!(f.buf.write("::".as_bytes()))
119 try!(f.buf.write(seg.name.as_bytes()));
121 if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
122 try!(f.buf.write("<".as_bytes()));
123 let mut comma = false;
124 for lifetime in seg.lifetimes.iter() {
126 try!(f.buf.write(", ".as_bytes()));
129 try!(write!(f.buf, "{}", *lifetime));
131 for ty in seg.types.iter() {
133 try!(f.buf.write(", ".as_bytes()));
136 try!(write!(f.buf, "{}", *ty));
138 try!(f.buf.write(">".as_bytes()));
145 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
146 /// rendering function with the necessary arguments for linking to a local path.
147 fn resolved_path(w: &mut io::Writer, id: ast::NodeId, p: &clean::Path,
148 print_all: bool) -> fmt::Result {
149 path(w, p, print_all,
150 |_cache, loc| { Some("../".repeat(loc.len())) },
152 match cache.paths.find(&id) {
154 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
159 /// Used when rendering an `ExternalPath` structure. Like `resolved_path` this
160 /// will invoke `path` with proper linking-style arguments.
161 fn external_path(w: &mut io::Writer, p: &clean::Path, print_all: bool,
162 fqn: &[~str], kind: clean::TypeKind,
163 krate: ast::CrateNum) -> fmt::Result {
164 path(w, p, print_all,
166 match *cache.extern_locations.get(&krate) {
167 render::Remote(ref s) => Some(s.clone()),
168 render::Local => Some("../".repeat(loc.len())),
169 render::Unknown => None,
173 Some((fqn.to_owned(), match kind {
174 clean::TypeStruct => "struct",
175 clean::TypeEnum => "enum",
176 clean::TypeFunction => "fn",
177 clean::TypeTrait => "trait",
182 fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool,
183 root: |&render::Cache, &[~str]| -> Option<~str>,
184 info: |&render::Cache| -> Option<(~[~str], &'static str)>)
187 // The generics will get written to both the title and link
188 let mut generics = ~"";
189 let last = path.segments.last().unwrap();
190 if last.lifetimes.len() > 0 || last.types.len() > 0 {
192 generics.push_str("<");
193 for lifetime in last.lifetimes.iter() {
194 if counter > 0 { generics.push_str(", "); }
196 generics.push_str(format!("{}", *lifetime));
198 for ty in last.types.iter() {
199 if counter > 0 { generics.push_str(", "); }
201 generics.push_str(format!("{}", *ty));
203 generics.push_str(">");
206 // Did someone say rightward-drift?
207 local_data::get(current_location_key, |loc| {
208 let loc = loc.unwrap();
210 local_data::get(cache_key, |cache| {
211 let cache = cache.unwrap().get();
212 let abs_root = root(cache, loc.as_slice());
213 let rel_root = match path.segments[0].name.as_slice() {
214 "self" => Some(~"./"),
219 let amt = path.segments.len() - 1;
223 for seg in path.segments.slice_to(amt).iter() {
224 if "super" == seg.name || "self" == seg.name {
225 try!(write!(w, "{}::", seg.name));
227 root.push_str(seg.name);
229 try!(write!(w, "<a class='mod'
230 href='{}index.html'>{}</a>::",
237 for seg in path.segments.slice_to(amt).iter() {
238 try!(write!(w, "{}::", seg.name));
245 // This is a documented path, link to it!
246 Some((ref fqp, shortty)) if abs_root.is_some() => {
247 let mut url = abs_root.unwrap();
248 let to_link = fqp.slice_to(fqp.len() - 1);
249 for component in to_link.iter() {
250 url.push_str(*component);
255 url.push_str(*fqp.last().unwrap());
256 url.push_str("/index.html");
259 url.push_str(shortty);
261 url.push_str(*fqp.last().unwrap());
262 url.push_str(".html");
266 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
267 shortty, url, fqp.connect("::"), last.name));
271 try!(write!(w, "{}", last.name));
274 try!(write!(w, "{}", generics));
280 /// Helper to render type parameters
281 fn typarams(w: &mut io::Writer,
282 typarams: &Option<~[clean::TyParamBound]>) -> fmt::Result {
284 Some(ref params) => {
285 try!(write!(w, "<"));
286 for (i, param) in params.iter().enumerate() {
288 try!(write!(w, ", "));
290 try!(write!(w, "{}", *param));
292 try!(write!(w, ">"));
299 impl fmt::Show for clean::Type {
300 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302 clean::TyParamBinder(id) | clean::Generic(id) => {
303 local_data::get(cache_key, |cache| {
304 let m = cache.unwrap().get();
305 f.buf.write(m.typarams.get(&id).as_bytes())
308 clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
309 try!(resolved_path(f.buf, id, path, false));
312 clean::ExternalPath{path: ref path, typarams: ref tp,
313 fqn: ref fqn, kind, krate} => {
314 try!(external_path(f.buf, path, false, fqn.as_slice(), kind,
318 clean::Self(..) => f.buf.write("Self".as_bytes()),
319 clean::Primitive(prim) => {
321 ast::TyInt(ast::TyI) => "int",
322 ast::TyInt(ast::TyI8) => "i8",
323 ast::TyInt(ast::TyI16) => "i16",
324 ast::TyInt(ast::TyI32) => "i32",
325 ast::TyInt(ast::TyI64) => "i64",
326 ast::TyUint(ast::TyU) => "uint",
327 ast::TyUint(ast::TyU8) => "u8",
328 ast::TyUint(ast::TyU16) => "u16",
329 ast::TyUint(ast::TyU32) => "u32",
330 ast::TyUint(ast::TyU64) => "u64",
331 ast::TyFloat(ast::TyF32) => "f32",
332 ast::TyFloat(ast::TyF64) => "f64",
334 ast::TyBool => "bool",
335 ast::TyChar => "char",
337 f.buf.write(s.as_bytes())
339 clean::Closure(ref decl) => {
340 let region = match decl.region {
341 Some(ref region) => format!("{} ", *region),
345 write!(f.buf, "{}{}{arrow, select, yes{ -> {ret}} other{}}",
346 PuritySpace(decl.purity),
348 ast::OwnedSigil => format!("proc({})", decl.decl.inputs),
349 ast::BorrowedSigil => format!("{}|{}|", region, decl.decl.inputs),
350 ast::ManagedSigil => format!("@{}fn({})", region, decl.decl.inputs),
352 arrow = match decl.decl.output { clean::Unit => "no", _ => "yes" },
353 ret = decl.decl.output)
354 // FIXME: where are bounds and lifetimes printed?!
356 clean::BareFunction(ref decl) => {
357 write!(f.buf, "{}{}fn{}{}",
358 PuritySpace(decl.purity),
360 ref x if "" == *x => ~"",
361 ref x if "\"Rust\"" == *x => ~"",
362 ref s => " " + *s + " ",
367 clean::Tuple(ref typs) => {
368 try!(f.buf.write("(".as_bytes()));
369 for (i, typ) in typs.iter().enumerate() {
371 try!(f.buf.write(", ".as_bytes()))
373 try!(write!(f.buf, "{}", *typ));
375 f.buf.write(")".as_bytes())
377 clean::Vector(ref t) => write!(f.buf, "[{}]", **t),
378 clean::FixedVector(ref t, ref s) => {
379 write!(f.buf, "[{}, ..{}]", **t, *s)
381 clean::String => f.buf.write("str".as_bytes()),
382 clean::Bool => f.buf.write("bool".as_bytes()),
383 clean::Unit => f.buf.write("()".as_bytes()),
384 clean::Bottom => f.buf.write("!".as_bytes()),
385 clean::Unique(ref t) => write!(f.buf, "~{}", **t),
386 clean::Managed(ref t) => write!(f.buf, "@{}", **t),
387 clean::RawPointer(m, ref t) => {
388 write!(f.buf, "*{}{}",
390 clean::Mutable => "mut ",
391 clean::Immutable => "",
394 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
395 let lt = match *l { Some(ref l) => format!("{} ", *l), _ => ~"" };
396 write!(f.buf, "&{}{}{}",
399 clean::Mutable => "mut ",
400 clean::Immutable => "",
408 impl fmt::Show for clean::Arguments {
409 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
410 for (i, input) in self.values.iter().enumerate() {
411 if i > 0 { try!(write!(f.buf, ", ")); }
412 if input.name.len() > 0 {
413 try!(write!(f.buf, "{}: ", input.name));
415 try!(write!(f.buf, "{}", input.type_));
421 impl fmt::Show for clean::FnDecl {
422 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423 write!(f.buf, "({args}){arrow, select, yes{ -> {ret}} other{}}",
425 arrow = match self.output { clean::Unit => "no", _ => "yes" },
430 impl<'a> fmt::Show for Method<'a> {
431 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
432 let Method(selfty, d) = *self;
435 clean::SelfStatic => {},
436 clean::SelfValue => args.push_str("self"),
437 clean::SelfOwned => args.push_str("~self"),
438 clean::SelfBorrowed(Some(ref lt), clean::Immutable) => {
439 args.push_str(format!("&{} self", *lt));
441 clean::SelfBorrowed(Some(ref lt), clean::Mutable) => {
442 args.push_str(format!("&{} mut self", *lt));
444 clean::SelfBorrowed(None, clean::Mutable) => {
445 args.push_str("&mut self");
447 clean::SelfBorrowed(None, clean::Immutable) => {
448 args.push_str("&self");
451 for (i, input) in d.inputs.values.iter().enumerate() {
452 if i > 0 || args.len() > 0 { args.push_str(", "); }
453 if input.name.len() > 0 {
454 args.push_str(format!("{}: ", input.name));
456 args.push_str(format!("{}", input.type_));
458 write!(f.buf, "({args}){arrow, select, yes{ -> {ret}} other{}}",
460 arrow = match d.output { clean::Unit => "no", _ => "yes" },
465 impl fmt::Show for VisSpace {
466 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468 Some(ast::Public) => write!(f.buf, "pub "),
469 Some(ast::Private) => write!(f.buf, "priv "),
470 Some(ast::Inherited) | None => Ok(())
475 impl fmt::Show for PuritySpace {
476 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478 ast::UnsafeFn => write!(f.buf, "unsafe "),
479 ast::ExternFn => write!(f.buf, "extern "),
480 ast::ImpureFn => Ok(())
485 impl fmt::Show for clean::ViewPath {
486 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
488 clean::SimpleImport(ref name, ref src) => {
489 if *name == src.path.segments.last().unwrap().name {
490 write!(f.buf, "use {};", *src)
492 write!(f.buf, "use {} = {};", *name, *src)
495 clean::GlobImport(ref src) => {
496 write!(f.buf, "use {}::*;", *src)
498 clean::ImportList(ref src, ref names) => {
499 try!(write!(f.buf, "use {}::\\{", *src));
500 for (i, n) in names.iter().enumerate() {
502 try!(write!(f.buf, ", "));
504 try!(write!(f.buf, "{}", *n));
506 write!(f.buf, "\\};")
512 impl fmt::Show for clean::ImportSource {
513 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
515 // FIXME: shouldn't be restricted to just local imports
516 Some(did) if ast_util::is_local(did) => {
517 resolved_path(f.buf, did.node, &self.path, true)
520 for (i, seg) in self.path.segments.iter().enumerate() {
522 try!(write!(f.buf, "::"))
524 try!(write!(f.buf, "{}", seg.name));
532 impl fmt::Show for clean::ViewListIdent {
533 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
535 // FIXME: shouldn't be limited to just local imports
536 Some(did) if ast_util::is_local(did) => {
537 let path = clean::Path {
539 segments: ~[clean::PathSegment {
540 name: self.name.clone(),
545 resolved_path(f.buf, did.node, &path, false)
547 _ => write!(f.buf, "{}", self.name),