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::iter::repeat;
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)
33 pub struct VisSpace(pub Option<ast::Visibility>);
34 /// Similarly to VisSpace, this structure is used to render a function style with a
37 pub struct UnsafetySpace(pub ast::Unsafety);
38 /// Wrapper struct for properly emitting a method declaration.
39 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
40 /// Similar to VisSpace, but used for mutability
42 pub struct MutableSpace(pub clean::Mutability);
43 /// Similar to VisSpace, but used for mutability
45 pub struct RawMutableSpace(pub clean::Mutability);
46 /// Wrapper struct for properly emitting the stability level.
47 pub struct Stability<'a>(pub &'a Option<clean::Stability>);
48 /// Wrapper struct for emitting the stability level concisely.
49 pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
50 /// Wrapper struct for emitting a where clause from Generics.
51 pub struct WhereClause<'a>(pub &'a clean::Generics);
52 /// Wrapper struct for emitting type parameter bounds.
53 pub struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]);
56 pub fn get(&self) -> Option<ast::Visibility> {
57 let VisSpace(v) = *self; v
62 pub fn get(&self) -> ast::Unsafety {
63 let UnsafetySpace(v) = *self; v
67 impl<'a> fmt::Show for TyParamBounds<'a> {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 let &TyParamBounds(bounds) = self;
70 for (i, bound) in bounds.iter().enumerate() {
72 try!(f.write_str(" + "));
74 try!(write!(f, "{}", *bound));
80 impl fmt::Show for clean::Generics {
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
83 try!(f.write_str("<"));
85 for (i, life) in self.lifetimes.iter().enumerate() {
87 try!(f.write_str(", "));
89 try!(write!(f, "{}", *life));
92 if self.type_params.len() > 0 {
93 if self.lifetimes.len() > 0 {
94 try!(f.write_str(", "));
96 for (i, tp) in self.type_params.iter().enumerate() {
98 try!(f.write_str(", "))
100 try!(f.write_str(tp.name[]));
102 if tp.bounds.len() > 0 {
103 try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
107 Some(ref ty) => { try!(write!(f, " = {}", ty)); },
112 try!(f.write_str(">"));
117 impl<'a> fmt::Show for WhereClause<'a> {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 let &WhereClause(gens) = self;
120 if gens.where_predicates.len() == 0 {
123 try!(f.write_str(" <span class='where'>where "));
124 for (i, pred) in gens.where_predicates.iter().enumerate() {
126 try!(f.write_str(", "));
129 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
130 let bounds = bounds.as_slice();
131 try!(write!(f, "{}: {}", ty, TyParamBounds(bounds)));
133 &clean::WherePredicate::RegionPredicate { ref lifetime,
135 try!(write!(f, "{}: ", lifetime));
136 for (i, lifetime) in bounds.iter().enumerate() {
138 try!(f.write_str(" + "));
141 try!(write!(f, "{}", lifetime));
144 &clean::WherePredicate::EqPredicate => {
149 try!(f.write_str("</span>"));
154 impl fmt::Show for clean::Lifetime {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 try!(f.write_str(self.get_ref()));
161 impl fmt::Show for clean::PolyTrait {
162 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
163 if self.lifetimes.len() > 0 {
164 try!(f.write_str("for<"));
165 for (i, lt) in self.lifetimes.iter().enumerate() {
167 try!(f.write_str(", "));
169 try!(write!(f, "{}", lt));
171 try!(f.write_str("> "));
173 write!(f, "{}", self.trait_)
177 impl fmt::Show for clean::TyParamBound {
178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180 clean::RegionBound(ref lt) => {
183 clean::TraitBound(ref ty, modifier) => {
184 let modifier_str = match modifier {
185 ast::TraitBoundModifier::None => "",
186 ast::TraitBoundModifier::Maybe => "?",
188 write!(f, "{}{}", modifier_str, *ty)
194 impl fmt::Show for clean::PathParameters {
195 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 clean::PathParameters::AngleBracketed { ref lifetimes, ref types } => {
198 if lifetimes.len() > 0 || types.len() > 0 {
199 try!(f.write_str("<"));
200 let mut comma = false;
201 for lifetime in lifetimes.iter() {
203 try!(f.write_str(", "));
206 try!(write!(f, "{}", *lifetime));
208 for ty in types.iter() {
210 try!(f.write_str(", "));
213 try!(write!(f, "{}", *ty));
215 try!(f.write_str(">"));
218 clean::PathParameters::Parenthesized { ref inputs, ref output } => {
219 try!(f.write_str("("));
220 let mut comma = false;
221 for ty in inputs.iter() {
223 try!(f.write_str(", "));
226 try!(write!(f, "{}", *ty));
228 try!(f.write_str(")"));
229 if let Some(ref ty) = *output {
230 try!(f.write_str(" -> "));
231 try!(write!(f, "{}", ty));
239 impl fmt::Show for clean::PathSegment {
240 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
241 try!(f.write_str(self.name.as_slice()));
242 write!(f, "{}", self.params)
246 impl fmt::Show for clean::Path {
247 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249 try!(f.write_str("::"))
252 for (i, seg) in self.segments.iter().enumerate() {
254 try!(f.write_str("::"))
256 try!(write!(f, "{}", seg));
262 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
263 /// rendering function with the necessary arguments for linking to a local path.
264 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
265 print_all: bool) -> fmt::Result {
266 path(w, p, print_all,
268 if ast_util::is_local(did) || cache.inlined.contains(&did) {
269 Some(repeat("../").take(loc.len()).collect::<String>())
271 match cache.extern_locations[did.krate] {
272 render::Remote(ref s) => Some(s.to_string()),
274 Some(repeat("../").take(loc.len()).collect::<String>())
276 render::Unknown => None,
281 match cache.paths.get(&did) {
283 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
288 fn path<F, G>(w: &mut fmt::Formatter,
294 F: FnOnce(&render::Cache, &[String]) -> Option<String>,
295 G: FnOnce(&render::Cache) -> Option<(Vec<String>, ItemType)>,
297 // The generics will get written to both the title and link
298 let last = path.segments.last().unwrap();
299 let generics = format!("{}", last.params);
301 let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
303 let abs_root = root(&*cache, loc.as_slice());
304 let rel_root = match path.segments[0].name.as_slice() {
305 "self" => Some("./".to_string()),
310 let amt = path.segments.len() - 1;
313 let mut root = String::from_str(root.as_slice());
314 for seg in path.segments[..amt].iter() {
315 if "super" == seg.name ||
317 try!(write!(w, "{}::", seg.name));
319 root.push_str(seg.name.as_slice());
321 try!(write!(w, "<a class='mod'
322 href='{}index.html'>{}</a>::",
329 for seg in path.segments[..amt].iter() {
330 try!(write!(w, "{}::", seg.name));
336 match info(&*cache) {
337 // This is a documented path, link to it!
338 Some((ref fqp, shortty)) if abs_root.is_some() => {
339 let mut url = String::from_str(abs_root.unwrap().as_slice());
340 let to_link = fqp[..fqp.len() - 1];
341 for component in to_link.iter() {
342 url.push_str(component.as_slice());
346 ItemType::Module => {
347 url.push_str(fqp.last().unwrap().as_slice());
348 url.push_str("/index.html");
351 url.push_str(shortty.to_static_str());
353 url.push_str(fqp.last().unwrap().as_slice());
354 url.push_str(".html");
358 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
359 shortty, url, fqp.connect("::"), last.name));
363 try!(write!(w, "{}", last.name));
366 try!(write!(w, "{}", generics.as_slice()));
370 fn primitive_link(f: &mut fmt::Formatter,
371 prim: clean::PrimitiveType,
372 name: &str) -> fmt::Result {
374 let mut needs_termination = false;
375 match m.primitive_locations.get(&prim) {
376 Some(&ast::LOCAL_CRATE) => {
377 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
378 let len = if len == 0 {0} else {len - 1};
379 try!(write!(f, "<a href='{}primitive.{}.html'>",
380 repeat("../").take(len).collect::<String>(),
382 needs_termination = true;
385 let path = &m.paths[ast::DefId {
387 node: ast::CRATE_NODE_ID,
389 let loc = match m.extern_locations[cnum] {
390 render::Remote(ref s) => Some(s.to_string()),
392 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
393 Some(repeat("../").take(len).collect::<String>())
395 render::Unknown => None,
399 try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
401 path.0.first().unwrap(),
403 needs_termination = true;
410 try!(write!(f, "{}", name));
411 if needs_termination {
412 try!(write!(f, "</a>"));
417 /// Helper to render type parameters
418 fn tybounds(w: &mut fmt::Formatter,
419 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
421 Some(ref params) => {
422 for param in params.iter() {
423 try!(write!(w, " + "));
424 try!(write!(w, "{}", *param));
432 impl fmt::Show for clean::Type {
433 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
435 clean::TyParamBinder(id) => {
436 f.write_str(cache().typarams[ast_util::local_def(id)][])
438 clean::Generic(ref name) => {
439 f.write_str(name.as_slice())
441 clean::ResolvedPath{ did, ref typarams, ref path } => {
442 try!(resolved_path(f, did, path, false));
443 tybounds(f, typarams)
445 clean::Infer => write!(f, "_"),
446 clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
447 clean::Closure(ref decl) => {
448 write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}",
449 style = UnsafetySpace(decl.unsafety),
450 lifetimes = if decl.lifetimes.len() == 0 {
453 format!("for <{:#}>", decl.lifetimes)
455 args = decl.decl.inputs,
456 arrow = decl.decl.output,
458 let mut ret = String::new();
459 for bound in decl.bounds.iter() {
461 clean::RegionBound(..) => {}
462 clean::TraitBound(ref t, modifier) => {
468 if modifier == ast::TraitBoundModifier::Maybe {
471 ret.push_str(format!("{}",
479 clean::Proc(ref decl) => {
480 write!(f, "{style}{lifetimes}proc({args}){bounds}{arrow}",
481 style = UnsafetySpace(decl.unsafety),
482 lifetimes = if decl.lifetimes.len() == 0 {
485 format!("for <{:#}>", decl.lifetimes)
487 args = decl.decl.inputs,
488 bounds = if decl.bounds.len() == 0 {
493 .map(|s| s.to_string());
496 m.collect::<Vec<String>>().connect(" + "))
498 arrow = decl.decl.output)
500 clean::BareFunction(ref decl) => {
501 write!(f, "{}{}fn{}{}",
502 UnsafetySpace(decl.unsafety),
503 match decl.abi.as_slice() {
504 "" => " extern ".to_string(),
505 "\"Rust\"" => "".to_string(),
506 s => format!(" extern {} ", s)
511 clean::Tuple(ref typs) => {
512 primitive_link(f, clean::PrimitiveTuple,
513 match typs.as_slice() {
514 [ref one] => format!("({},)", one),
515 many => format!("({:#})", many)
518 clean::Vector(ref t) => {
519 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
521 clean::FixedVector(ref t, ref s) => {
522 primitive_link(f, clean::Slice,
523 format!("[{}, ..{}]", **t, *s).as_slice())
525 clean::Bottom => f.write_str("!"),
526 clean::RawPointer(m, ref t) => {
527 write!(f, "*{}{}", RawMutableSpace(m), **t)
529 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
531 Some(ref l) => format!("{} ", *l),
534 let m = MutableSpace(mutability);
536 clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
539 primitive_link(f, clean::Slice,
540 format!("&{}{}[{}]", lt, m, **bt).as_slice()),
542 try!(primitive_link(f, clean::Slice,
543 format!("&{}{}[", lt, m).as_slice()));
544 try!(write!(f, "{}", **bt));
545 primitive_link(f, clean::Slice, "]")
550 write!(f, "&{}{}{}", lt, m, **ty)
554 clean::PolyTraitRef(ref bounds) => {
555 for (i, bound) in bounds.iter().enumerate() {
557 try!(write!(f, " + "));
559 try!(write!(f, "{}", *bound));
563 clean::QPath { ref name, ref self_type, ref trait_ } => {
564 write!(f, "<{} as {}>::{}", self_type, trait_, name)
566 clean::Unique(..) => {
567 panic!("should have been cleaned")
573 impl fmt::Show for clean::Arguments {
574 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
575 for (i, input) in self.values.iter().enumerate() {
576 if i > 0 { try!(write!(f, ", ")); }
577 if input.name.len() > 0 {
578 try!(write!(f, "{}: ", input.name));
580 try!(write!(f, "{}", input.type_));
586 impl fmt::Show for clean::FunctionRetTy {
587 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
589 clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
590 clean::Return(ref ty) => write!(f, " -> {}", ty),
591 clean::NoReturn => write!(f, " -> !")
596 impl fmt::Show for clean::FnDecl {
597 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
598 write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
602 impl<'a> fmt::Show for Method<'a> {
603 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604 let Method(selfty, d) = *self;
605 let mut args = String::new();
607 clean::SelfStatic => {},
608 clean::SelfValue => args.push_str("self"),
609 clean::SelfBorrowed(Some(ref lt), mtbl) => {
610 args.push_str(format!("&{} {}self", *lt,
611 MutableSpace(mtbl)).as_slice());
613 clean::SelfBorrowed(None, mtbl) => {
614 args.push_str(format!("&{}self",
615 MutableSpace(mtbl)).as_slice());
617 clean::SelfExplicit(ref typ) => {
618 args.push_str(format!("self: {}", *typ).as_slice());
621 for (i, input) in d.inputs.values.iter().enumerate() {
622 if i > 0 || args.len() > 0 { args.push_str(", "); }
623 if input.name.len() > 0 {
624 args.push_str(format!("{}: ", input.name).as_slice());
626 args.push_str(format!("{}", input.type_).as_slice());
628 write!(f, "({args}){arrow}", args = args, arrow = d.output)
632 impl fmt::Show for VisSpace {
633 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635 Some(ast::Public) => write!(f, "pub "),
636 Some(ast::Inherited) | None => Ok(())
641 impl fmt::Show for UnsafetySpace {
642 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
644 ast::Unsafety::Unsafe => write!(f, "unsafe "),
645 ast::Unsafety::Normal => Ok(())
650 impl fmt::Show for clean::ViewPath {
651 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
653 clean::SimpleImport(ref name, ref src) => {
654 if *name == src.path.segments.last().unwrap().name {
655 write!(f, "use {};", *src)
657 write!(f, "use {} as {};", *src, *name)
660 clean::GlobImport(ref src) => {
661 write!(f, "use {}::*;", *src)
663 clean::ImportList(ref src, ref names) => {
664 try!(write!(f, "use {}::{{", *src));
665 for (i, n) in names.iter().enumerate() {
667 try!(write!(f, ", "));
669 try!(write!(f, "{}", *n));
677 impl fmt::Show for clean::ImportSource {
678 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
680 Some(did) => resolved_path(f, did, &self.path, true),
682 for (i, seg) in self.path.segments.iter().enumerate() {
684 try!(write!(f, "::"))
686 try!(write!(f, "{}", seg.name));
694 impl fmt::Show for clean::ViewListIdent {
695 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
698 let path = clean::Path {
700 segments: vec!(clean::PathSegment {
701 name: self.name.clone(),
702 params: clean::PathParameters::AngleBracketed {
703 lifetimes: Vec::new(),
708 resolved_path(f, did, &path, false)
710 _ => write!(f, "{}", self.name),
715 impl fmt::Show for MutableSpace {
716 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
718 MutableSpace(clean::Immutable) => Ok(()),
719 MutableSpace(clean::Mutable) => write!(f, "mut "),
724 impl fmt::Show for RawMutableSpace {
725 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
727 RawMutableSpace(clean::Immutable) => write!(f, "const "),
728 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
733 impl<'a> fmt::Show for Stability<'a> {
734 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
735 let Stability(stab) = *self;
737 Some(ref stability) => {
738 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
739 lvl = stability.level.to_string(),
740 reason = stability.text)
747 impl<'a> fmt::Show for ConciseStability<'a> {
748 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
749 let ConciseStability(stab) = *self;
751 Some(ref stability) => {
752 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
753 lvl = stability.level.to_string(),
754 colon = if stability.text.len() > 0 { ": " } else { "" },
755 reason = stability.text)
758 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
764 impl fmt::Show for ModuleSummary {
765 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
766 fn fmt_inner<'a>(f: &mut fmt::Formatter,
767 context: &mut Vec<&'a str>,
768 m: &'a ModuleSummary)
771 let tot = cnt.total();
772 if tot == 0 { return Ok(()) }
774 context.push(m.name.as_slice());
775 let path = context.connect("::");
777 try!(write!(f, "<tr>"));
778 try!(write!(f, "<td><a href='{}'>{}</a></td>", {
779 let mut url = context.slice_from(1).to_vec();
780 url.push("index.html");
784 try!(write!(f, "<td class='summary-column'>"));
785 try!(write!(f, "<span class='summary Stable' \
786 style='width: {:.4}%; display: inline-block'> </span>",
787 (100 * cnt.stable) as f64/tot as f64));
788 try!(write!(f, "<span class='summary Unstable' \
789 style='width: {:.4}%; display: inline-block'> </span>",
790 (100 * cnt.unstable) as f64/tot as f64));
791 try!(write!(f, "<span class='summary Experimental' \
792 style='width: {:.4}%; display: inline-block'> </span>",
793 (100 * cnt.experimental) as f64/tot as f64));
794 try!(write!(f, "<span class='summary Deprecated' \
795 style='width: {:.4}%; display: inline-block'> </span>",
796 (100 * cnt.deprecated) as f64/tot as f64));
797 try!(write!(f, "<span class='summary Unmarked' \
798 style='width: {:.4}%; display: inline-block'> </span>",
799 (100 * cnt.unmarked) as f64/tot as f64));
800 try!(write!(f, "</td></tr>"));
802 for submodule in m.submodules.iter() {
803 try!(fmt_inner(f, context, submodule));
809 let mut context = Vec::new();
811 let tot = self.counts.total();
812 let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
815 ((100 * self.counts.stable)/tot,
816 (100 * self.counts.unstable)/tot,
817 (100 * self.counts.experimental)/tot,
818 (100 * self.counts.deprecated)/tot,
819 (100 * self.counts.unmarked)/tot)
823 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
824 This dashboard summarizes the stability levels for all of the public modules of
825 the crate, according to the total number of items at each level in the module and
826 its children (percentages total for {name}):
828 <a class='stability Stable'></a> stable ({}%),<br/>
829 <a class='stability Unstable'></a> unstable ({}%),<br/>
830 <a class='stability Experimental'></a> experimental ({}%),<br/>
831 <a class='stability Deprecated'></a> deprecated ({}%),<br/>
832 <a class='stability Unmarked'></a> unmarked ({}%)
834 The counts do not include methods or trait
835 implementations that are visible only through a re-exported type.",
836 stable, unstable, experimental, deprecated, unmarked,
838 try!(write!(f, "<table>"));
839 try!(fmt_inner(f, &mut context, self));
840 write!(f, "</table>")