]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
auto merge of #15591 : aturon/rust/box-cell-stability, 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                                match typs.as_slice() {
435                                     [ref one] => format!("({},)", one),
436                                     many => format!("({:#})", many)
437                                }.as_slice())
438             }
439             clean::Vector(ref t) => {
440                 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
441             }
442             clean::FixedVector(ref t, ref s) => {
443                 primitive_link(f, clean::Slice,
444                                format!("[{}, ..{}]", **t, *s).as_slice())
445             }
446             clean::Bottom => f.write("!".as_bytes()),
447             clean::Unique(ref t) => write!(f, "Box<{}>", **t),
448             clean::Managed(ref t) => write!(f, "Gc<{}>", **t),
449             clean::RawPointer(m, ref t) => {
450                 write!(f, "*{}{}", RawMutableSpace(m), **t)
451             }
452             clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
453                 let lt = match *l {
454                     Some(ref l) => format!("{} ", *l),
455                     _ => "".to_string(),
456                 };
457                 write!(f, "&amp;{}{}{}", lt, MutableSpace(mutability), **ty)
458             }
459         }
460     }
461 }
462
463 impl fmt::Show for clean::Arguments {
464     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
465         for (i, input) in self.values.iter().enumerate() {
466             if i > 0 { try!(write!(f, ", ")); }
467             if input.name.len() > 0 {
468                 try!(write!(f, "{}: ", input.name));
469             }
470             try!(write!(f, "{}", input.type_));
471         }
472         Ok(())
473     }
474 }
475
476 impl fmt::Show for clean::FnDecl {
477     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478         write!(f, "({args}){arrow}",
479                args = self.inputs,
480                arrow = match self.output {
481                    clean::Primitive(clean::Unit) => "".to_string(),
482                    _ => format!(" -&gt; {}", self.output),
483                })
484     }
485 }
486
487 impl<'a> fmt::Show for Method<'a> {
488     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
489         let Method(selfty, d) = *self;
490         let mut args = String::new();
491         match *selfty {
492             clean::SelfStatic => {},
493             clean::SelfValue => args.push_str("self"),
494             clean::SelfOwned => args.push_str("~self"),
495             clean::SelfBorrowed(Some(ref lt), mtbl) => {
496                 args.push_str(format!("&amp;{} {}self", *lt,
497                                       MutableSpace(mtbl)).as_slice());
498             }
499             clean::SelfBorrowed(None, mtbl) => {
500                 args.push_str(format!("&amp;{}self",
501                                       MutableSpace(mtbl)).as_slice());
502             }
503         }
504         for (i, input) in d.inputs.values.iter().enumerate() {
505             if i > 0 || args.len() > 0 { args.push_str(", "); }
506             if input.name.len() > 0 {
507                 args.push_str(format!("{}: ", input.name).as_slice());
508             }
509             args.push_str(format!("{}", input.type_).as_slice());
510         }
511         write!(f, "({args}){arrow}",
512                args = args,
513                arrow = match d.output {
514                    clean::Primitive(clean::Unit) => "".to_string(),
515                    _ => format!(" -&gt; {}", d.output),
516                })
517     }
518 }
519
520 impl fmt::Show for VisSpace {
521     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
522         match self.get() {
523             Some(ast::Public) => write!(f, "pub "),
524             Some(ast::Inherited) | None => Ok(())
525         }
526     }
527 }
528
529 impl fmt::Show for FnStyleSpace {
530     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
531         match self.get() {
532             ast::UnsafeFn => write!(f, "unsafe "),
533             ast::NormalFn => Ok(())
534         }
535     }
536 }
537
538 impl fmt::Show for clean::ViewPath {
539     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
540         match *self {
541             clean::SimpleImport(ref name, ref src) => {
542                 if *name == src.path.segments.last().unwrap().name {
543                     write!(f, "use {};", *src)
544                 } else {
545                     write!(f, "use {} = {};", *name, *src)
546                 }
547             }
548             clean::GlobImport(ref src) => {
549                 write!(f, "use {}::*;", *src)
550             }
551             clean::ImportList(ref src, ref names) => {
552                 try!(write!(f, "use {}::{{", *src));
553                 for (i, n) in names.iter().enumerate() {
554                     if i > 0 {
555                         try!(write!(f, ", "));
556                     }
557                     try!(write!(f, "{}", *n));
558                 }
559                 write!(f, "}};")
560             }
561         }
562     }
563 }
564
565 impl fmt::Show for clean::ImportSource {
566     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
567         match self.did {
568             Some(did) => resolved_path(f, did, &self.path, true),
569             _ => {
570                 for (i, seg) in self.path.segments.iter().enumerate() {
571                     if i > 0 {
572                         try!(write!(f, "::"))
573                     }
574                     try!(write!(f, "{}", seg.name));
575                 }
576                 Ok(())
577             }
578         }
579     }
580 }
581
582 impl fmt::Show for clean::ViewListIdent {
583     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
584         match self.source {
585             Some(did) => {
586                 let path = clean::Path {
587                     global: false,
588                     segments: vec!(clean::PathSegment {
589                         name: self.name.clone(),
590                         lifetimes: Vec::new(),
591                         types: Vec::new(),
592                     })
593                 };
594                 resolved_path(f, did, &path, false)
595             }
596             _ => write!(f, "{}", self.name),
597         }
598     }
599 }
600
601 impl fmt::Show for MutableSpace {
602     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
603         match *self {
604             MutableSpace(clean::Immutable) => Ok(()),
605             MutableSpace(clean::Mutable) => write!(f, "mut "),
606         }
607     }
608 }
609
610 impl fmt::Show for RawMutableSpace {
611     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
612         match *self {
613             RawMutableSpace(clean::Immutable) => write!(f, "const "),
614             RawMutableSpace(clean::Mutable) => write!(f, "mut "),
615         }
616     }
617 }
618
619 impl<'a> fmt::Show for Stability<'a> {
620     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
621         let Stability(stab) = *self;
622         match *stab {
623             Some(ref stability) => {
624                 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
625                        lvl = stability.level.to_string(),
626                        reason = stability.text)
627             }
628             None => Ok(())
629         }
630     }
631 }
632
633 impl<'a> fmt::Show for ConciseStability<'a> {
634     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635         let ConciseStability(stab) = *self;
636         match *stab {
637             Some(ref stability) => {
638                 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
639                        lvl = stability.level.to_string(),
640                        colon = if stability.text.len() > 0 { ": " } else { "" },
641                        reason = stability.text)
642             }
643             None => {
644                 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
645             }
646         }
647     }
648 }
649
650 impl fmt::Show for ModuleSummary {
651     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652         fn fmt_inner<'a>(f: &mut fmt::Formatter,
653                          context: &mut Vec<&'a str>,
654                          m: &'a ModuleSummary)
655                      -> fmt::Result {
656             let cnt = m.counts;
657             let tot = cnt.total();
658             if tot == 0 { return Ok(()) }
659
660             context.push(m.name.as_slice());
661             let path = context.connect("::");
662
663             // the total width of each row's stability summary, in pixels
664             let width = 500;
665
666             try!(write!(f, "<tr>"));
667             try!(write!(f, "<td class='summary'>\
668                             <a class='summary' href='{}'>{}</a></td>",
669                         Vec::from_slice(context.slice_from(1))
670                             .append_one("index.html").connect("/"),
671                         path));
672             try!(write!(f, "<td>"));
673             try!(write!(f, "<span class='summary Stable' \
674                             style='width: {}px; display: inline-block'>&nbsp</span>",
675                         (width * cnt.stable)/tot));
676             try!(write!(f, "<span class='summary Unstable' \
677                             style='width: {}px; display: inline-block'>&nbsp</span>",
678                         (width * cnt.unstable)/tot));
679             try!(write!(f, "<span class='summary Experimental' \
680                             style='width: {}px; display: inline-block'>&nbsp</span>",
681                         (width * cnt.experimental)/tot));
682             try!(write!(f, "<span class='summary Deprecated' \
683                             style='width: {}px; display: inline-block'>&nbsp</span>",
684                         (width * cnt.deprecated)/tot));
685             try!(write!(f, "<span class='summary Unmarked' \
686                             style='width: {}px; display: inline-block'>&nbsp</span>",
687                         (width * cnt.unmarked)/tot));
688             try!(write!(f, "</td></tr>"));
689
690             for submodule in m.submodules.iter() {
691                 try!(fmt_inner(f, context, submodule));
692             }
693             context.pop();
694             Ok(())
695         }
696
697         let mut context = Vec::new();
698
699         try!(write!(f,
700 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
701 This dashboard summarizes the stability levels for all of the public modules of
702 the crate, according to the total number of items at each level in the module and its children:
703 <blockquote>
704 <a class='stability Stable'></a> stable,<br/>
705 <a class='stability Unstable'></a> unstable,<br/>
706 <a class='stability Experimental'></a> experimental,<br/>
707 <a class='stability Deprecated'></a> deprecated,<br/>
708 <a class='stability Unmarked'></a> unmarked
709 </blockquote>
710 The counts do not include methods or trait
711 implementations that are visible only through a re-exported type.",
712 self.name));
713         try!(write!(f, "<table>"))
714         try!(fmt_inner(f, &mut context, self));
715         write!(f, "</table>")
716     }
717 }