]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
doc: remove incomplete sentence
[rust.git] / src / librustdoc / html / format.rs
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.
4 //
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.
10
11 //! HTML formatting module
12 //!
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.
17
18 use std::fmt;
19 use std::iter::repeat;
20
21 use syntax::ast;
22 use syntax::ast_util;
23
24 use clean;
25 use stability_summary::ModuleSummary;
26 use html::item_type::ItemType;
27 use html::render;
28 use html::render::{cache, CURRENT_LOCATION_KEY};
29
30 /// Helper to render an optional visibility with a space after it (if the
31 /// visibility is preset)
32 #[deriving(Copy)]
33 pub struct VisSpace(pub Option<ast::Visibility>);
34 /// Similarly to VisSpace, this structure is used to render a function style with a
35 /// space after it.
36 #[deriving(Copy)]
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
41 #[deriving(Copy)]
42 pub struct MutableSpace(pub clean::Mutability);
43 /// Similar to VisSpace, but used for mutability
44 #[deriving(Copy)]
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
55 impl VisSpace {
56     pub fn get(&self) -> Option<ast::Visibility> {
57         let VisSpace(v) = *self; v
58     }
59 }
60
61 impl UnsafetySpace {
62     pub fn get(&self) -> ast::Unsafety {
63         let UnsafetySpace(v) = *self; v
64     }
65 }
66
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() {
71             if i > 0 {
72                 try!(f.write_str(" + "));
73             }
74             try!(write!(f, "{}", *bound));
75         }
76         Ok(())
77     }
78 }
79
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("&lt;"));
84
85         for (i, life) in self.lifetimes.iter().enumerate() {
86             if i > 0 {
87                 try!(f.write_str(", "));
88             }
89             try!(write!(f, "{}", *life));
90         }
91
92         if self.type_params.len() > 0 {
93             if self.lifetimes.len() > 0 {
94                 try!(f.write_str(", "));
95             }
96             for (i, tp) in self.type_params.iter().enumerate() {
97                 if i > 0 {
98                     try!(f.write_str(", "))
99                 }
100                 try!(f.write_str(tp.name[]));
101
102                 if tp.bounds.len() > 0 {
103                     try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice())));
104                 }
105
106                 match tp.default {
107                     Some(ref ty) => { try!(write!(f, " = {}", ty)); },
108                     None => {}
109                 };
110             }
111         }
112         try!(f.write_str("&gt;"));
113         Ok(())
114     }
115 }
116
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 {
121             return Ok(());
122         }
123         try!(f.write_str(" <span class='where'>where "));
124         for (i, pred) in gens.where_predicates.iter().enumerate() {
125             if i > 0 {
126                 try!(f.write_str(", "));
127             }
128             match pred {
129                 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
130                     let bounds = bounds.as_slice();
131                     try!(write!(f, "{}: {}", ty, TyParamBounds(bounds)));
132                 }
133                 &clean::WherePredicate::RegionPredicate { ref lifetime,
134                                                           ref bounds } => {
135                     try!(write!(f, "{}: ", lifetime));
136                     for (i, lifetime) in bounds.iter().enumerate() {
137                         if i > 0 {
138                             try!(f.write_str(" + "));
139                         }
140
141                         try!(write!(f, "{}", lifetime));
142                     }
143                 }
144                 &clean::WherePredicate::EqPredicate => {
145                     unimplemented!()
146                 }
147             }
148         }
149         try!(f.write_str("</span>"));
150         Ok(())
151     }
152 }
153
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()));
157         Ok(())
158     }
159 }
160
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&lt;"));
165             for (i, lt) in self.lifetimes.iter().enumerate() {
166                 if i > 0 {
167                     try!(f.write_str(", "));
168                 }
169                 try!(write!(f, "{}", lt));
170             }
171             try!(f.write_str("&gt; "));
172         }
173         write!(f, "{}", self.trait_)
174     }
175 }
176
177 impl fmt::Show for clean::TyParamBound {
178     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179         match *self {
180             clean::RegionBound(ref lt) => {
181                 write!(f, "{}", *lt)
182             }
183             clean::TraitBound(ref ty, modifier) => {
184                 let modifier_str = match modifier {
185                     ast::TraitBoundModifier::None => "",
186                     ast::TraitBoundModifier::Maybe => "?",
187                 };
188                 write!(f, "{}{}", modifier_str, *ty)
189             }
190         }
191     }
192 }
193
194 impl fmt::Show for clean::PathParameters {
195     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196         match *self {
197             clean::PathParameters::AngleBracketed { ref lifetimes, ref types } => {
198                 if lifetimes.len() > 0 || types.len() > 0 {
199                     try!(f.write_str("&lt;"));
200                     let mut comma = false;
201                     for lifetime in lifetimes.iter() {
202                         if comma {
203                             try!(f.write_str(", "));
204                         }
205                         comma = true;
206                         try!(write!(f, "{}", *lifetime));
207                     }
208                     for ty in types.iter() {
209                         if comma {
210                             try!(f.write_str(", "));
211                         }
212                         comma = true;
213                         try!(write!(f, "{}", *ty));
214                     }
215                     try!(f.write_str("&gt;"));
216                 }
217             }
218             clean::PathParameters::Parenthesized { ref inputs, ref output } => {
219                 try!(f.write_str("("));
220                 let mut comma = false;
221                 for ty in inputs.iter() {
222                     if comma {
223                         try!(f.write_str(", "));
224                     }
225                     comma = true;
226                     try!(write!(f, "{}", *ty));
227                 }
228                 try!(f.write_str(")"));
229                 if let Some(ref ty) = *output {
230                     try!(f.write_str(" -&gt; "));
231                     try!(write!(f, "{}", ty));
232                 }
233             }
234         }
235         Ok(())
236     }
237 }
238
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)
243     }
244 }
245
246 impl fmt::Show for clean::Path {
247     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
248         if self.global {
249             try!(f.write_str("::"))
250         }
251
252         for (i, seg) in self.segments.iter().enumerate() {
253             if i > 0 {
254                 try!(f.write_str("::"))
255             }
256             try!(write!(f, "{}", seg));
257         }
258         Ok(())
259     }
260 }
261
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,
267         |cache, loc| {
268             if ast_util::is_local(did) || cache.inlined.contains(&did) {
269                 Some(repeat("../").take(loc.len()).collect::<String>())
270             } else {
271                 match cache.extern_locations[did.krate] {
272                     render::Remote(ref s) => Some(s.to_string()),
273                     render::Local => {
274                         Some(repeat("../").take(loc.len()).collect::<String>())
275                     }
276                     render::Unknown => None,
277                 }
278             }
279         },
280         |cache| {
281             match cache.paths.get(&did) {
282                 None => None,
283                 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
284             }
285         })
286 }
287
288 fn path<F, G>(w: &mut fmt::Formatter,
289               path: &clean::Path,
290               print_all: bool,
291               root: F,
292               info: G)
293               -> fmt::Result where
294     F: FnOnce(&render::Cache, &[String]) -> Option<String>,
295     G: FnOnce(&render::Cache) -> Option<(Vec<String>, ItemType)>,
296 {
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);
300
301     let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
302     let cache = cache();
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()),
306         _ => None,
307     };
308
309     if print_all {
310         let amt = path.segments.len() - 1;
311         match rel_root {
312             Some(root) => {
313                 let mut root = String::from_str(root.as_slice());
314                 for seg in path.segments[..amt].iter() {
315                     if "super" == seg.name ||
316                             "self" == seg.name {
317                         try!(write!(w, "{}::", seg.name));
318                     } else {
319                         root.push_str(seg.name.as_slice());
320                         root.push_str("/");
321                         try!(write!(w, "<a class='mod'
322                                             href='{}index.html'>{}</a>::",
323                                       root.as_slice(),
324                                       seg.name));
325                     }
326                 }
327             }
328             None => {
329                 for seg in path.segments[..amt].iter() {
330                     try!(write!(w, "{}::", seg.name));
331                 }
332             }
333         }
334     }
335
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());
343                 url.push_str("/");
344             }
345             match shortty {
346                 ItemType::Module => {
347                     url.push_str(fqp.last().unwrap().as_slice());
348                     url.push_str("/index.html");
349                 }
350                 _ => {
351                     url.push_str(shortty.to_static_str());
352                     url.push_str(".");
353                     url.push_str(fqp.last().unwrap().as_slice());
354                     url.push_str(".html");
355                 }
356             }
357
358             try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
359                           shortty, url, fqp.connect("::"), last.name));
360         }
361
362         _ => {
363             try!(write!(w, "{}", last.name));
364         }
365     }
366     try!(write!(w, "{}", generics.as_slice()));
367     Ok(())
368 }
369
370 fn primitive_link(f: &mut fmt::Formatter,
371                   prim: clean::PrimitiveType,
372                   name: &str) -> fmt::Result {
373     let m = cache();
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>(),
381                         prim.to_url_str()));
382             needs_termination = true;
383         }
384         Some(&cnum) => {
385             let path = &m.paths[ast::DefId {
386                 krate: cnum,
387                 node: ast::CRATE_NODE_ID,
388             }];
389             let loc = match m.extern_locations[cnum] {
390                 render::Remote(ref s) => Some(s.to_string()),
391                 render::Local => {
392                     let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
393                     Some(repeat("../").take(len).collect::<String>())
394                 }
395                 render::Unknown => None,
396             };
397             match loc {
398                 Some(root) => {
399                     try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
400                                 root,
401                                 path.0.first().unwrap(),
402                                 prim.to_url_str()));
403                     needs_termination = true;
404                 }
405                 None => {}
406             }
407         }
408         None => {}
409     }
410     try!(write!(f, "{}", name));
411     if needs_termination {
412         try!(write!(f, "</a>"));
413     }
414     Ok(())
415 }
416
417 /// Helper to render type parameters
418 fn tybounds(w: &mut fmt::Formatter,
419             typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
420     match *typarams {
421         Some(ref params) => {
422             for param in params.iter() {
423                 try!(write!(w, " + "));
424                 try!(write!(w, "{}", *param));
425             }
426             Ok(())
427         }
428         None => Ok(())
429     }
430 }
431
432 impl fmt::Show for clean::Type {
433     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
434         match *self {
435             clean::TyParamBinder(id) => {
436                 f.write_str(cache().typarams[ast_util::local_def(id)][])
437             }
438             clean::Generic(ref name) => {
439                 f.write_str(name.as_slice())
440             }
441             clean::ResolvedPath{ did, ref typarams, ref path } => {
442                 try!(resolved_path(f, did, path, false));
443                 tybounds(f, typarams)
444             }
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 {
451                            "".to_string()
452                        } else {
453                            format!("for &lt;{:#}&gt;", decl.lifetimes)
454                        },
455                        args = decl.decl.inputs,
456                        arrow = decl.decl.output,
457                        bounds = {
458                            let mut ret = String::new();
459                            for bound in decl.bounds.iter() {
460                                 match *bound {
461                                     clean::RegionBound(..) => {}
462                                     clean::TraitBound(ref t, modifier) => {
463                                         if ret.len() == 0 {
464                                             ret.push_str(": ");
465                                         } else {
466                                             ret.push_str(" + ");
467                                         }
468                                         if modifier == ast::TraitBoundModifier::Maybe {
469                                             ret.push_str("?");
470                                         }
471                                         ret.push_str(format!("{}",
472                                                              *t).as_slice());
473                                     }
474                                 }
475                            }
476                            ret
477                        })
478             }
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 {
483                            "".to_string()
484                        } else {
485                            format!("for &lt;{:#}&gt;", decl.lifetimes)
486                        },
487                        args = decl.decl.inputs,
488                        bounds = if decl.bounds.len() == 0 {
489                            "".to_string()
490                        } else {
491                            let m = decl.bounds
492                                            .iter()
493                                            .map(|s| s.to_string());
494                            format!(
495                                ": {}",
496                                m.collect::<Vec<String>>().connect(" + "))
497                        },
498                        arrow = decl.decl.output)
499             }
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)
507                        },
508                        decl.generics,
509                        decl.decl)
510             }
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)
516                                }.as_slice())
517             }
518             clean::Vector(ref t) => {
519                 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
520             }
521             clean::FixedVector(ref t, ref s) => {
522                 primitive_link(f, clean::Slice,
523                                format!("[{}, ..{}]", **t, *s).as_slice())
524             }
525             clean::Bottom => f.write_str("!"),
526             clean::RawPointer(m, ref t) => {
527                 write!(f, "*{}{}", RawMutableSpace(m), **t)
528             }
529             clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
530                 let lt = match *l {
531                     Some(ref l) => format!("{} ", *l),
532                     _ => "".to_string(),
533                 };
534                 let m = MutableSpace(mutability);
535                 match **ty {
536                     clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
537                         match **bt {
538                             clean::Generic(_) =>
539                                 primitive_link(f, clean::Slice,
540                                     format!("&amp;{}{}[{}]", lt, m, **bt).as_slice()),
541                             _ => {
542                                 try!(primitive_link(f, clean::Slice,
543                                     format!("&amp;{}{}[", lt, m).as_slice()));
544                                 try!(write!(f, "{}", **bt));
545                                 primitive_link(f, clean::Slice, "]")
546                             }
547                         }
548                     }
549                     _ => {
550                         write!(f, "&amp;{}{}{}", lt, m, **ty)
551                     }
552                 }
553             }
554             clean::PolyTraitRef(ref bounds) => {
555                 for (i, bound) in bounds.iter().enumerate() {
556                     if i != 0 {
557                         try!(write!(f, " + "));
558                     }
559                     try!(write!(f, "{}", *bound));
560                 }
561                 Ok(())
562             }
563             clean::QPath { ref name, ref self_type, ref trait_ } => {
564                 write!(f, "&lt;{} as {}&gt;::{}", self_type, trait_, name)
565             }
566             clean::Unique(..) => {
567                 panic!("should have been cleaned")
568             }
569         }
570     }
571 }
572
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));
579             }
580             try!(write!(f, "{}", input.type_));
581         }
582         Ok(())
583     }
584 }
585
586 impl fmt::Show for clean::FunctionRetTy {
587     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
588         match *self {
589             clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
590             clean::Return(ref ty) => write!(f, " -&gt; {}", ty),
591             clean::NoReturn => write!(f, " -&gt; !")
592         }
593     }
594 }
595
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)
599     }
600 }
601
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();
606         match *selfty {
607             clean::SelfStatic => {},
608             clean::SelfValue => args.push_str("self"),
609             clean::SelfBorrowed(Some(ref lt), mtbl) => {
610                 args.push_str(format!("&amp;{} {}self", *lt,
611                                       MutableSpace(mtbl)).as_slice());
612             }
613             clean::SelfBorrowed(None, mtbl) => {
614                 args.push_str(format!("&amp;{}self",
615                                       MutableSpace(mtbl)).as_slice());
616             }
617             clean::SelfExplicit(ref typ) => {
618                 args.push_str(format!("self: {}", *typ).as_slice());
619             }
620         }
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());
625             }
626             args.push_str(format!("{}", input.type_).as_slice());
627         }
628         write!(f, "({args}){arrow}", args = args, arrow = d.output)
629     }
630 }
631
632 impl fmt::Show for VisSpace {
633     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
634         match self.get() {
635             Some(ast::Public) => write!(f, "pub "),
636             Some(ast::Inherited) | None => Ok(())
637         }
638     }
639 }
640
641 impl fmt::Show for UnsafetySpace {
642     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
643         match self.get() {
644             ast::Unsafety::Unsafe => write!(f, "unsafe "),
645             ast::Unsafety::Normal => Ok(())
646         }
647     }
648 }
649
650 impl fmt::Show for clean::ViewPath {
651     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652         match *self {
653             clean::SimpleImport(ref name, ref src) => {
654                 if *name == src.path.segments.last().unwrap().name {
655                     write!(f, "use {};", *src)
656                 } else {
657                     write!(f, "use {} as {};", *src, *name)
658                 }
659             }
660             clean::GlobImport(ref src) => {
661                 write!(f, "use {}::*;", *src)
662             }
663             clean::ImportList(ref src, ref names) => {
664                 try!(write!(f, "use {}::{{", *src));
665                 for (i, n) in names.iter().enumerate() {
666                     if i > 0 {
667                         try!(write!(f, ", "));
668                     }
669                     try!(write!(f, "{}", *n));
670                 }
671                 write!(f, "}};")
672             }
673         }
674     }
675 }
676
677 impl fmt::Show for clean::ImportSource {
678     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
679         match self.did {
680             Some(did) => resolved_path(f, did, &self.path, true),
681             _ => {
682                 for (i, seg) in self.path.segments.iter().enumerate() {
683                     if i > 0 {
684                         try!(write!(f, "::"))
685                     }
686                     try!(write!(f, "{}", seg.name));
687                 }
688                 Ok(())
689             }
690         }
691     }
692 }
693
694 impl fmt::Show for clean::ViewListIdent {
695     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
696         match self.source {
697             Some(did) => {
698                 let path = clean::Path {
699                     global: false,
700                     segments: vec!(clean::PathSegment {
701                         name: self.name.clone(),
702                         params: clean::PathParameters::AngleBracketed {
703                             lifetimes: Vec::new(),
704                             types: Vec::new(),
705                         }
706                     })
707                 };
708                 resolved_path(f, did, &path, false)
709             }
710             _ => write!(f, "{}", self.name),
711         }
712     }
713 }
714
715 impl fmt::Show for MutableSpace {
716     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
717         match *self {
718             MutableSpace(clean::Immutable) => Ok(()),
719             MutableSpace(clean::Mutable) => write!(f, "mut "),
720         }
721     }
722 }
723
724 impl fmt::Show for RawMutableSpace {
725     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
726         match *self {
727             RawMutableSpace(clean::Immutable) => write!(f, "const "),
728             RawMutableSpace(clean::Mutable) => write!(f, "mut "),
729         }
730     }
731 }
732
733 impl<'a> fmt::Show for Stability<'a> {
734     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
735         let Stability(stab) = *self;
736         match *stab {
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)
741             }
742             None => Ok(())
743         }
744     }
745 }
746
747 impl<'a> fmt::Show for ConciseStability<'a> {
748     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
749         let ConciseStability(stab) = *self;
750         match *stab {
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)
756             }
757             None => {
758                 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
759             }
760         }
761     }
762 }
763
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)
769                      -> fmt::Result {
770             let cnt = m.counts;
771             let tot = cnt.total();
772             if tot == 0 { return Ok(()) }
773
774             context.push(m.name.as_slice());
775             let path = context.connect("::");
776
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");
781                             url.connect("/")
782                         },
783                         path));
784             try!(write!(f, "<td class='summary-column'>"));
785             try!(write!(f, "<span class='summary Stable' \
786                             style='width: {:.4}%; display: inline-block'>&nbsp</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'>&nbsp</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'>&nbsp</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'>&nbsp</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'>&nbsp</span>",
799                         (100 * cnt.unmarked) as f64/tot as f64));
800             try!(write!(f, "</td></tr>"));
801
802             for submodule in m.submodules.iter() {
803                 try!(fmt_inner(f, context, submodule));
804             }
805             context.pop();
806             Ok(())
807         }
808
809         let mut context = Vec::new();
810
811         let tot = self.counts.total();
812         let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
813             (0, 0, 0, 0, 0)
814         } else {
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)
820         };
821
822         try!(write!(f,
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}):
827 <blockquote>
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 ({}%)
833 </blockquote>
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,
837 name=self.name));
838         try!(write!(f, "<table>"));
839         try!(fmt_inner(f, &mut context, self));
840         write!(f, "</table>")
841     }
842 }