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;
25 use stability_summary::ModuleSummary;
27 use html::item_type::ItemType;
29 use html::render::{cache_key, current_location_key};
31 /// Helper to render an optional visibility with a space after it (if the
32 /// visibility is preset)
33 pub struct VisSpace(pub Option<ast::Visibility>);
34 /// Similarly to VisSpace, this structure is used to render a function style with a
36 pub struct FnStyleSpace(pub ast::FnStyle);
37 /// Wrapper struct for properly emitting a method declaration.
38 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
39 /// Similar to VisSpace, but used for mutability
40 pub struct MutableSpace(pub clean::Mutability);
41 /// Similar to VisSpace, but used for mutability
42 pub struct RawMutableSpace(pub clean::Mutability);
43 /// Wrapper struct for properly emitting the stability level.
44 pub struct Stability<'a>(pub &'a Option<clean::Stability>);
45 /// Wrapper struct for emitting the stability level concisely.
46 pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
49 pub fn get(&self) -> Option<ast::Visibility> {
50 let VisSpace(v) = *self; v
55 pub fn get(&self) -> ast::FnStyle {
56 let FnStyleSpace(v) = *self; v
60 impl fmt::Show for clean::Generics {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
63 try!(f.write("<".as_bytes()));
65 for (i, life) in self.lifetimes.iter().enumerate() {
67 try!(f.write(", ".as_bytes()));
69 try!(write!(f, "{}", *life));
72 if self.type_params.len() > 0 {
73 if self.lifetimes.len() > 0 {
74 try!(f.write(", ".as_bytes()));
77 for (i, tp) in self.type_params.iter().enumerate() {
79 try!(f.write(", ".as_bytes()))
81 try!(f.write(tp.name.as_bytes()));
83 if tp.bounds.len() > 0 {
84 try!(f.write(": ".as_bytes()));
85 for (i, bound) in tp.bounds.iter().enumerate() {
87 try!(f.write(" + ".as_bytes()));
89 try!(write!(f, "{}", *bound));
94 Some(ref ty) => { try!(write!(f, " = {}", ty)); },
99 try!(f.write(">".as_bytes()));
104 impl fmt::Show for clean::Lifetime {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 try!(f.write(self.get_ref().as_bytes()));
111 impl fmt::Show for clean::TyParamBound {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 clean::RegionBound => {
115 f.write("'static".as_bytes())
117 clean::TraitBound(ref ty) => {
124 impl fmt::Show for clean::Path {
125 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 try!(f.write("::".as_bytes()))
130 for (i, seg) in self.segments.iter().enumerate() {
132 try!(f.write("::".as_bytes()))
134 try!(f.write(seg.name.as_bytes()));
136 if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
137 try!(f.write("<".as_bytes()));
138 let mut comma = false;
139 for lifetime in seg.lifetimes.iter() {
141 try!(f.write(", ".as_bytes()));
144 try!(write!(f, "{}", *lifetime));
146 for ty in seg.types.iter() {
148 try!(f.write(", ".as_bytes()));
151 try!(write!(f, "{}", *ty));
153 try!(f.write(">".as_bytes()));
160 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
161 /// rendering function with the necessary arguments for linking to a local path.
162 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
163 print_all: bool) -> fmt::Result {
164 path(w, p, print_all,
166 if ast_util::is_local(did) || cache.inlined.contains(&did) {
167 Some(("../".repeat(loc.len())).to_string())
169 match *cache.extern_locations.get(&did.krate) {
170 render::Remote(ref s) => Some(s.to_string()),
172 Some(("../".repeat(loc.len())).to_string())
174 render::Unknown => None,
179 match cache.paths.find(&did) {
181 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
186 fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
187 root: |&render::Cache, &[String]| -> Option<String>,
188 info: |&render::Cache| -> Option<(Vec<String> , ItemType)>)
191 // The generics will get written to both the title and link
192 let mut generics = String::new();
193 let last = path.segments.last().unwrap();
194 if last.lifetimes.len() > 0 || last.types.len() > 0 {
195 let mut counter = 0u;
196 generics.push_str("<");
197 for lifetime in last.lifetimes.iter() {
198 if counter > 0 { generics.push_str(", "); }
200 generics.push_str(format!("{}", *lifetime).as_slice());
202 for ty in last.types.iter() {
203 if counter > 0 { generics.push_str(", "); }
205 generics.push_str(format!("{}", *ty).as_slice());
207 generics.push_str(">");
210 let loc = current_location_key.get().unwrap();
211 let cache = cache_key.get().unwrap();
212 let abs_root = root(&**cache, loc.as_slice());
213 let rel_root = match path.segments.get(0).name.as_slice() {
214 "self" => Some("./".to_string()),
219 let amt = path.segments.len() - 1;
222 let mut root = String::from_str(root.as_slice());
223 for seg in path.segments.slice_to(amt).iter() {
224 if "super" == seg.name.as_slice() ||
225 "self" == seg.name.as_slice() {
226 try!(write!(w, "{}::", seg.name));
228 root.push_str(seg.name.as_slice());
230 try!(write!(w, "<a class='mod'
231 href='{}index.html'>{}</a>::",
238 for seg in path.segments.slice_to(amt).iter() {
239 try!(write!(w, "{}::", seg.name));
245 match info(&**cache) {
246 // This is a documented path, link to it!
247 Some((ref fqp, shortty)) if abs_root.is_some() => {
248 let mut url = String::from_str(abs_root.unwrap().as_slice());
249 let to_link = fqp.slice_to(fqp.len() - 1);
250 for component in to_link.iter() {
251 url.push_str(component.as_slice());
255 item_type::Module => {
256 url.push_str(fqp.last().unwrap().as_slice());
257 url.push_str("/index.html");
260 url.push_str(shortty.to_static_str());
262 url.push_str(fqp.last().unwrap().as_slice());
263 url.push_str(".html");
267 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
268 shortty, url, fqp.connect("::"), last.name));
272 try!(write!(w, "{}", last.name));
275 try!(write!(w, "{}", generics.as_slice()));
279 fn primitive_link(f: &mut fmt::Formatter,
280 prim: clean::Primitive,
281 name: &str) -> fmt::Result {
282 let m = cache_key.get().unwrap();
283 let mut needs_termination = false;
284 match m.primitive_locations.find(&prim) {
285 Some(&ast::LOCAL_CRATE) => {
286 let loc = current_location_key.get().unwrap();
287 let len = if loc.len() == 0 {0} else {loc.len() - 1};
288 try!(write!(f, "<a href='{}primitive.{}.html'>",
291 needs_termination = true;
294 let path = m.paths.get(&ast::DefId {
296 node: ast::CRATE_NODE_ID,
298 let loc = match *m.extern_locations.get(&cnum) {
299 render::Remote(ref s) => Some(s.to_string()),
301 let loc = current_location_key.get().unwrap();
302 Some("../".repeat(loc.len()))
304 render::Unknown => None,
308 try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
310 path.ref0().as_slice().head().unwrap(),
312 needs_termination = true;
319 try!(write!(f, "{}", name));
320 if needs_termination {
321 try!(write!(f, "</a>"));
326 /// Helper to render type parameters
327 fn tybounds(w: &mut fmt::Formatter,
328 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
330 Some(ref params) => {
331 for param in params.iter() {
332 try!(write!(w, " + "));
333 try!(write!(w, "{}", *param));
341 impl fmt::Show for clean::Type {
342 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344 clean::TyParamBinder(id) => {
345 let m = cache_key.get().unwrap();
346 f.write(m.typarams.get(&ast_util::local_def(id)).as_bytes())
348 clean::Generic(did) => {
349 let m = cache_key.get().unwrap();
350 f.write(m.typarams.get(&did).as_bytes())
352 clean::ResolvedPath{ did, ref typarams, ref path } => {
353 try!(resolved_path(f, did, path, false));
354 tybounds(f, typarams)
356 clean::Self(..) => f.write("Self".as_bytes()),
357 clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
358 clean::Closure(ref decl, ref region) => {
359 write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}",
360 style = FnStyleSpace(decl.fn_style),
361 lifetimes = if decl.lifetimes.len() == 0 {
364 format!("<{:#}>", decl.lifetimes)
366 args = decl.decl.inputs,
367 arrow = match decl.decl.output {
368 clean::Primitive(clean::Unit) => "".to_string(),
369 _ => format!(" -> {}", decl.decl.output),
372 let mut ret = String::new();
375 ret.push_str(format!(": {}",
380 for bound in decl.bounds.iter() {
382 clean::RegionBound => {}
383 clean::TraitBound(ref t) => {
389 ret.push_str(format!("{}",
397 clean::Proc(ref decl) => {
398 write!(f, "{style}{lifetimes}proc({args}){bounds}{arrow}",
399 style = FnStyleSpace(decl.fn_style),
400 lifetimes = if decl.lifetimes.len() == 0 {
403 format!("<{:#}>", decl.lifetimes)
405 args = decl.decl.inputs,
406 bounds = if decl.bounds.len() == 0 {
409 let mut m = decl.bounds
411 .map(|s| s.to_string());
414 m.collect::<Vec<String>>().connect(" + "))
416 arrow = match decl.decl.output {
417 clean::Primitive(clean::Unit) => "".to_string(),
418 _ => format!(" -> {}", decl.decl.output)
421 clean::BareFunction(ref decl) => {
422 write!(f, "{}{}fn{}{}",
423 FnStyleSpace(decl.fn_style),
424 match decl.abi.as_slice() {
425 "" => " extern ".to_string(),
426 "\"Rust\"" => "".to_string(),
427 s => format!(" extern {} ", s)
432 clean::Tuple(ref typs) => {
433 primitive_link(f, clean::PrimitiveTuple,
434 match typs.as_slice() {
435 [ref one] => format!("({},)", one),
436 many => format!("({:#})", many)
439 clean::Vector(ref t) => {
440 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
442 clean::FixedVector(ref t, ref s) => {
443 primitive_link(f, clean::Slice,
444 format!("[{}, ..{}]", **t, *s).as_slice())
446 clean::Bottom => f.write("!".as_bytes()),
447 clean::Unique(ref t) => write!(f, "Box<{}>", **t),
448 clean::Managed(ref t) => write!(f, "Gc<{}>", **t),
449 clean::RawPointer(m, ref t) => {
450 write!(f, "*{}{}", RawMutableSpace(m), **t)
452 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
454 Some(ref l) => format!("{} ", *l),
457 write!(f, "&{}{}{}", lt, MutableSpace(mutability), **ty)
463 impl fmt::Show for clean::Arguments {
464 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
465 for (i, input) in self.values.iter().enumerate() {
466 if i > 0 { try!(write!(f, ", ")); }
467 if input.name.len() > 0 {
468 try!(write!(f, "{}: ", input.name));
470 try!(write!(f, "{}", input.type_));
476 impl fmt::Show for clean::FnDecl {
477 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478 write!(f, "({args}){arrow}",
480 arrow = match self.output {
481 clean::Primitive(clean::Unit) => "".to_string(),
482 _ => format!(" -> {}", self.output),
487 impl<'a> fmt::Show for Method<'a> {
488 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
489 let Method(selfty, d) = *self;
490 let mut args = String::new();
492 clean::SelfStatic => {},
493 clean::SelfValue => args.push_str("self"),
494 clean::SelfOwned => args.push_str("~self"),
495 clean::SelfBorrowed(Some(ref lt), mtbl) => {
496 args.push_str(format!("&{} {}self", *lt,
497 MutableSpace(mtbl)).as_slice());
499 clean::SelfBorrowed(None, mtbl) => {
500 args.push_str(format!("&{}self",
501 MutableSpace(mtbl)).as_slice());
504 for (i, input) in d.inputs.values.iter().enumerate() {
505 if i > 0 || args.len() > 0 { args.push_str(", "); }
506 if input.name.len() > 0 {
507 args.push_str(format!("{}: ", input.name).as_slice());
509 args.push_str(format!("{}", input.type_).as_slice());
511 write!(f, "({args}){arrow}",
513 arrow = match d.output {
514 clean::Primitive(clean::Unit) => "".to_string(),
515 _ => format!(" -> {}", d.output),
520 impl fmt::Show for VisSpace {
521 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
523 Some(ast::Public) => write!(f, "pub "),
524 Some(ast::Inherited) | None => Ok(())
529 impl fmt::Show for FnStyleSpace {
530 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
532 ast::UnsafeFn => write!(f, "unsafe "),
533 ast::NormalFn => Ok(())
538 impl fmt::Show for clean::ViewPath {
539 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
541 clean::SimpleImport(ref name, ref src) => {
542 if *name == src.path.segments.last().unwrap().name {
543 write!(f, "use {};", *src)
545 write!(f, "use {} = {};", *name, *src)
548 clean::GlobImport(ref src) => {
549 write!(f, "use {}::*;", *src)
551 clean::ImportList(ref src, ref names) => {
552 try!(write!(f, "use {}::{{", *src));
553 for (i, n) in names.iter().enumerate() {
555 try!(write!(f, ", "));
557 try!(write!(f, "{}", *n));
565 impl fmt::Show for clean::ImportSource {
566 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568 Some(did) => resolved_path(f, did, &self.path, true),
570 for (i, seg) in self.path.segments.iter().enumerate() {
572 try!(write!(f, "::"))
574 try!(write!(f, "{}", seg.name));
582 impl fmt::Show for clean::ViewListIdent {
583 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
586 let path = clean::Path {
588 segments: vec!(clean::PathSegment {
589 name: self.name.clone(),
590 lifetimes: Vec::new(),
594 resolved_path(f, did, &path, false)
596 _ => write!(f, "{}", self.name),
601 impl fmt::Show for MutableSpace {
602 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604 MutableSpace(clean::Immutable) => Ok(()),
605 MutableSpace(clean::Mutable) => write!(f, "mut "),
610 impl fmt::Show for RawMutableSpace {
611 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
613 RawMutableSpace(clean::Immutable) => write!(f, "const "),
614 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
619 impl<'a> fmt::Show for Stability<'a> {
620 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
621 let Stability(stab) = *self;
623 Some(ref stability) => {
624 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
625 lvl = stability.level.to_string(),
626 reason = stability.text)
633 impl<'a> fmt::Show for ConciseStability<'a> {
634 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635 let ConciseStability(stab) = *self;
637 Some(ref stability) => {
638 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
639 lvl = stability.level.to_string(),
640 colon = if stability.text.len() > 0 { ": " } else { "" },
641 reason = stability.text)
644 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
650 impl fmt::Show for ModuleSummary {
651 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652 fn fmt_inner<'a>(f: &mut fmt::Formatter,
653 context: &mut Vec<&'a str>,
654 m: &'a ModuleSummary)
657 let tot = cnt.total();
658 if tot == 0 { return Ok(()) }
660 context.push(m.name.as_slice());
661 let path = context.connect("::");
663 // the total width of each row's stability summary, in pixels
666 try!(write!(f, "<tr>"));
667 try!(write!(f, "<td class='summary'>\
668 <a class='summary' href='{}'>{}</a></td>",
669 Vec::from_slice(context.slice_from(1))
670 .append_one("index.html").connect("/"),
672 try!(write!(f, "<td>"));
673 try!(write!(f, "<span class='summary Stable' \
674 style='width: {}px; display: inline-block'> </span>",
675 (width * cnt.stable)/tot));
676 try!(write!(f, "<span class='summary Unstable' \
677 style='width: {}px; display: inline-block'> </span>",
678 (width * cnt.unstable)/tot));
679 try!(write!(f, "<span class='summary Experimental' \
680 style='width: {}px; display: inline-block'> </span>",
681 (width * cnt.experimental)/tot));
682 try!(write!(f, "<span class='summary Deprecated' \
683 style='width: {}px; display: inline-block'> </span>",
684 (width * cnt.deprecated)/tot));
685 try!(write!(f, "<span class='summary Unmarked' \
686 style='width: {}px; display: inline-block'> </span>",
687 (width * cnt.unmarked)/tot));
688 try!(write!(f, "</td></tr>"));
690 for submodule in m.submodules.iter() {
691 try!(fmt_inner(f, context, submodule));
697 let mut context = Vec::new();
700 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
701 This dashboard summarizes the stability levels for all of the public modules of
702 the crate, according to the total number of items at each level in the module and its children:
704 <a class='stability Stable'></a> stable,<br/>
705 <a class='stability Unstable'></a> unstable,<br/>
706 <a class='stability Experimental'></a> experimental,<br/>
707 <a class='stability Deprecated'></a> deprecated,<br/>
708 <a class='stability Unmarked'></a> unmarked
710 The counts do not include methods or trait
711 implementations that are visible only through a re-exported type.",
713 try!(write!(f, "<table>"))
714 try!(fmt_inner(f, &mut context, self));
715 write!(f, "</table>")