]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
auto merge of #15605 : blake2-ppc/rust/rustdoc-const-t, r=alexcrichton
[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::string::String;
20
21 use syntax::ast;
22 use syntax::ast_util;
23
24 use clean;
25 use stability_summary::ModuleSummary;
26 use html::item_type;
27 use html::item_type::ItemType;
28 use html::render;
29 use html::render::{cache_key, current_location_key};
30
31 /// Helper to render an optional visibility with a space after it (if the
32 /// 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
35 /// space after it.
36 pub struct FnStyleSpace(pub ast::FnStyle);
37 /// Wrapper struct for properly emitting a method declaration.
38 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
39 /// Similar to VisSpace, but used for mutability
40 pub struct MutableSpace(pub clean::Mutability);
41 /// Similar to VisSpace, but used for mutability
42 pub struct RawMutableSpace(pub clean::Mutability);
43 /// Wrapper struct for properly emitting the stability level.
44 pub struct Stability<'a>(pub &'a Option<clean::Stability>);
45 /// Wrapper struct for emitting the stability level concisely.
46 pub struct ConciseStability<'a>(pub &'a Option<clean::Stability>);
47
48 impl VisSpace {
49     pub fn get(&self) -> Option<ast::Visibility> {
50         let VisSpace(v) = *self; v
51     }
52 }
53
54 impl FnStyleSpace {
55     pub fn get(&self) -> ast::FnStyle {
56         let FnStyleSpace(v) = *self; v
57     }
58 }
59
60 impl fmt::Show for clean::Generics {
61     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62         if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
63         try!(f.write("&lt;".as_bytes()));
64
65         for (i, life) in self.lifetimes.iter().enumerate() {
66             if i > 0 {
67                 try!(f.write(", ".as_bytes()));
68             }
69             try!(write!(f, "{}", *life));
70         }
71
72         if self.type_params.len() > 0 {
73             if self.lifetimes.len() > 0 {
74                 try!(f.write(", ".as_bytes()));
75             }
76
77             for (i, tp) in self.type_params.iter().enumerate() {
78                 if i > 0 {
79                     try!(f.write(", ".as_bytes()))
80                 }
81                 try!(f.write(tp.name.as_bytes()));
82
83                 if tp.bounds.len() > 0 {
84                     try!(f.write(": ".as_bytes()));
85                     for (i, bound) in tp.bounds.iter().enumerate() {
86                         if i > 0 {
87                             try!(f.write(" + ".as_bytes()));
88                         }
89                         try!(write!(f, "{}", *bound));
90                     }
91                 }
92
93                 match tp.default {
94                     Some(ref ty) => { try!(write!(f, " = {}", ty)); },
95                     None => {}
96                 };
97             }
98         }
99         try!(f.write("&gt;".as_bytes()));
100         Ok(())
101     }
102 }
103
104 impl fmt::Show for clean::Lifetime {
105     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106         try!(f.write(self.get_ref().as_bytes()));
107         Ok(())
108     }
109 }
110
111 impl fmt::Show for clean::TyParamBound {
112     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113         match *self {
114             clean::RegionBound => {
115                 f.write("'static".as_bytes())
116             }
117             clean::TraitBound(ref ty) => {
118                 write!(f, "{}", *ty)
119             }
120         }
121     }
122 }
123
124 impl fmt::Show for clean::Path {
125     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
126         if self.global {
127             try!(f.write("::".as_bytes()))
128         }
129
130         for (i, seg) in self.segments.iter().enumerate() {
131             if i > 0 {
132                 try!(f.write("::".as_bytes()))
133             }
134             try!(f.write(seg.name.as_bytes()));
135
136             if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
137                 try!(f.write("&lt;".as_bytes()));
138                 let mut comma = false;
139                 for lifetime in seg.lifetimes.iter() {
140                     if comma {
141                         try!(f.write(", ".as_bytes()));
142                     }
143                     comma = true;
144                     try!(write!(f, "{}", *lifetime));
145                 }
146                 for ty in seg.types.iter() {
147                     if comma {
148                         try!(f.write(", ".as_bytes()));
149                     }
150                     comma = true;
151                     try!(write!(f, "{}", *ty));
152                 }
153                 try!(f.write("&gt;".as_bytes()));
154             }
155         }
156         Ok(())
157     }
158 }
159
160 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
161 /// rendering function with the necessary arguments for linking to a local path.
162 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, p: &clean::Path,
163                  print_all: bool) -> fmt::Result {
164     path(w, p, print_all,
165         |cache, loc| {
166             if ast_util::is_local(did) || cache.inlined.contains(&did) {
167                 Some(("../".repeat(loc.len())).to_string())
168             } else {
169                 match *cache.extern_locations.get(&did.krate) {
170                     render::Remote(ref s) => Some(s.to_string()),
171                     render::Local => {
172                         Some(("../".repeat(loc.len())).to_string())
173                     }
174                     render::Unknown => None,
175                 }
176             }
177         },
178         |cache| {
179             match cache.paths.find(&did) {
180                 None => None,
181                 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
182             }
183         })
184 }
185
186 fn path(w: &mut fmt::Formatter, path: &clean::Path, print_all: bool,
187         root: |&render::Cache, &[String]| -> Option<String>,
188         info: |&render::Cache| -> Option<(Vec<String> , ItemType)>)
189     -> fmt::Result
190 {
191     // The generics will get written to both the title and link
192     let mut generics = String::new();
193     let last = path.segments.last().unwrap();
194     if last.lifetimes.len() > 0 || last.types.len() > 0 {
195         let mut counter = 0u;
196         generics.push_str("&lt;");
197         for lifetime in last.lifetimes.iter() {
198             if counter > 0 { generics.push_str(", "); }
199             counter += 1;
200             generics.push_str(format!("{}", *lifetime).as_slice());
201         }
202         for ty in last.types.iter() {
203             if counter > 0 { generics.push_str(", "); }
204             counter += 1;
205             generics.push_str(format!("{}", *ty).as_slice());
206         }
207         generics.push_str("&gt;");
208     }
209
210     let loc = current_location_key.get().unwrap();
211     let cache = cache_key.get().unwrap();
212     let abs_root = root(&**cache, loc.as_slice());
213     let rel_root = match path.segments.get(0).name.as_slice() {
214         "self" => Some("./".to_string()),
215         _ => None,
216     };
217
218     if print_all {
219         let amt = path.segments.len() - 1;
220         match rel_root {
221             Some(root) => {
222                 let mut root = String::from_str(root.as_slice());
223                 for seg in path.segments.slice_to(amt).iter() {
224                     if "super" == seg.name.as_slice() ||
225                             "self" == seg.name.as_slice() {
226                         try!(write!(w, "{}::", seg.name));
227                     } else {
228                         root.push_str(seg.name.as_slice());
229                         root.push_str("/");
230                         try!(write!(w, "<a class='mod'
231                                             href='{}index.html'>{}</a>::",
232                                       root.as_slice(),
233                                       seg.name));
234                     }
235                 }
236             }
237             None => {
238                 for seg in path.segments.slice_to(amt).iter() {
239                     try!(write!(w, "{}::", seg.name));
240                 }
241             }
242         }
243     }
244
245     match info(&**cache) {
246         // This is a documented path, link to it!
247         Some((ref fqp, shortty)) if abs_root.is_some() => {
248             let mut url = String::from_str(abs_root.unwrap().as_slice());
249             let to_link = fqp.slice_to(fqp.len() - 1);
250             for component in to_link.iter() {
251                 url.push_str(component.as_slice());
252                 url.push_str("/");
253             }
254             match shortty {
255                 item_type::Module => {
256                     url.push_str(fqp.last().unwrap().as_slice());
257                     url.push_str("/index.html");
258                 }
259                 _ => {
260                     url.push_str(shortty.to_static_str());
261                     url.push_str(".");
262                     url.push_str(fqp.last().unwrap().as_slice());
263                     url.push_str(".html");
264                 }
265             }
266
267             try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
268                           shortty, url, fqp.connect("::"), last.name));
269         }
270
271         _ => {
272             try!(write!(w, "{}", last.name));
273         }
274     }
275     try!(write!(w, "{}", generics.as_slice()));
276     Ok(())
277 }
278
279 fn primitive_link(f: &mut fmt::Formatter,
280                   prim: clean::Primitive,
281                   name: &str) -> fmt::Result {
282     let m = cache_key.get().unwrap();
283     let mut needs_termination = false;
284     match m.primitive_locations.find(&prim) {
285         Some(&ast::LOCAL_CRATE) => {
286             let loc = current_location_key.get().unwrap();
287             let len = if loc.len() == 0 {0} else {loc.len() - 1};
288             try!(write!(f, "<a href='{}primitive.{}.html'>",
289                         "../".repeat(len),
290                         prim.to_url_str()));
291             needs_termination = true;
292         }
293         Some(&cnum) => {
294             let path = m.paths.get(&ast::DefId {
295                 krate: cnum,
296                 node: ast::CRATE_NODE_ID,
297             });
298             let loc = match *m.extern_locations.get(&cnum) {
299                 render::Remote(ref s) => Some(s.to_string()),
300                 render::Local => {
301                     let loc = current_location_key.get().unwrap();
302                     Some("../".repeat(loc.len()))
303                 }
304                 render::Unknown => None,
305             };
306             match loc {
307                 Some(root) => {
308                     try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
309                                 root,
310                                 path.ref0().as_slice().head().unwrap(),
311                                 prim.to_url_str()));
312                     needs_termination = true;
313                 }
314                 None => {}
315             }
316         }
317         None => {}
318     }
319     try!(write!(f, "{}", name));
320     if needs_termination {
321         try!(write!(f, "</a>"));
322     }
323     Ok(())
324 }
325
326 /// Helper to render type parameters
327 fn tybounds(w: &mut fmt::Formatter,
328             typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
329     match *typarams {
330         Some(ref params) => {
331             for param in params.iter() {
332                 try!(write!(w, " + "));
333                 try!(write!(w, "{}", *param));
334             }
335             Ok(())
336         }
337         None => Ok(())
338     }
339 }
340
341 impl fmt::Show for clean::Type {
342     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343         match *self {
344             clean::TyParamBinder(id) => {
345                 let m = cache_key.get().unwrap();
346                 f.write(m.typarams.get(&ast_util::local_def(id)).as_bytes())
347             }
348             clean::Generic(did) => {
349                 let m = cache_key.get().unwrap();
350                 f.write(m.typarams.get(&did).as_bytes())
351             }
352             clean::ResolvedPath{ did, ref typarams, ref path } => {
353                 try!(resolved_path(f, did, path, false));
354                 tybounds(f, typarams)
355             }
356             clean::Self(..) => f.write("Self".as_bytes()),
357             clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
358             clean::Closure(ref decl, ref region) => {
359                 write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}",
360                        style = FnStyleSpace(decl.fn_style),
361                        lifetimes = if decl.lifetimes.len() == 0 {
362                            "".to_string()
363                        } else {
364                            format!("&lt;{:#}&gt;", decl.lifetimes)
365                        },
366                        args = decl.decl.inputs,
367                        arrow = match decl.decl.output {
368                            clean::Primitive(clean::Unit) => "".to_string(),
369                            _ => format!(" -&gt; {}", decl.decl.output),
370                        },
371                        bounds = {
372                            let mut ret = String::new();
373                            match *region {
374                                Some(ref lt) => {
375                                    ret.push_str(format!(": {}",
376                                                         *lt).as_slice());
377                                }
378                                None => {}
379                            }
380                            for bound in decl.bounds.iter() {
381                                 match *bound {
382                                     clean::RegionBound => {}
383                                     clean::TraitBound(ref t) => {
384                                         if ret.len() == 0 {
385                                             ret.push_str(": ");
386                                         } else {
387                                             ret.push_str(" + ");
388                                         }
389                                         ret.push_str(format!("{}",
390                                                              *t).as_slice());
391                                     }
392                                 }
393                            }
394                            ret
395                        })
396             }
397             clean::Proc(ref decl) => {
398                 write!(f, "{style}{lifetimes}proc({args}){bounds}{arrow}",
399                        style = FnStyleSpace(decl.fn_style),
400                        lifetimes = if decl.lifetimes.len() == 0 {
401                            "".to_string()
402                        } else {
403                            format!("&lt;{:#}&gt;", decl.lifetimes)
404                        },
405                        args = decl.decl.inputs,
406                        bounds = if decl.bounds.len() == 0 {
407                            "".to_string()
408                        } else {
409                            let mut m = decl.bounds
410                                            .iter()
411                                            .map(|s| s.to_string());
412                            format!(
413                                ": {}",
414                                m.collect::<Vec<String>>().connect(" + "))
415                        },
416                        arrow = match decl.decl.output {
417                            clean::Primitive(clean::Unit) => "".to_string(),
418                            _ => format!(" -&gt; {}", decl.decl.output)
419                        })
420             }
421             clean::BareFunction(ref decl) => {
422                 write!(f, "{}{}fn{}{}",
423                        FnStyleSpace(decl.fn_style),
424                        match decl.abi.as_slice() {
425                            "" => " extern ".to_string(),
426                            "\"Rust\"" => "".to_string(),
427                            s => format!(" extern {} ", s)
428                        },
429                        decl.generics,
430                        decl.decl)
431             }
432             clean::Tuple(ref typs) => {
433                 primitive_link(f, clean::PrimitiveTuple,
434                                format!("({:#})", typs).as_slice())
435             }
436             clean::Vector(ref t) => {
437                 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
438             }
439             clean::FixedVector(ref t, ref s) => {
440                 primitive_link(f, clean::Slice,
441                                format!("[{}, ..{}]", **t, *s).as_slice())
442             }
443             clean::Bottom => f.write("!".as_bytes()),
444             clean::Unique(ref t) => write!(f, "Box<{}>", **t),
445             clean::Managed(ref t) => write!(f, "Gc<{}>", **t),
446             clean::RawPointer(m, ref t) => {
447                 write!(f, "*{}{}", RawMutableSpace(m), **t)
448             }
449             clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
450                 let lt = match *l {
451                     Some(ref l) => format!("{} ", *l),
452                     _ => "".to_string(),
453                 };
454                 write!(f, "&amp;{}{}{}", lt, MutableSpace(mutability), **ty)
455             }
456         }
457     }
458 }
459
460 impl fmt::Show for clean::Arguments {
461     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
462         for (i, input) in self.values.iter().enumerate() {
463             if i > 0 { try!(write!(f, ", ")); }
464             if input.name.len() > 0 {
465                 try!(write!(f, "{}: ", input.name));
466             }
467             try!(write!(f, "{}", input.type_));
468         }
469         Ok(())
470     }
471 }
472
473 impl fmt::Show for clean::FnDecl {
474     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
475         write!(f, "({args}){arrow}",
476                args = self.inputs,
477                arrow = match self.output {
478                    clean::Primitive(clean::Unit) => "".to_string(),
479                    _ => format!(" -&gt; {}", self.output),
480                })
481     }
482 }
483
484 impl<'a> fmt::Show for Method<'a> {
485     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
486         let Method(selfty, d) = *self;
487         let mut args = String::new();
488         match *selfty {
489             clean::SelfStatic => {},
490             clean::SelfValue => args.push_str("self"),
491             clean::SelfOwned => args.push_str("~self"),
492             clean::SelfBorrowed(Some(ref lt), mtbl) => {
493                 args.push_str(format!("&amp;{} {}self", *lt,
494                                       MutableSpace(mtbl)).as_slice());
495             }
496             clean::SelfBorrowed(None, mtbl) => {
497                 args.push_str(format!("&amp;{}self",
498                                       MutableSpace(mtbl)).as_slice());
499             }
500         }
501         for (i, input) in d.inputs.values.iter().enumerate() {
502             if i > 0 || args.len() > 0 { args.push_str(", "); }
503             if input.name.len() > 0 {
504                 args.push_str(format!("{}: ", input.name).as_slice());
505             }
506             args.push_str(format!("{}", input.type_).as_slice());
507         }
508         write!(f, "({args}){arrow}",
509                args = args,
510                arrow = match d.output {
511                    clean::Primitive(clean::Unit) => "".to_string(),
512                    _ => format!(" -&gt; {}", d.output),
513                })
514     }
515 }
516
517 impl fmt::Show for VisSpace {
518     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519         match self.get() {
520             Some(ast::Public) => write!(f, "pub "),
521             Some(ast::Inherited) | None => Ok(())
522         }
523     }
524 }
525
526 impl fmt::Show for FnStyleSpace {
527     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528         match self.get() {
529             ast::UnsafeFn => write!(f, "unsafe "),
530             ast::NormalFn => Ok(())
531         }
532     }
533 }
534
535 impl fmt::Show for clean::ViewPath {
536     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537         match *self {
538             clean::SimpleImport(ref name, ref src) => {
539                 if *name == src.path.segments.last().unwrap().name {
540                     write!(f, "use {};", *src)
541                 } else {
542                     write!(f, "use {} = {};", *name, *src)
543                 }
544             }
545             clean::GlobImport(ref src) => {
546                 write!(f, "use {}::*;", *src)
547             }
548             clean::ImportList(ref src, ref names) => {
549                 try!(write!(f, "use {}::{{", *src));
550                 for (i, n) in names.iter().enumerate() {
551                     if i > 0 {
552                         try!(write!(f, ", "));
553                     }
554                     try!(write!(f, "{}", *n));
555                 }
556                 write!(f, "}};")
557             }
558         }
559     }
560 }
561
562 impl fmt::Show for clean::ImportSource {
563     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
564         match self.did {
565             Some(did) => resolved_path(f, did, &self.path, true),
566             _ => {
567                 for (i, seg) in self.path.segments.iter().enumerate() {
568                     if i > 0 {
569                         try!(write!(f, "::"))
570                     }
571                     try!(write!(f, "{}", seg.name));
572                 }
573                 Ok(())
574             }
575         }
576     }
577 }
578
579 impl fmt::Show for clean::ViewListIdent {
580     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
581         match self.source {
582             Some(did) => {
583                 let path = clean::Path {
584                     global: false,
585                     segments: vec!(clean::PathSegment {
586                         name: self.name.clone(),
587                         lifetimes: Vec::new(),
588                         types: Vec::new(),
589                     })
590                 };
591                 resolved_path(f, did, &path, false)
592             }
593             _ => write!(f, "{}", self.name),
594         }
595     }
596 }
597
598 impl fmt::Show for MutableSpace {
599     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
600         match *self {
601             MutableSpace(clean::Immutable) => Ok(()),
602             MutableSpace(clean::Mutable) => write!(f, "mut "),
603         }
604     }
605 }
606
607 impl fmt::Show for RawMutableSpace {
608     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
609         match *self {
610             RawMutableSpace(clean::Immutable) => write!(f, "const "),
611             RawMutableSpace(clean::Mutable) => write!(f, "mut "),
612         }
613     }
614 }
615
616 impl<'a> fmt::Show for Stability<'a> {
617     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
618         let Stability(stab) = *self;
619         match *stab {
620             Some(ref stability) => {
621                 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
622                        lvl = stability.level.to_string(),
623                        reason = stability.text)
624             }
625             None => Ok(())
626         }
627     }
628 }
629
630 impl<'a> fmt::Show for ConciseStability<'a> {
631     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
632         let ConciseStability(stab) = *self;
633         match *stab {
634             Some(ref stability) => {
635                 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
636                        lvl = stability.level.to_string(),
637                        colon = if stability.text.len() > 0 { ": " } else { "" },
638                        reason = stability.text)
639             }
640             None => {
641                 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
642             }
643         }
644     }
645 }
646
647 impl fmt::Show for ModuleSummary {
648     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
649         fn fmt_inner<'a>(f: &mut fmt::Formatter,
650                          context: &mut Vec<&'a str>,
651                          m: &'a ModuleSummary)
652                      -> fmt::Result {
653             let cnt = m.counts;
654             let tot = cnt.total();
655             if tot == 0 { return Ok(()) }
656
657             context.push(m.name.as_slice());
658             let path = context.connect("::");
659
660             // the total width of each row's stability summary, in pixels
661             let width = 500;
662
663             try!(write!(f, "<tr>"));
664             try!(write!(f, "<td class='summary'>\
665                             <a class='summary' href='{}'>{}</a></td>",
666                         Vec::from_slice(context.slice_from(1))
667                             .append_one("index.html").connect("/"),
668                         path));
669             try!(write!(f, "<td>"));
670             try!(write!(f, "<span class='summary Stable' \
671                             style='width: {}px; display: inline-block'>&nbsp</span>",
672                         (width * cnt.stable)/tot));
673             try!(write!(f, "<span class='summary Unstable' \
674                             style='width: {}px; display: inline-block'>&nbsp</span>",
675                         (width * cnt.unstable)/tot));
676             try!(write!(f, "<span class='summary Experimental' \
677                             style='width: {}px; display: inline-block'>&nbsp</span>",
678                         (width * cnt.experimental)/tot));
679             try!(write!(f, "<span class='summary Deprecated' \
680                             style='width: {}px; display: inline-block'>&nbsp</span>",
681                         (width * cnt.deprecated)/tot));
682             try!(write!(f, "<span class='summary Unmarked' \
683                             style='width: {}px; display: inline-block'>&nbsp</span>",
684                         (width * cnt.unmarked)/tot));
685             try!(write!(f, "</td></tr>"));
686
687             for submodule in m.submodules.iter() {
688                 try!(fmt_inner(f, context, submodule));
689             }
690             context.pop();
691             Ok(())
692         }
693
694         let mut context = Vec::new();
695
696         try!(write!(f,
697 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
698 This dashboard summarizes the stability levels for all of the public modules of
699 the crate, according to the total number of items at each level in the module and its children:
700 <blockquote>
701 <a class='stability Stable'></a> stable,<br/>
702 <a class='stability Unstable'></a> unstable,<br/>
703 <a class='stability Experimental'></a> experimental,<br/>
704 <a class='stability Deprecated'></a> deprecated,<br/>
705 <a class='stability Unmarked'></a> unmarked
706 </blockquote>
707 The counts do not include methods or trait
708 implementations that are visible only through a re-exported type.",
709 self.name));
710         try!(write!(f, "<table>"))
711         try!(fmt_inner(f, &mut context, self));
712         write!(f, "</table>")
713     }
714 }