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::NoReturn => write!(f, " -> !")
565 impl fmt::String for clean::FnDecl {
566 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567 write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
571 impl<'a> fmt::String for Method<'a> {
572 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
573 let Method(selfty, d) = *self;
574 let mut args = String::new();
576 clean::SelfStatic => {},
577 clean::SelfValue => args.push_str("self"),
578 clean::SelfBorrowed(Some(ref lt), mtbl) => {
579 args.push_str(format!("&{} {}self", *lt,
580 MutableSpace(mtbl)).as_slice());
582 clean::SelfBorrowed(None, mtbl) => {
583 args.push_str(format!("&{}self",
584 MutableSpace(mtbl)).as_slice());
586 clean::SelfExplicit(ref typ) => {
587 args.push_str(format!("self: {}", *typ).as_slice());
590 for (i, input) in d.inputs.values.iter().enumerate() {
591 if i > 0 || args.len() > 0 { args.push_str(", "); }
592 if input.name.len() > 0 {
593 args.push_str(format!("{}: ", input.name).as_slice());
595 args.push_str(format!("{}", input.type_).as_slice());
597 write!(f, "({args}){arrow}", args = args, arrow = d.output)
601 impl fmt::String for VisSpace {
602 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604 Some(ast::Public) => write!(f, "pub "),
605 Some(ast::Inherited) | None => Ok(())
610 impl fmt::String for UnsafetySpace {
611 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
613 ast::Unsafety::Unsafe => write!(f, "unsafe "),
614 ast::Unsafety::Normal => Ok(())
619 impl fmt::String for clean::ViewPath {
620 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
622 clean::SimpleImport(ref name, ref src) => {
623 if *name == src.path.segments.last().unwrap().name {
624 write!(f, "use {};", *src)
626 write!(f, "use {} as {};", *src, *name)
629 clean::GlobImport(ref src) => {
630 write!(f, "use {}::*;", *src)
632 clean::ImportList(ref src, ref names) => {
633 try!(write!(f, "use {}::{{", *src));
634 for (i, n) in names.iter().enumerate() {
636 try!(write!(f, ", "));
638 try!(write!(f, "{}", *n));
646 impl fmt::String for clean::ImportSource {
647 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
649 Some(did) => resolved_path(f, did, &self.path, true),
651 for (i, seg) in self.path.segments.iter().enumerate() {
653 try!(write!(f, "::"))
655 try!(write!(f, "{}", seg.name));
663 impl fmt::String for clean::ViewListIdent {
664 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
667 let path = clean::Path {
669 segments: vec!(clean::PathSegment {
670 name: self.name.clone(),
671 params: clean::PathParameters::AngleBracketed {
672 lifetimes: Vec::new(),
678 resolved_path(f, did, &path, false)
680 _ => write!(f, "{}", self.name),
685 impl fmt::String for clean::TypeBinding {
686 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
687 write!(f, "{}={}", self.name, self.ty)
691 impl fmt::String for MutableSpace {
692 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
694 MutableSpace(clean::Immutable) => Ok(()),
695 MutableSpace(clean::Mutable) => write!(f, "mut "),
700 impl fmt::String for RawMutableSpace {
701 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
703 RawMutableSpace(clean::Immutable) => write!(f, "const "),
704 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
709 impl<'a> fmt::String for Stability<'a> {
710 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
711 let Stability(stab) = *self;
713 Some(ref stability) => {
714 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
715 lvl = stability.level,
716 reason = stability.text)
723 impl<'a> fmt::String for ConciseStability<'a> {
724 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
725 let ConciseStability(stab) = *self;
727 Some(ref stability) => {
728 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
729 lvl = stability.level,
730 colon = if stability.text.len() > 0 { ": " } else { "" },
731 reason = stability.text)
734 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
740 impl fmt::String for ModuleSummary {
741 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
742 fn fmt_inner<'a>(f: &mut fmt::Formatter,
743 context: &mut Vec<&'a str>,
744 m: &'a ModuleSummary)
747 let tot = cnt.total();
748 if tot == 0 { return Ok(()) }
750 context.push(m.name.as_slice());
751 let path = context.connect("::");
753 try!(write!(f, "<tr>"));
754 try!(write!(f, "<td><a href='{}'>{}</a></td>", {
755 let mut url = context.slice_from(1).to_vec();
756 url.push("index.html");
760 try!(write!(f, "<td class='summary-column'>"));
761 try!(write!(f, "<span class='summary Stable' \
762 style='width: {:.4}%; display: inline-block'> </span>",
763 (100 * cnt.stable) as f64/tot as f64));
764 try!(write!(f, "<span class='summary Unstable' \
765 style='width: {:.4}%; display: inline-block'> </span>",
766 (100 * cnt.unstable) as f64/tot as f64));
767 try!(write!(f, "<span class='summary Experimental' \
768 style='width: {:.4}%; display: inline-block'> </span>",
769 (100 * cnt.experimental) as f64/tot as f64));
770 try!(write!(f, "<span class='summary Deprecated' \
771 style='width: {:.4}%; display: inline-block'> </span>",
772 (100 * cnt.deprecated) as f64/tot as f64));
773 try!(write!(f, "<span class='summary Unmarked' \
774 style='width: {:.4}%; display: inline-block'> </span>",
775 (100 * cnt.unmarked) as f64/tot as f64));
776 try!(write!(f, "</td></tr>"));
778 for submodule in m.submodules.iter() {
779 try!(fmt_inner(f, context, submodule));
785 let mut context = Vec::new();
787 let tot = self.counts.total();
788 let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
791 ((100 * self.counts.stable)/tot,
792 (100 * self.counts.unstable)/tot,
793 (100 * self.counts.experimental)/tot,
794 (100 * self.counts.deprecated)/tot,
795 (100 * self.counts.unmarked)/tot)
799 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</a></h1>
800 This dashboard summarizes the stability levels for all of the public modules of
801 the crate, according to the total number of items at each level in the module and
802 its children (percentages total for {name}):
804 <a class='stability Stable'></a> stable ({}%),<br/>
805 <a class='stability Unstable'></a> unstable ({}%),<br/>
806 <a class='stability Experimental'></a> experimental ({}%),<br/>
807 <a class='stability Deprecated'></a> deprecated ({}%),<br/>
808 <a class='stability Unmarked'></a> unmarked ({}%)
810 The counts do not include methods or trait
811 implementations that are visible only through a re-exported type.",
812 stable, unstable, experimental, deprecated, unmarked,
814 try!(write!(f, "<table>"));
815 try!(fmt_inner(f, &mut context, self));
816 write!(f, "</table>")