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;
26 use html::item_type::ItemType;
28 use html::render::{cache, 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);
38 /// Similar to VisSpace, but used for mutability
39 pub struct MutableSpace(pub clean::Mutability);
40 /// Similar to VisSpace, but used for mutability
41 pub struct RawMutableSpace(pub clean::Mutability);
42 /// Wrapper struct for properly emitting the stability level.
43 pub struct Stability<'a>(pub &'a Option<clean::Stability>);
44 /// Wrapper struct for emitting the stability level concisely.
45 pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
46 /// Wrapper struct for emitting a where clause from Generics.
47 pub struct WhereClause<'a>(pub &'a clean::Generics);
48 /// Wrapper struct for emitting type parameter bounds.
49 pub struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]);
52 pub fn get(&self) -> Option<ast::Visibility> {
53 let VisSpace(v) = *self; v
58 pub fn get(&self) -> ast::FnStyle {
59 let FnStyleSpace(v) = *self; v
63 impl<'a> fmt::Show for TyParamBounds<'a> {
64 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65 let &TyParamBounds(bounds) = self;
66 for (i, bound) in bounds.iter().enumerate() {
68 try!(f.write(" + ".as_bytes()));
70 try!(write!(f, "{}", *bound));
76 impl fmt::Show for clean::Generics {
77 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
79 try!(f.write("<".as_bytes()));
81 for (i, life) in self.lifetimes.iter().enumerate() {
83 try!(f.write(", ".as_bytes()));
85 try!(write!(f, "{}", *life));
88 if self.type_params.len() > 0 {
89 if self.lifetimes.len() > 0 {
90 try!(f.write(", ".as_bytes()));
92 for (i, tp) in self.type_params.iter().enumerate() {
94 try!(f.write(", ".as_bytes()))
96 if let Some(ref unbound) = tp.default_unbound {
97 try!(write!(f, "{}? ", unbound));
99 try!(f.write(tp.name.as_bytes()));
101 if tp.bounds.len() > 0 {
102 try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
106 Some(ref ty) => { try!(write!(f, " = {}", ty)); },
111 try!(f.write(">".as_bytes()));
116 impl<'a> fmt::Show for WhereClause<'a> {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 let &WhereClause(gens) = self;
119 if gens.where_predicates.len() == 0 {
122 try!(f.write(" where ".as_bytes()));
123 for (i, pred) in gens.where_predicates.iter().enumerate() {
125 try!(f.write(", ".as_bytes()));
127 let bounds = pred.bounds.as_slice();
128 try!(write!(f, "{}: {}", pred.name, TyParamBounds(bounds)));
134 impl fmt::Show for clean::Lifetime {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 try!(f.write(self.get_ref().as_bytes()));
141 impl fmt::Show for clean::TyParamBound {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 clean::RegionBound(ref lt) => {
147 clean::TraitBound(ref ty) => {
154 impl fmt::Show for clean::Path {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 try!(f.write("::".as_bytes()))
160 for (i, seg) in self.segments.iter().enumerate() {
162 try!(f.write("::".as_bytes()))
164 try!(f.write(seg.name.as_bytes()));
166 if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
167 try!(f.write("<".as_bytes()));
168 let mut comma = false;
169 for lifetime in seg.lifetimes.iter() {
171 try!(f.write(", ".as_bytes()));
174 try!(write!(f, "{}", *lifetime));
176 for ty in seg.types.iter() {
178 try!(f.write(", ".as_bytes()));
181 try!(write!(f, "{}", *ty));
183 try!(f.write(">".as_bytes()));
190 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
191 /// rendering function with the necessary arguments for linking to a local path.
192 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
193 print_all: bool) -> fmt::Result {
194 path(w, p, print_all,
196 if ast_util::is_local(did) || cache.inlined.contains(&did) {
197 Some(("../".repeat(loc.len())).to_string())
199 match cache.extern_locations[did.krate] {
200 render::Remote(ref s) => Some(s.to_string()),
202 Some(("../".repeat(loc.len())).to_string())
204 render::Unknown => None,
209 match cache.paths.get(&did) {
211 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
216 fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
217 root: |&render::Cache, &[String]| -> Option<String>,
218 info: |&render::Cache| -> Option<(Vec<String> , ItemType)>)
221 // The generics will get written to both the title and link
222 let mut generics = String::new();
223 let last = path.segments.last().unwrap();
224 if last.lifetimes.len() > 0 || last.types.len() > 0 {
225 let mut counter = 0u;
226 generics.push_str("<");
227 for lifetime in last.lifetimes.iter() {
228 if counter > 0 { generics.push_str(", "); }
230 generics.push_str(format!("{}", *lifetime).as_slice());
232 for ty in last.types.iter() {
233 if counter > 0 { generics.push_str(", "); }
235 generics.push_str(format!("{}", *ty).as_slice());
237 generics.push_str(">");
240 let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
242 let abs_root = root(&*cache, loc.as_slice());
243 let rel_root = match path.segments[0].name.as_slice() {
244 "self" => Some("./".to_string()),
249 let amt = path.segments.len() - 1;
252 let mut root = String::from_str(root.as_slice());
253 for seg in path.segments[..amt].iter() {
254 if "super" == seg.name.as_slice() ||
255 "self" == seg.name.as_slice() {
256 try!(write!(w, "{}::", seg.name));
258 root.push_str(seg.name.as_slice());
260 try!(write!(w, "<a class='mod'
261 href='{}index.html'>{}</a>::",
268 for seg in path.segments[..amt].iter() {
269 try!(write!(w, "{}::", seg.name));
275 match info(&*cache) {
276 // This is a documented path, link to it!
277 Some((ref fqp, shortty)) if abs_root.is_some() => {
278 let mut url = String::from_str(abs_root.unwrap().as_slice());
279 let to_link = fqp[..fqp.len() - 1];
280 for component in to_link.iter() {
281 url.push_str(component.as_slice());
285 ItemType::Module => {
286 url.push_str(fqp.last().unwrap().as_slice());
287 url.push_str("/index.html");
290 url.push_str(shortty.to_static_str());
292 url.push_str(fqp.last().unwrap().as_slice());
293 url.push_str(".html");
297 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
298 shortty, url, fqp.connect("::"), last.name));
302 try!(write!(w, "{}", last.name));
305 try!(write!(w, "{}", generics.as_slice()));
309 fn primitive_link(f: &mut fmt::Formatter,
310 prim: clean::PrimitiveType,
311 name: &str) -> fmt::Result {
313 let mut needs_termination = false;
314 match m.primitive_locations.get(&prim) {
315 Some(&ast::LOCAL_CRATE) => {
316 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
317 let len = if len == 0 {0} else {len - 1};
318 try!(write!(f, "<a href='{}primitive.{}.html'>",
321 needs_termination = true;
324 let path = &m.paths[ast::DefId {
326 node: ast::CRATE_NODE_ID,
328 let loc = match m.extern_locations[cnum] {
329 render::Remote(ref s) => Some(s.to_string()),
331 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
332 Some("../".repeat(len))
334 render::Unknown => None,
338 try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
340 path.ref0().as_slice().head().unwrap(),
342 needs_termination = true;
349 try!(write!(f, "{}", name));
350 if needs_termination {
351 try!(write!(f, "</a>"));
356 /// Helper to render type parameters
357 fn tybounds(w: &mut fmt::Formatter,
358 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
360 Some(ref params) => {
361 for param in params.iter() {
362 try!(write!(w, " + "));
363 try!(write!(w, "{}", *param));
371 impl fmt::Show for clean::Type {
372 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374 clean::TyParamBinder(id) => {
375 f.write(cache().typarams[ast_util::local_def(id)].as_bytes())
377 clean::Generic(did) => {
378 f.write(cache().typarams[did].as_bytes())
380 clean::ResolvedPath{ did, ref typarams, ref path } => {
381 try!(resolved_path(f, did, path, false));
382 tybounds(f, typarams)
384 clean::Self(..) => f.write("Self".as_bytes()),
385 clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
386 clean::Closure(ref decl) => {
387 write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}",
388 style = FnStyleSpace(decl.fn_style),
389 lifetimes = if decl.lifetimes.len() == 0 {
392 format!("<{:#}>", decl.lifetimes)
394 args = decl.decl.inputs,
395 arrow = decl.decl.output,
397 let mut ret = String::new();
398 for bound in decl.bounds.iter() {
400 clean::RegionBound(..) => {}
401 clean::TraitBound(ref t) => {
407 ret.push_str(format!("{}",
415 clean::Proc(ref decl) => {
416 write!(f, "{style}{lifetimes}proc({args}){bounds}{arrow}",
417 style = FnStyleSpace(decl.fn_style),
418 lifetimes = if decl.lifetimes.len() == 0 {
421 format!("<{:#}>", decl.lifetimes)
423 args = decl.decl.inputs,
424 bounds = if decl.bounds.len() == 0 {
429 .map(|s| s.to_string());
432 m.collect::<Vec<String>>().connect(" + "))
434 arrow = decl.decl.output)
436 clean::BareFunction(ref decl) => {
437 write!(f, "{}{}fn{}{}",
438 FnStyleSpace(decl.fn_style),
439 match decl.abi.as_slice() {
440 "" => " extern ".to_string(),
441 "\"Rust\"" => "".to_string(),
442 s => format!(" extern {} ", s)
447 clean::Tuple(ref typs) => {
448 primitive_link(f, clean::PrimitiveTuple,
449 match typs.as_slice() {
450 [ref one] => format!("({},)", one),
451 many => format!("({:#})", many)
454 clean::Vector(ref t) => {
455 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
457 clean::FixedVector(ref t, ref s) => {
458 primitive_link(f, clean::Slice,
459 format!("[{}, ..{}]", **t, *s).as_slice())
461 clean::Bottom => f.write("!".as_bytes()),
462 clean::RawPointer(m, ref t) => {
463 write!(f, "*{}{}", RawMutableSpace(m), **t)
465 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
467 Some(ref l) => format!("{} ", *l),
470 let m = MutableSpace(mutability);
472 clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
475 primitive_link(f, clean::Slice,
476 format!("&{}{}[{}]", lt, m, **bt).as_slice()),
478 try!(primitive_link(f, clean::Slice,
479 format!("&{}{}[", lt, m).as_slice()));
480 try!(write!(f, "{}", **bt));
481 primitive_link(f, clean::Slice, "]")
486 write!(f, "&{}{}{}", lt, m, **ty)
490 clean::QPath { ref name, ref self_type, ref trait_ } => {
491 write!(f, "<{} as {}>::{}", self_type, trait_, name)
493 clean::Unique(..) => {
494 panic!("should have been cleaned")
500 impl fmt::Show for clean::Arguments {
501 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
502 for (i, input) in self.values.iter().enumerate() {
503 if i > 0 { try!(write!(f, ", ")); }
504 if input.name.len() > 0 {
505 try!(write!(f, "{}: ", input.name));
507 try!(write!(f, "{}", input.type_));
513 impl fmt::Show for clean::FunctionRetTy {
514 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
516 clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
517 clean::Return(ref ty) => write!(f, " -> {}", ty),
518 clean::NoReturn => write!(f, " -> !")
523 impl fmt::Show for clean::FnDecl {
524 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
525 write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
529 impl<'a> fmt::Show for Method<'a> {
530 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
531 let Method(selfty, d) = *self;
532 let mut args = String::new();
534 clean::SelfStatic => {},
535 clean::SelfValue => args.push_str("self"),
536 clean::SelfBorrowed(Some(ref lt), mtbl) => {
537 args.push_str(format!("&{} {}self", *lt,
538 MutableSpace(mtbl)).as_slice());
540 clean::SelfBorrowed(None, mtbl) => {
541 args.push_str(format!("&{}self",
542 MutableSpace(mtbl)).as_slice());
544 clean::SelfExplicit(ref typ) => {
545 args.push_str(format!("self: {}", *typ).as_slice());
548 for (i, input) in d.inputs.values.iter().enumerate() {
549 if i > 0 || args.len() > 0 { args.push_str(", "); }
550 if input.name.len() > 0 {
551 args.push_str(format!("{}: ", input.name).as_slice());
553 args.push_str(format!("{}", input.type_).as_slice());
555 write!(f, "({args}){arrow}", args = args, arrow = d.output)
559 impl fmt::Show for VisSpace {
560 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
562 Some(ast::Public) => write!(f, "pub "),
563 Some(ast::Inherited) | None => Ok(())
568 impl fmt::Show for FnStyleSpace {
569 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
571 ast::UnsafeFn => write!(f, "unsafe "),
572 ast::NormalFn => Ok(())
577 impl fmt::Show for clean::ViewPath {
578 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
580 clean::SimpleImport(ref name, ref src) => {
581 if *name == src.path.segments.last().unwrap().name {
582 write!(f, "use {};", *src)
584 write!(f, "use {} as {};", *src, *name)
587 clean::GlobImport(ref src) => {
588 write!(f, "use {}::*;", *src)
590 clean::ImportList(ref src, ref names) => {
591 try!(write!(f, "use {}::{{", *src));
592 for (i, n) in names.iter().enumerate() {
594 try!(write!(f, ", "));
596 try!(write!(f, "{}", *n));
604 impl fmt::Show for clean::ImportSource {
605 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
607 Some(did) => resolved_path(f, did, &self.path, true),
609 for (i, seg) in self.path.segments.iter().enumerate() {
611 try!(write!(f, "::"))
613 try!(write!(f, "{}", seg.name));
621 impl fmt::Show for clean::ViewListIdent {
622 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
625 let path = clean::Path {
627 segments: vec!(clean::PathSegment {
628 name: self.name.clone(),
629 lifetimes: Vec::new(),
633 resolved_path(f, did, &path, false)
635 _ => write!(f, "{}", self.name),
640 impl fmt::Show for MutableSpace {
641 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643 MutableSpace(clean::Immutable) => Ok(()),
644 MutableSpace(clean::Mutable) => write!(f, "mut "),
649 impl fmt::Show for RawMutableSpace {
650 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652 RawMutableSpace(clean::Immutable) => write!(f, "const "),
653 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
658 impl<'a> fmt::Show for Stability<'a> {
659 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
660 let Stability(stab) = *self;
662 Some(ref stability) => {
663 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
664 lvl = stability.level.to_string(),
665 reason = stability.text)
672 impl<'a> fmt::Show for ConciseStability<'a> {
673 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
674 let ConciseStability(stab) = *self;
676 Some(ref stability) => {
677 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
678 lvl = stability.level.to_string(),
679 colon = if stability.text.len() > 0 { ": " } else { "" },
680 reason = stability.text)
683 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
689 impl fmt::Show for ModuleSummary {
690 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
691 fn fmt_inner<'a>(f: &mut fmt::Formatter,
692 context: &mut Vec<&'a str>,
693 m: &'a ModuleSummary)
696 let tot = cnt.total();
697 if tot == 0 { return Ok(()) }
699 context.push(m.name.as_slice());
700 let path = context.connect("::");
702 try!(write!(f, "<tr>"));
703 try!(write!(f, "<td><a href='{}'>{}</a></td>", {
704 let mut url = context.slice_from(1).to_vec();
705 url.push("index.html");
709 try!(write!(f, "<td class='summary-column'>"));
710 try!(write!(f, "<span class='summary Stable' \
711 style='width: {:.4}%; display: inline-block'> </span>",
712 (100 * cnt.stable) as f64/tot as f64));
713 try!(write!(f, "<span class='summary Unstable' \
714 style='width: {:.4}%; display: inline-block'> </span>",
715 (100 * cnt.unstable) as f64/tot as f64));
716 try!(write!(f, "<span class='summary Experimental' \
717 style='width: {:.4}%; display: inline-block'> </span>",
718 (100 * cnt.experimental) as f64/tot as f64));
719 try!(write!(f, "<span class='summary Deprecated' \
720 style='width: {:.4}%; display: inline-block'> </span>",
721 (100 * cnt.deprecated) as f64/tot as f64));
722 try!(write!(f, "<span class='summary Unmarked' \
723 style='width: {:.4}%; display: inline-block'> </span>",
724 (100 * cnt.unmarked) as f64/tot as f64));
725 try!(write!(f, "</td></tr>"));
727 for submodule in m.submodules.iter() {
728 try!(fmt_inner(f, context, submodule));
734 let mut context = Vec::new();
736 let tot = self.counts.total();
737 let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
740 ((100 * self.counts.stable)/tot,
741 (100 * self.counts.unstable)/tot,
742 (100 * self.counts.experimental)/tot,
743 (100 * self.counts.deprecated)/tot,
744 (100 * self.counts.unmarked)/tot)
748 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
749 This dashboard summarizes the stability levels for all of the public modules of
750 the crate, according to the total number of items at each level in the module and
751 its children (percentages total for {name}):
753 <a class='stability Stable'></a> stable ({}%),<br/>
754 <a class='stability Unstable'></a> unstable ({}%),<br/>
755 <a class='stability Experimental'></a> experimental ({}%),<br/>
756 <a class='stability Deprecated'></a> deprecated ({}%),<br/>
757 <a class='stability Unmarked'></a> unmarked ({}%)
759 The counts do not include methods or trait
760 implementations that are visible only through a re-exported type.",
761 stable, unstable, experimental, deprecated, unmarked,
763 try!(write!(f, "<table>"))
764 try!(fmt_inner(f, &mut context, self));
765 write!(f, "</table>")