1 // Copyright 2013-2014 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.
21 use std::strbuf::StrBuf;
28 use html::item_type::ItemType;
30 use html::render::{cache_key, current_location_key};
32 /// Helper to render an optional visibility with a space after it (if the
33 /// visibility is preset)
34 pub struct VisSpace(pub Option<ast::Visibility>);
35 /// Similarly to VisSpace, this structure is used to render a function style with a
37 pub struct FnStyleSpace(pub ast::FnStyle);
38 /// Wrapper struct for properly emitting a method declaration.
39 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
42 pub fn get(&self) -> Option<ast::Visibility> {
43 let VisSpace(v) = *self; v
48 pub fn get(&self) -> ast::FnStyle {
49 let FnStyleSpace(v) = *self; v
53 impl fmt::Show for clean::Generics {
54 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
56 try!(f.buf.write("<".as_bytes()));
58 for (i, life) in self.lifetimes.iter().enumerate() {
60 try!(f.buf.write(", ".as_bytes()));
62 try!(write!(f.buf, "{}", *life));
65 if self.type_params.len() > 0 {
66 if self.lifetimes.len() > 0 {
67 try!(f.buf.write(", ".as_bytes()));
70 for (i, tp) in self.type_params.iter().enumerate() {
72 try!(f.buf.write(", ".as_bytes()))
74 try!(f.buf.write(tp.name.as_bytes()));
76 if tp.bounds.len() > 0 {
77 try!(f.buf.write(": ".as_bytes()));
78 for (i, bound) in tp.bounds.iter().enumerate() {
80 try!(f.buf.write(" + ".as_bytes()));
82 try!(write!(f.buf, "{}", *bound));
87 try!(f.buf.write(">".as_bytes()));
92 impl fmt::Show for clean::Lifetime {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 try!(f.buf.write("'".as_bytes()));
95 try!(f.buf.write(self.get_ref().as_bytes()));
100 impl fmt::Show for clean::TyParamBound {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
103 clean::RegionBound => {
104 f.buf.write("'static".as_bytes())
106 clean::TraitBound(ref ty) => {
107 write!(f.buf, "{}", *ty)
113 impl fmt::Show for clean::Path {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 try!(f.buf.write("::".as_bytes()))
118 for (i, seg) in self.segments.iter().enumerate() {
120 try!(f.buf.write("::".as_bytes()))
122 try!(f.buf.write(seg.name.as_bytes()));
124 if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
125 try!(f.buf.write("<".as_bytes()));
126 let mut comma = false;
127 for lifetime in seg.lifetimes.iter() {
129 try!(f.buf.write(", ".as_bytes()));
132 try!(write!(f.buf, "{}", *lifetime));
134 for ty in seg.types.iter() {
136 try!(f.buf.write(", ".as_bytes()));
139 try!(write!(f.buf, "{}", *ty));
141 try!(f.buf.write(">".as_bytes()));
148 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
149 /// rendering function with the necessary arguments for linking to a local path.
150 fn resolved_path(w: &mut io::Writer, id: ast::NodeId, p: &clean::Path,
151 print_all: bool) -> fmt::Result {
152 path(w, p, print_all,
153 |_cache, loc| { Some("../".repeat(loc.len())) },
155 match cache.paths.find(&id) {
157 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
162 /// Used when rendering an `ExternalPath` structure. Like `resolved_path` this
163 /// will invoke `path` with proper linking-style arguments.
164 fn external_path(w: &mut io::Writer, p: &clean::Path, print_all: bool,
165 fqn: &[~str], kind: clean::TypeKind,
166 krate: ast::CrateNum) -> fmt::Result {
167 path(w, p, print_all,
169 match *cache.extern_locations.get(&krate) {
170 render::Remote(ref s) => Some(s.clone()),
171 render::Local => Some("../".repeat(loc.len())),
172 render::Unknown => None,
176 Some((Vec::from_slice(fqn), match kind {
177 clean::TypeStruct => item_type::Struct,
178 clean::TypeEnum => item_type::Enum,
179 clean::TypeFunction => item_type::Function,
180 clean::TypeTrait => item_type::Trait,
185 fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool,
186 root: |&render::Cache, &[~str]| -> Option<~str>,
187 info: |&render::Cache| -> Option<(Vec<~str> , ItemType)>)
190 // The generics will get written to both the title and link
191 let mut generics = StrBuf::new();
192 let last = path.segments.last().unwrap();
193 if last.lifetimes.len() > 0 || last.types.len() > 0 {
195 generics.push_str("<");
196 for lifetime in last.lifetimes.iter() {
197 if counter > 0 { generics.push_str(", "); }
199 generics.push_str(format!("{}", *lifetime));
201 for ty in last.types.iter() {
202 if counter > 0 { generics.push_str(", "); }
204 generics.push_str(format!("{}", *ty));
206 generics.push_str(">");
209 // Did someone say rightward-drift?
210 local_data::get(current_location_key, |loc| {
211 let loc = loc.unwrap();
213 local_data::get(cache_key, |cache| {
214 let cache = cache.unwrap();
215 let abs_root = root(&**cache, loc.as_slice());
216 let rel_root = match path.segments.get(0).name.as_slice() {
217 "self" => Some(~"./"),
222 let amt = path.segments.len() - 1;
225 let mut root = StrBuf::from_str(root);
226 for seg in path.segments.slice_to(amt).iter() {
227 if "super" == seg.name || "self" == seg.name {
228 try!(write!(w, "{}::", seg.name));
230 root.push_str(seg.name);
232 try!(write!(w, "<a class='mod'
233 href='{}index.html'>{}</a>::",
240 for seg in path.segments.slice_to(amt).iter() {
241 try!(write!(w, "{}::", seg.name));
247 match info(&**cache) {
248 // This is a documented path, link to it!
249 Some((ref fqp, shortty)) if abs_root.is_some() => {
250 let mut url = StrBuf::from_str(abs_root.unwrap());
251 let to_link = fqp.slice_to(fqp.len() - 1);
252 for component in to_link.iter() {
253 url.push_str(*component);
257 item_type::Module => {
258 url.push_str(*fqp.last().unwrap());
259 url.push_str("/index.html");
262 url.push_str(shortty.to_static_str());
264 url.push_str(*fqp.last().unwrap());
265 url.push_str(".html");
269 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
270 shortty, url, fqp.connect("::"), last.name));
274 try!(write!(w, "{}", last.name));
277 try!(write!(w, "{}", generics.as_slice()));
283 /// Helper to render type parameters
284 fn tybounds(w: &mut io::Writer,
285 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
287 Some(ref params) => {
288 try!(write!(w, ":"));
289 for (i, param) in params.iter().enumerate() {
291 try!(write!(w, " + "));
293 try!(write!(w, "{}", *param));
301 impl fmt::Show for clean::Type {
302 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 clean::TyParamBinder(id) | clean::Generic(id) => {
305 local_data::get(cache_key, |cache| {
306 let m = cache.unwrap();
307 f.buf.write(m.typarams.get(&id).as_bytes())
310 clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
311 try!(resolved_path(f.buf, id, path, false));
314 clean::ExternalPath{path: ref path, typarams: ref tp,
315 fqn: ref fqn, kind, krate} => {
316 try!(external_path(f.buf, path, false, fqn.as_slice(), kind,
320 clean::Self(..) => f.buf.write("Self".as_bytes()),
321 clean::Primitive(prim) => {
323 ast::TyInt(ast::TyI) => "int",
324 ast::TyInt(ast::TyI8) => "i8",
325 ast::TyInt(ast::TyI16) => "i16",
326 ast::TyInt(ast::TyI32) => "i32",
327 ast::TyInt(ast::TyI64) => "i64",
328 ast::TyUint(ast::TyU) => "uint",
329 ast::TyUint(ast::TyU8) => "u8",
330 ast::TyUint(ast::TyU16) => "u16",
331 ast::TyUint(ast::TyU32) => "u32",
332 ast::TyUint(ast::TyU64) => "u64",
333 ast::TyFloat(ast::TyF32) => "f32",
334 ast::TyFloat(ast::TyF64) => "f64",
336 ast::TyBool => "bool",
337 ast::TyChar => "char",
339 f.buf.write(s.as_bytes())
341 clean::Closure(ref decl, ref region) => {
342 write!(f.buf, "{style}{lifetimes}|{args}|{bounds}\
343 {arrow, select, yes{ -> {ret}} other{}}",
344 style = FnStyleSpace(decl.fn_style),
345 lifetimes = if decl.lifetimes.len() == 0 {
348 format!("<{:#}>", decl.lifetimes)
350 args = decl.decl.inputs,
351 arrow = match decl.decl.output { clean::Unit => "no", _ => "yes" },
352 ret = decl.decl.output,
354 let mut ret = StrBuf::new();
357 ret.push_str(format!(": {}", *lt));
361 for bound in decl.bounds.iter() {
363 clean::RegionBound => {}
364 clean::TraitBound(ref t) => {
370 ret.push_str(format!("{}", *t));
377 clean::Proc(ref decl) => {
378 write!(f.buf, "{style}{lifetimes}proc({args}){bounds}\
379 {arrow, select, yes{ -> {ret}} other{}}",
380 style = FnStyleSpace(decl.fn_style),
381 lifetimes = if decl.lifetimes.len() == 0 {
384 format!("<{:#}>", decl.lifetimes)
386 args = decl.decl.inputs,
387 bounds = if decl.bounds.len() == 0 {
390 let mut m = decl.bounds.iter().map(|s| s.to_str());
391 ": " + m.collect::<~[~str]>().connect(" + ")
393 arrow = match decl.decl.output { clean::Unit => "no", _ => "yes" },
394 ret = decl.decl.output)
396 clean::BareFunction(ref decl) => {
397 write!(f.buf, "{}{}fn{}{}",
398 FnStyleSpace(decl.fn_style),
400 ref x if "" == *x => ~"",
401 ref x if "\"Rust\"" == *x => ~"",
402 ref s => " " + *s + " ",
407 clean::Tuple(ref typs) => {
408 try!(f.buf.write("(".as_bytes()));
409 for (i, typ) in typs.iter().enumerate() {
411 try!(f.buf.write(", ".as_bytes()))
413 try!(write!(f.buf, "{}", *typ));
415 f.buf.write(")".as_bytes())
417 clean::Vector(ref t) => write!(f.buf, "[{}]", **t),
418 clean::FixedVector(ref t, ref s) => {
419 write!(f.buf, "[{}, ..{}]", **t, *s)
421 clean::String => f.buf.write("str".as_bytes()),
422 clean::Bool => f.buf.write("bool".as_bytes()),
423 clean::Unit => f.buf.write("()".as_bytes()),
424 clean::Bottom => f.buf.write("!".as_bytes()),
425 clean::Unique(ref t) => write!(f.buf, "~{}", **t),
426 clean::Managed(ref t) => write!(f.buf, "@{}", **t),
427 clean::RawPointer(m, ref t) => {
428 write!(f.buf, "*{}{}",
430 clean::Mutable => "mut ",
431 clean::Immutable => "",
434 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
435 let lt = match *l { Some(ref l) => format!("{} ", *l), _ => ~"" };
436 write!(f.buf, "&{}{}{}",
439 clean::Mutable => "mut ",
440 clean::Immutable => "",
448 impl fmt::Show for clean::Arguments {
449 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
450 for (i, input) in self.values.iter().enumerate() {
451 if i > 0 { try!(write!(f.buf, ", ")); }
452 if input.name.len() > 0 {
453 try!(write!(f.buf, "{}: ", input.name));
455 try!(write!(f.buf, "{}", input.type_));
461 impl fmt::Show for clean::FnDecl {
462 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
463 write!(f.buf, "({args}){arrow, select, yes{ -> {ret}} other{}}",
465 arrow = match self.output { clean::Unit => "no", _ => "yes" },
470 impl<'a> fmt::Show for Method<'a> {
471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 let Method(selfty, d) = *self;
473 let mut args = StrBuf::new();
475 clean::SelfStatic => {},
476 clean::SelfValue => args.push_str("self"),
477 clean::SelfOwned => args.push_str("~self"),
478 clean::SelfBorrowed(Some(ref lt), clean::Immutable) => {
479 args.push_str(format!("&{} self", *lt));
481 clean::SelfBorrowed(Some(ref lt), clean::Mutable) => {
482 args.push_str(format!("&{} mut self", *lt));
484 clean::SelfBorrowed(None, clean::Mutable) => {
485 args.push_str("&mut self");
487 clean::SelfBorrowed(None, clean::Immutable) => {
488 args.push_str("&self");
491 for (i, input) in d.inputs.values.iter().enumerate() {
492 if i > 0 || args.len() > 0 { args.push_str(", "); }
493 if input.name.len() > 0 {
494 args.push_str(format!("{}: ", input.name));
496 args.push_str(format!("{}", input.type_));
499 "({args}){arrow, select, yes{ -> {ret}} other{}}",
501 arrow = match d.output { clean::Unit => "no", _ => "yes" },
506 impl fmt::Show for VisSpace {
507 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
509 Some(ast::Public) => write!(f.buf, "pub "),
510 Some(ast::Inherited) | None => Ok(())
515 impl fmt::Show for FnStyleSpace {
516 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518 ast::UnsafeFn => write!(f.buf, "unsafe "),
519 ast::ExternFn => write!(f.buf, "extern "),
520 ast::NormalFn => Ok(())
525 impl fmt::Show for clean::ViewPath {
526 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528 clean::SimpleImport(ref name, ref src) => {
529 if *name == src.path.segments.last().unwrap().name {
530 write!(f.buf, "use {};", *src)
532 write!(f.buf, "use {} = {};", *name, *src)
535 clean::GlobImport(ref src) => {
536 write!(f.buf, "use {}::*;", *src)
538 clean::ImportList(ref src, ref names) => {
539 try!(write!(f.buf, "use {}::\\{", *src));
540 for (i, n) in names.iter().enumerate() {
542 try!(write!(f.buf, ", "));
544 try!(write!(f.buf, "{}", *n));
546 write!(f.buf, "\\};")
552 impl fmt::Show for clean::ImportSource {
553 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
555 // FIXME: shouldn't be restricted to just local imports
556 Some(did) if ast_util::is_local(did) => {
557 resolved_path(f.buf, did.node, &self.path, true)
560 for (i, seg) in self.path.segments.iter().enumerate() {
562 try!(write!(f.buf, "::"))
564 try!(write!(f.buf, "{}", seg.name));
572 impl fmt::Show for clean::ViewListIdent {
573 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
575 // FIXME: shouldn't be limited to just local imports
576 Some(did) if ast_util::is_local(did) => {
577 let path = clean::Path {
579 segments: vec!(clean::PathSegment {
580 name: self.name.clone(),
581 lifetimes: Vec::new(),
585 resolved_path(f.buf, did.node, &path, false)
587 _ => write!(f.buf, "{}", self.name),