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.
19 use std::string::String;
26 use html::item_type::ItemType;
28 use html::render::{cache_key, current_location_key};
30 /// Helper to render an optional visibility with a space after it (if the
31 /// visibility is preset)
32 pub struct VisSpace(pub Option<ast::Visibility>);
33 /// Similarly to VisSpace, this structure is used to render a function style with a
35 pub struct FnStyleSpace(pub ast::FnStyle);
36 /// Wrapper struct for properly emitting a method declaration.
37 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
40 pub fn get(&self) -> Option<ast::Visibility> {
41 let VisSpace(v) = *self; v
46 pub fn get(&self) -> ast::FnStyle {
47 let FnStyleSpace(v) = *self; v
51 impl fmt::Show for clean::Generics {
52 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
54 try!(f.write("<".as_bytes()));
56 for (i, life) in self.lifetimes.iter().enumerate() {
58 try!(f.write(", ".as_bytes()));
60 try!(write!(f, "{}", *life));
63 if self.type_params.len() > 0 {
64 if self.lifetimes.len() > 0 {
65 try!(f.write(", ".as_bytes()));
68 for (i, tp) in self.type_params.iter().enumerate() {
70 try!(f.write(", ".as_bytes()))
72 try!(f.write(tp.name.as_bytes()));
74 if tp.bounds.len() > 0 {
75 try!(f.write(": ".as_bytes()));
76 for (i, bound) in tp.bounds.iter().enumerate() {
78 try!(f.write(" + ".as_bytes()));
80 try!(write!(f, "{}", *bound));
85 try!(f.write(">".as_bytes()));
90 impl fmt::Show for clean::Lifetime {
91 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92 try!(f.write("'".as_bytes()));
93 try!(f.write(self.get_ref().as_bytes()));
98 impl fmt::Show for clean::TyParamBound {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 clean::RegionBound => {
102 f.write("'static".as_bytes())
104 clean::TraitBound(ref ty) => {
111 impl fmt::Show for clean::Path {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 try!(f.write("::".as_bytes()))
117 for (i, seg) in self.segments.iter().enumerate() {
119 try!(f.write("::".as_bytes()))
121 try!(f.write(seg.name.as_bytes()));
123 if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
124 try!(f.write("<".as_bytes()));
125 let mut comma = false;
126 for lifetime in seg.lifetimes.iter() {
128 try!(f.write(", ".as_bytes()));
131 try!(write!(f, "{}", *lifetime));
133 for ty in seg.types.iter() {
135 try!(f.write(", ".as_bytes()));
138 try!(write!(f, "{}", *ty));
140 try!(f.write(">".as_bytes()));
147 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
148 /// rendering function with the necessary arguments for linking to a local path.
149 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
150 print_all: bool) -> fmt::Result {
151 path(w, p, print_all,
153 if ast_util::is_local(did) || cache.inlined.contains(&did) {
154 Some(("../".repeat(loc.len())).to_string())
156 match *cache.extern_locations.get(&did.krate) {
157 render::Remote(ref s) => Some(s.to_string()),
159 Some(("../".repeat(loc.len())).to_string())
161 render::Unknown => None,
166 match cache.paths.find(&did) {
168 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
173 fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
174 root: |&render::Cache, &[String]| -> Option<String>,
175 info: |&render::Cache| -> Option<(Vec<String> , ItemType)>)
178 // The generics will get written to both the title and link
179 let mut generics = String::new();
180 let last = path.segments.last().unwrap();
181 if last.lifetimes.len() > 0 || last.types.len() > 0 {
183 generics.push_str("<");
184 for lifetime in last.lifetimes.iter() {
185 if counter > 0 { generics.push_str(", "); }
187 generics.push_str(format!("{}", *lifetime).as_slice());
189 for ty in last.types.iter() {
190 if counter > 0 { generics.push_str(", "); }
192 generics.push_str(format!("{}", *ty).as_slice());
194 generics.push_str(">");
197 let loc = current_location_key.get().unwrap();
198 let cache = cache_key.get().unwrap();
199 let abs_root = root(&**cache, loc.as_slice());
200 let rel_root = match path.segments.get(0).name.as_slice() {
201 "self" => Some("./".to_string()),
206 let amt = path.segments.len() - 1;
209 let mut root = String::from_str(root.as_slice());
210 for seg in path.segments.slice_to(amt).iter() {
211 if "super" == seg.name.as_slice() ||
212 "self" == seg.name.as_slice() {
213 try!(write!(w, "{}::", seg.name));
215 root.push_str(seg.name.as_slice());
217 try!(write!(w, "<a class='mod'
218 href='{}index.html'>{}</a>::",
225 for seg in path.segments.slice_to(amt).iter() {
226 try!(write!(w, "{}::", seg.name));
232 match info(&**cache) {
233 // This is a documented path, link to it!
234 Some((ref fqp, shortty)) if abs_root.is_some() => {
235 let mut url = String::from_str(abs_root.unwrap().as_slice());
236 let to_link = fqp.slice_to(fqp.len() - 1);
237 for component in to_link.iter() {
238 url.push_str(component.as_slice());
242 item_type::Module => {
243 url.push_str(fqp.last().unwrap().as_slice());
244 url.push_str("/index.html");
247 url.push_str(shortty.to_static_str());
249 url.push_str(fqp.last().unwrap().as_slice());
250 url.push_str(".html");
254 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
255 shortty, url, fqp.connect("::"), last.name));
259 try!(write!(w, "{}", last.name));
262 try!(write!(w, "{}", generics.as_slice()));
266 fn primitive_link(f: &mut fmt::Formatter,
267 prim: clean::Primitive,
268 name: &str) -> fmt::Result {
269 let m = cache_key.get().unwrap();
270 let mut needs_termination = false;
271 match m.primitive_locations.find(&prim) {
272 Some(&ast::LOCAL_CRATE) => {
273 let loc = current_location_key.get().unwrap();
274 let len = if loc.len() == 0 {0} else {loc.len() - 1};
275 try!(write!(f, "<a href='{}primitive.{}.html'>",
278 needs_termination = true;
281 let path = m.paths.get(&ast::DefId {
283 node: ast::CRATE_NODE_ID,
285 let loc = match *m.extern_locations.get(&cnum) {
286 render::Remote(ref s) => Some(s.to_string()),
288 let loc = current_location_key.get().unwrap();
289 Some("../".repeat(loc.len()))
291 render::Unknown => None,
295 try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
297 path.ref0().as_slice().head().unwrap(),
299 needs_termination = true;
306 try!(write!(f, "{}", name));
307 if needs_termination {
308 try!(write!(f, "</a>"));
313 /// Helper to render type parameters
314 fn tybounds(w: &mut fmt::Formatter,
315 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
317 Some(ref params) => {
318 try!(write!(w, ":"));
319 for (i, param) in params.iter().enumerate() {
321 try!(write!(w, " + "));
323 try!(write!(w, "{}", *param));
331 impl fmt::Show for clean::Type {
332 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334 clean::TyParamBinder(id) => {
335 let m = cache_key.get().unwrap();
336 f.write(m.typarams.get(&ast_util::local_def(id)).as_bytes())
338 clean::Generic(did) => {
339 let m = cache_key.get().unwrap();
340 f.write(m.typarams.get(&did).as_bytes())
342 clean::ResolvedPath{ did, ref typarams, ref path } => {
343 try!(resolved_path(f, did, path, false));
344 tybounds(f, typarams)
346 clean::Self(..) => f.write("Self".as_bytes()),
347 clean::Primitive(prim) => primitive_link(f, prim, prim.to_str()),
348 clean::Closure(ref decl, ref region) => {
349 write!(f, "{style}{lifetimes}|{args}|{bounds}\
350 {arrow, select, yes{ -> {ret}} other{}}",
351 style = FnStyleSpace(decl.fn_style),
352 lifetimes = if decl.lifetimes.len() == 0 {
355 format!("<{:#}>", decl.lifetimes)
357 args = decl.decl.inputs,
358 arrow = match decl.decl.output {
359 clean::Primitive(clean::Nil) => "no",
362 ret = decl.decl.output,
364 let mut ret = String::new();
367 ret.push_str(format!(": {}",
372 for bound in decl.bounds.iter() {
374 clean::RegionBound => {}
375 clean::TraitBound(ref t) => {
381 ret.push_str(format!("{}",
389 clean::Proc(ref decl) => {
390 write!(f, "{style}{lifetimes}proc({args}){bounds}\
391 {arrow, select, yes{ -> {ret}} other{}}",
392 style = FnStyleSpace(decl.fn_style),
393 lifetimes = if decl.lifetimes.len() == 0 {
396 format!("<{:#}>", decl.lifetimes)
398 args = decl.decl.inputs,
399 bounds = if decl.bounds.len() == 0 {
402 let mut m = decl.bounds
404 .map(|s| s.to_str().to_string());
407 m.collect::<Vec<String>>().connect(" + "))
409 arrow = match decl.decl.output {
410 clean::Primitive(clean::Nil) => "no",
413 ret = decl.decl.output)
415 clean::BareFunction(ref decl) => {
416 write!(f, "{}{}fn{}{}",
417 FnStyleSpace(decl.fn_style),
418 match decl.abi.as_slice() {
419 "" => " extern ".to_string(),
420 "\"Rust\"" => "".to_string(),
421 s => format!(" extern {} ", s)
426 clean::Tuple(ref typs) => {
427 try!(f.write("(".as_bytes()));
428 for (i, typ) in typs.iter().enumerate() {
430 try!(f.write(", ".as_bytes()))
432 try!(write!(f, "{}", *typ));
434 f.write(")".as_bytes())
436 clean::Vector(ref t) => {
437 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
439 clean::FixedVector(ref t, ref s) => {
440 primitive_link(f, clean::Slice,
441 format!("[{}, ..{}]", **t, *s).as_slice())
443 clean::Bottom => f.write("!".as_bytes()),
444 clean::Unique(ref t) => write!(f, "~{}", **t),
445 clean::Managed(ref t) => write!(f, "@{}", **t),
446 clean::RawPointer(m, ref t) => {
449 clean::Mutable => "mut ",
450 clean::Immutable => "",
453 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
455 Some(ref l) => format!("{} ", *l),
458 write!(f, "&{}{}{}",
461 clean::Mutable => "mut ",
462 clean::Immutable => "",
470 impl fmt::Show for clean::Arguments {
471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 for (i, input) in self.values.iter().enumerate() {
473 if i > 0 { try!(write!(f, ", ")); }
474 if input.name.len() > 0 {
475 try!(write!(f, "{}: ", input.name));
477 try!(write!(f, "{}", input.type_));
483 impl fmt::Show for clean::FnDecl {
484 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
485 write!(f, "({args}){arrow, select, yes{ -> {ret}} other{}}",
487 arrow = match self.output {
488 clean::Primitive(clean::Nil) => "no",
495 impl<'a> fmt::Show for Method<'a> {
496 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497 let Method(selfty, d) = *self;
498 let mut args = String::new();
500 clean::SelfStatic => {},
501 clean::SelfValue => args.push_str("self"),
502 clean::SelfOwned => args.push_str("~self"),
503 clean::SelfBorrowed(Some(ref lt), clean::Immutable) => {
504 args.push_str(format!("&{} self", *lt).as_slice());
506 clean::SelfBorrowed(Some(ref lt), clean::Mutable) => {
507 args.push_str(format!("&{} mut self", *lt).as_slice());
509 clean::SelfBorrowed(None, clean::Mutable) => {
510 args.push_str("&mut self");
512 clean::SelfBorrowed(None, clean::Immutable) => {
513 args.push_str("&self");
516 for (i, input) in d.inputs.values.iter().enumerate() {
517 if i > 0 || args.len() > 0 { args.push_str(", "); }
518 if input.name.len() > 0 {
519 args.push_str(format!("{}: ", input.name).as_slice());
521 args.push_str(format!("{}", input.type_).as_slice());
524 "({args}){arrow, select, yes{ -> {ret}} other{}}",
526 arrow = match d.output {
527 clean::Primitive(clean::Nil) => "no",
534 impl fmt::Show for VisSpace {
535 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537 Some(ast::Public) => write!(f, "pub "),
538 Some(ast::Inherited) | None => Ok(())
543 impl fmt::Show for FnStyleSpace {
544 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
546 ast::UnsafeFn => write!(f, "unsafe "),
547 ast::NormalFn => Ok(())
552 impl fmt::Show for clean::ViewPath {
553 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
555 clean::SimpleImport(ref name, ref src) => {
556 if *name == src.path.segments.last().unwrap().name {
557 write!(f, "use {};", *src)
559 write!(f, "use {} = {};", *name, *src)
562 clean::GlobImport(ref src) => {
563 write!(f, "use {}::*;", *src)
565 clean::ImportList(ref src, ref names) => {
566 try!(write!(f, "use {}::\\{", *src));
567 for (i, n) in names.iter().enumerate() {
569 try!(write!(f, ", "));
571 try!(write!(f, "{}", *n));
579 impl fmt::Show for clean::ImportSource {
580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
582 Some(did) => resolved_path(f, did, &self.path, true),
584 for (i, seg) in self.path.segments.iter().enumerate() {
586 try!(write!(f, "::"))
588 try!(write!(f, "{}", seg.name));
596 impl fmt::Show for clean::ViewListIdent {
597 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
600 let path = clean::Path {
602 segments: vec!(clean::PathSegment {
603 name: self.name.clone(),
604 lifetimes: Vec::new(),
608 resolved_path(f, did, &path, false)
610 _ => write!(f, "{}", self.name),