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::String` 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]);
54 /// Wrapper struct for emitting a comma-separated list of items
55 pub struct CommaSep<'a, T: 'a>(pub &'a [T]);
58 pub fn get(&self) -> Option<ast::Visibility> {
59 let VisSpace(v) = *self; v
64 pub fn get(&self) -> ast::Unsafety {
65 let UnsafetySpace(v) = *self; v
69 impl<'a, T: fmt::String> fmt::String for CommaSep<'a, T> {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 for (i, item) in self.0.iter().enumerate() {
72 if i != 0 { try!(write!(f, ", ")); }
73 try!(write!(f, "{}", item));
79 impl<'a> fmt::String for TyParamBounds<'a> {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 let &TyParamBounds(bounds) = self;
82 for (i, bound) in bounds.iter().enumerate() {
84 try!(f.write_str(" + "));
86 try!(write!(f, "{}", *bound));
92 impl fmt::String for clean::Generics {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
95 try!(f.write_str("<"));
97 for (i, life) in self.lifetimes.iter().enumerate() {
99 try!(f.write_str(", "));
101 try!(write!(f, "{}", *life));
104 if self.type_params.len() > 0 {
105 if self.lifetimes.len() > 0 {
106 try!(f.write_str(", "));
108 for (i, tp) in self.type_params.iter().enumerate() {
110 try!(f.write_str(", "))
112 try!(f.write_str(tp.name.as_slice()));
114 if tp.bounds.len() > 0 {
115 try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
119 Some(ref ty) => { try!(write!(f, " = {}", ty)); },
124 try!(f.write_str(">"));
129 impl<'a> fmt::String for WhereClause<'a> {
130 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131 let &WhereClause(gens) = self;
132 if gens.where_predicates.len() == 0 {
135 try!(f.write_str(" <span class='where'>where "));
136 for (i, pred) in gens.where_predicates.iter().enumerate() {
138 try!(f.write_str(", "));
141 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
142 let bounds = bounds.as_slice();
143 try!(write!(f, "{}: {}", ty, TyParamBounds(bounds)));
145 &clean::WherePredicate::RegionPredicate { ref lifetime,
147 try!(write!(f, "{}: ", lifetime));
148 for (i, lifetime) in bounds.iter().enumerate() {
150 try!(f.write_str(" + "));
153 try!(write!(f, "{}", lifetime));
156 &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
157 try!(write!(f, "{} == {}", lhs, rhs));
161 try!(f.write_str("</span>"));
166 impl fmt::String for clean::Lifetime {
167 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
168 try!(f.write_str(self.get_ref()));
173 impl fmt::String for clean::PolyTrait {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 if self.lifetimes.len() > 0 {
176 try!(f.write_str("for<"));
177 for (i, lt) in self.lifetimes.iter().enumerate() {
179 try!(f.write_str(", "));
181 try!(write!(f, "{}", lt));
183 try!(f.write_str("> "));
185 write!(f, "{}", self.trait_)
189 impl fmt::String for clean::TyParamBound {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192 clean::RegionBound(ref lt) => {
195 clean::TraitBound(ref ty, modifier) => {
196 let modifier_str = match modifier {
197 ast::TraitBoundModifier::None => "",
198 ast::TraitBoundModifier::Maybe => "?",
200 write!(f, "{}{}", modifier_str, *ty)
206 impl fmt::String for clean::PathParameters {
207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209 clean::PathParameters::AngleBracketed {
210 ref lifetimes, ref types, ref bindings
212 if lifetimes.len() > 0 || types.len() > 0 || bindings.len() > 0 {
213 try!(f.write_str("<"));
214 let mut comma = false;
215 for lifetime in lifetimes.iter() {
217 try!(f.write_str(", "));
220 try!(write!(f, "{}", *lifetime));
222 for ty in types.iter() {
224 try!(f.write_str(", "));
227 try!(write!(f, "{}", *ty));
229 for binding in bindings.iter() {
231 try!(f.write_str(", "));
234 try!(write!(f, "{}", *binding));
236 try!(f.write_str(">"));
239 clean::PathParameters::Parenthesized { ref inputs, ref output } => {
240 try!(f.write_str("("));
241 let mut comma = false;
242 for ty in inputs.iter() {
244 try!(f.write_str(", "));
247 try!(write!(f, "{}", *ty));
249 try!(f.write_str(")"));
250 if let Some(ref ty) = *output {
251 try!(f.write_str(" -> "));
252 try!(write!(f, "{}", ty));
260 impl fmt::String for clean::PathSegment {
261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262 try!(f.write_str(self.name.as_slice()));
263 write!(f, "{}", self.params)
267 impl fmt::String for clean::Path {
268 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270 try!(f.write_str("::"))
273 for (i, seg) in self.segments.iter().enumerate() {
275 try!(f.write_str("::"))
277 try!(write!(f, "{}", seg));
283 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
284 /// rendering function with the necessary arguments for linking to a local path.
285 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
286 print_all: bool) -> fmt::Result {
287 path(w, p, print_all,
289 if ast_util::is_local(did) || cache.inlined.contains(&did) {
290 Some(repeat("../").take(loc.len()).collect::<String>())
292 match cache.extern_locations[did.krate] {
293 render::Remote(ref s) => Some(s.to_string()),
295 Some(repeat("../").take(loc.len()).collect::<String>())
297 render::Unknown => None,
302 match cache.paths.get(&did) {
304 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
309 fn path<F, G>(w: &mut fmt::Formatter,
315 F: FnOnce(&render::Cache, &[String]) -> Option<String>,
316 G: FnOnce(&render::Cache) -> Option<(Vec<String>, ItemType)>,
318 // The generics will get written to both the title and link
319 let last = path.segments.last().unwrap();
320 let generics = format!("{}", last.params);
322 let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
324 let abs_root = root(&*cache, loc.as_slice());
325 let rel_root = match path.segments[0].name.as_slice() {
326 "self" => Some("./".to_string()),
331 let amt = path.segments.len() - 1;
334 let mut root = String::from_str(root.as_slice());
335 for seg in path.segments[..amt].iter() {
336 if "super" == seg.name ||
338 try!(write!(w, "{}::", seg.name));
340 root.push_str(seg.name.as_slice());
342 try!(write!(w, "<a class='mod'
343 href='{}index.html'>{}</a>::",
350 for seg in path.segments[..amt].iter() {
351 try!(write!(w, "{}::", seg.name));
357 match info(&*cache) {
358 // This is a documented path, link to it!
359 Some((ref fqp, shortty)) if abs_root.is_some() => {
360 let mut url = String::from_str(abs_root.unwrap().as_slice());
361 let to_link = &fqp[..fqp.len() - 1];
362 for component in to_link.iter() {
363 url.push_str(component.as_slice());
367 ItemType::Module => {
368 url.push_str(fqp.last().unwrap().as_slice());
369 url.push_str("/index.html");
372 url.push_str(shortty.to_static_str());
374 url.push_str(fqp.last().unwrap().as_slice());
375 url.push_str(".html");
379 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
380 shortty, url, fqp.connect("::"), last.name));
384 try!(write!(w, "{}", last.name));
387 try!(write!(w, "{}", generics.as_slice()));
391 fn primitive_link(f: &mut fmt::Formatter,
392 prim: clean::PrimitiveType,
393 name: &str) -> fmt::Result {
395 let mut needs_termination = false;
396 match m.primitive_locations.get(&prim) {
397 Some(&ast::LOCAL_CRATE) => {
398 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
399 let len = if len == 0 {0} else {len - 1};
400 try!(write!(f, "<a href='{}primitive.{}.html'>",
401 repeat("../").take(len).collect::<String>(),
403 needs_termination = true;
406 let path = &m.paths[ast::DefId {
408 node: ast::CRATE_NODE_ID,
410 let loc = match m.extern_locations[cnum] {
411 render::Remote(ref s) => Some(s.to_string()),
413 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
414 Some(repeat("../").take(len).collect::<String>())
416 render::Unknown => None,
420 try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
422 path.0.first().unwrap(),
424 needs_termination = true;
431 try!(write!(f, "{}", name));
432 if needs_termination {
433 try!(write!(f, "</a>"));
438 /// Helper to render type parameters
439 fn tybounds(w: &mut fmt::Formatter,
440 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
442 Some(ref params) => {
443 for param in params.iter() {
444 try!(write!(w, " + "));
445 try!(write!(w, "{}", *param));
453 impl fmt::String for clean::Type {
454 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456 clean::TyParamBinder(id) => {
457 f.write_str(cache().typarams[ast_util::local_def(id)].as_slice())
459 clean::Generic(ref name) => {
460 f.write_str(name.as_slice())
462 clean::ResolvedPath{ did, ref typarams, ref path } => {
463 try!(resolved_path(f, did, path, false));
464 tybounds(f, typarams)
466 clean::Infer => write!(f, "_"),
467 clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
468 clean::BareFunction(ref decl) => {
469 write!(f, "{}{}fn{}{}",
470 UnsafetySpace(decl.unsafety),
471 match decl.abi.as_slice() {
472 "" => " extern ".to_string(),
473 "\"Rust\"" => "".to_string(),
474 s => format!(" extern {} ", s)
479 clean::Tuple(ref typs) => {
480 primitive_link(f, clean::PrimitiveTuple,
481 match typs.as_slice() {
482 [ref one] => format!("({},)", one),
483 many => format!("({})",
484 CommaSep(many.as_slice()))
487 clean::Vector(ref t) => {
488 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
490 clean::FixedVector(ref t, ref s) => {
491 primitive_link(f, clean::Slice,
492 format!("[{}; {}]", **t, *s).as_slice())
494 clean::Bottom => f.write_str("!"),
495 clean::RawPointer(m, ref t) => {
496 write!(f, "*{}{}", RawMutableSpace(m), **t)
498 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
500 Some(ref l) => format!("{} ", *l),
503 let m = MutableSpace(mutability);
505 clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
508 primitive_link(f, clean::Slice,
509 format!("&{}{}[{}]", lt, m, **bt).as_slice()),
511 try!(primitive_link(f, clean::Slice,
512 format!("&{}{}[", lt, m).as_slice()));
513 try!(write!(f, "{}", **bt));
514 primitive_link(f, clean::Slice, "]")
519 write!(f, "&{}{}{}", lt, m, **ty)
523 clean::PolyTraitRef(ref bounds) => {
524 for (i, bound) in bounds.iter().enumerate() {
526 try!(write!(f, " + "));
528 try!(write!(f, "{}", *bound));
532 clean::QPath { ref name, ref self_type, ref trait_ } => {
533 write!(f, "<{} as {}>::{}", self_type, trait_, name)
535 clean::Unique(..) => {
536 panic!("should have been cleaned")
542 impl fmt::String for clean::Arguments {
543 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544 for (i, input) in self.values.iter().enumerate() {
545 if i > 0 { try!(write!(f, ", ")); }
546 if input.name.len() > 0 {
547 try!(write!(f, "{}: ", input.name));
549 try!(write!(f, "{}", input.type_));
555 impl fmt::String for clean::FunctionRetTy {
556 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558 clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
559 clean::Return(ref ty) => write!(f, " -> {}", ty),
560 clean::DefaultReturn => Ok(()),
561 clean::NoReturn => write!(f, " -> !")
566 impl fmt::String for clean::FnDecl {
567 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
568 write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
572 impl<'a> fmt::String for Method<'a> {
573 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
574 let Method(selfty, d) = *self;
575 let mut args = String::new();
577 clean::SelfStatic => {},
578 clean::SelfValue => args.push_str("self"),
579 clean::SelfBorrowed(Some(ref lt), mtbl) => {
580 args.push_str(format!("&{} {}self", *lt,
581 MutableSpace(mtbl)).as_slice());
583 clean::SelfBorrowed(None, mtbl) => {
584 args.push_str(format!("&{}self",
585 MutableSpace(mtbl)).as_slice());
587 clean::SelfExplicit(ref typ) => {
588 args.push_str(format!("self: {}", *typ).as_slice());
591 for (i, input) in d.inputs.values.iter().enumerate() {
592 if i > 0 || args.len() > 0 { args.push_str(", "); }
593 if input.name.len() > 0 {
594 args.push_str(format!("{}: ", input.name).as_slice());
596 args.push_str(format!("{}", input.type_).as_slice());
598 write!(f, "({args}){arrow}", args = args, arrow = d.output)
602 impl fmt::String for VisSpace {
603 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605 Some(ast::Public) => write!(f, "pub "),
606 Some(ast::Inherited) | None => Ok(())
611 impl fmt::String for UnsafetySpace {
612 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
614 ast::Unsafety::Unsafe => write!(f, "unsafe "),
615 ast::Unsafety::Normal => Ok(())
620 impl fmt::String for clean::ViewPath {
621 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
623 clean::SimpleImport(ref name, ref src) => {
624 if *name == src.path.segments.last().unwrap().name {
625 write!(f, "use {};", *src)
627 write!(f, "use {} as {};", *src, *name)
630 clean::GlobImport(ref src) => {
631 write!(f, "use {}::*;", *src)
633 clean::ImportList(ref src, ref names) => {
634 try!(write!(f, "use {}::{{", *src));
635 for (i, n) in names.iter().enumerate() {
637 try!(write!(f, ", "));
639 try!(write!(f, "{}", *n));
647 impl fmt::String for clean::ImportSource {
648 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
650 Some(did) => resolved_path(f, did, &self.path, true),
652 for (i, seg) in self.path.segments.iter().enumerate() {
654 try!(write!(f, "::"))
656 try!(write!(f, "{}", seg.name));
664 impl fmt::String for clean::ViewListIdent {
665 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
668 let path = clean::Path {
670 segments: vec!(clean::PathSegment {
671 name: self.name.clone(),
672 params: clean::PathParameters::AngleBracketed {
673 lifetimes: Vec::new(),
679 resolved_path(f, did, &path, false)
681 _ => write!(f, "{}", self.name),
686 impl fmt::String for clean::TypeBinding {
687 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
688 write!(f, "{}={}", self.name, self.ty)
692 impl fmt::String for MutableSpace {
693 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
695 MutableSpace(clean::Immutable) => Ok(()),
696 MutableSpace(clean::Mutable) => write!(f, "mut "),
701 impl fmt::String for RawMutableSpace {
702 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
704 RawMutableSpace(clean::Immutable) => write!(f, "const "),
705 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
710 impl<'a> fmt::String for Stability<'a> {
711 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
712 let Stability(stab) = *self;
714 Some(ref stability) => {
715 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
716 lvl = stability.level,
717 reason = stability.text)
724 impl<'a> fmt::String for ConciseStability<'a> {
725 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
726 let ConciseStability(stab) = *self;
728 Some(ref stability) => {
729 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
730 lvl = stability.level,
731 colon = if stability.text.len() > 0 { ": " } else { "" },
732 reason = stability.text)
735 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
741 impl fmt::String for ModuleSummary {
742 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743 fn fmt_inner<'a>(f: &mut fmt::Formatter,
744 context: &mut Vec<&'a str>,
745 m: &'a ModuleSummary)
748 let tot = cnt.total();
749 if tot == 0 { return Ok(()) }
751 context.push(m.name.as_slice());
752 let path = context.connect("::");
754 try!(write!(f, "<tr>"));
755 try!(write!(f, "<td><a href='{}'>{}</a></td>", {
756 let mut url = context.slice_from(1).to_vec();
757 url.push("index.html");
761 try!(write!(f, "<td class='summary-column'>"));
762 try!(write!(f, "<span class='summary Stable' \
763 style='width: {:.4}%; display: inline-block'> </span>",
764 (100 * cnt.stable) as f64/tot as f64));
765 try!(write!(f, "<span class='summary Unstable' \
766 style='width: {:.4}%; display: inline-block'> </span>",
767 (100 * cnt.unstable) as f64/tot as f64));
768 try!(write!(f, "<span class='summary Experimental' \
769 style='width: {:.4}%; display: inline-block'> </span>",
770 (100 * cnt.experimental) as f64/tot as f64));
771 try!(write!(f, "<span class='summary Deprecated' \
772 style='width: {:.4}%; display: inline-block'> </span>",
773 (100 * cnt.deprecated) as f64/tot as f64));
774 try!(write!(f, "<span class='summary Unmarked' \
775 style='width: {:.4}%; display: inline-block'> </span>",
776 (100 * cnt.unmarked) as f64/tot as f64));
777 try!(write!(f, "</td></tr>"));
779 for submodule in m.submodules.iter() {
780 try!(fmt_inner(f, context, submodule));
786 let mut context = Vec::new();
788 let tot = self.counts.total();
789 let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
792 ((100 * self.counts.stable)/tot,
793 (100 * self.counts.unstable)/tot,
794 (100 * self.counts.experimental)/tot,
795 (100 * self.counts.deprecated)/tot,
796 (100 * self.counts.unmarked)/tot)
800 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
801 This dashboard summarizes the stability levels for all of the public modules of
802 the crate, according to the total number of items at each level in the module and
803 its children (percentages total for {name}):
805 <a class='stability Stable'></a> stable ({}%),<br/>
806 <a class='stability Unstable'></a> unstable ({}%),<br/>
807 <a class='stability Experimental'></a> experimental ({}%),<br/>
808 <a class='stability Deprecated'></a> deprecated ({}%),<br/>
809 <a class='stability Unmarked'></a> unmarked ({}%)
811 The counts do not include methods or trait
812 implementations that are visible only through a re-exported type.",
813 stable, unstable, experimental, deprecated, unmarked,
815 try!(write!(f, "<table>"));
816 try!(fmt_inner(f, &mut context, self));
817 write!(f, "</table>")