]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
auto merge of #15727 : fhahn/rust/remove-some-unwraps, 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[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             clean::SelfExplicit(ref typ) => {
504                 args.push_str(format!("self: {}", *typ).as_slice());
505             }
506         }
507         for (i, input) in d.inputs.values.iter().enumerate() {
508             if i > 0 || args.len() > 0 { args.push_str(", "); }
509             if input.name.len() > 0 {
510                 args.push_str(format!("{}: ", input.name).as_slice());
511             }
512             args.push_str(format!("{}", input.type_).as_slice());
513         }
514         write!(f, "({args}){arrow}",
515                args = args,
516                arrow = match d.output {
517                    clean::Primitive(clean::Unit) => "".to_string(),
518                    _ => format!(" -&gt; {}", d.output),
519                })
520     }
521 }
522
523 impl fmt::Show for VisSpace {
524     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
525         match self.get() {
526             Some(ast::Public) => write!(f, "pub "),
527             Some(ast::Inherited) | None => Ok(())
528         }
529     }
530 }
531
532 impl fmt::Show for FnStyleSpace {
533     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
534         match self.get() {
535             ast::UnsafeFn => write!(f, "unsafe "),
536             ast::NormalFn => Ok(())
537         }
538     }
539 }
540
541 impl fmt::Show for clean::ViewPath {
542     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
543         match *self {
544             clean::SimpleImport(ref name, ref src) => {
545                 if *name == src.path.segments.last().unwrap().name {
546                     write!(f, "use {};", *src)
547                 } else {
548                     write!(f, "use {} = {};", *name, *src)
549                 }
550             }
551             clean::GlobImport(ref src) => {
552                 write!(f, "use {}::*;", *src)
553             }
554             clean::ImportList(ref src, ref names) => {
555                 try!(write!(f, "use {}::{{", *src));
556                 for (i, n) in names.iter().enumerate() {
557                     if i > 0 {
558                         try!(write!(f, ", "));
559                     }
560                     try!(write!(f, "{}", *n));
561                 }
562                 write!(f, "}};")
563             }
564         }
565     }
566 }
567
568 impl fmt::Show for clean::ImportSource {
569     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
570         match self.did {
571             Some(did) => resolved_path(f, did, &self.path, true),
572             _ => {
573                 for (i, seg) in self.path.segments.iter().enumerate() {
574                     if i > 0 {
575                         try!(write!(f, "::"))
576                     }
577                     try!(write!(f, "{}", seg.name));
578                 }
579                 Ok(())
580             }
581         }
582     }
583 }
584
585 impl fmt::Show for clean::ViewListIdent {
586     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
587         match self.source {
588             Some(did) => {
589                 let path = clean::Path {
590                     global: false,
591                     segments: vec!(clean::PathSegment {
592                         name: self.name.clone(),
593                         lifetimes: Vec::new(),
594                         types: Vec::new(),
595                     })
596                 };
597                 resolved_path(f, did, &path, false)
598             }
599             _ => write!(f, "{}", self.name),
600         }
601     }
602 }
603
604 impl fmt::Show for MutableSpace {
605     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
606         match *self {
607             MutableSpace(clean::Immutable) => Ok(()),
608             MutableSpace(clean::Mutable) => write!(f, "mut "),
609         }
610     }
611 }
612
613 impl fmt::Show for RawMutableSpace {
614     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
615         match *self {
616             RawMutableSpace(clean::Immutable) => write!(f, "const "),
617             RawMutableSpace(clean::Mutable) => write!(f, "mut "),
618         }
619     }
620 }
621
622 impl<'a> fmt::Show for Stability<'a> {
623     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
624         let Stability(stab) = *self;
625         match *stab {
626             Some(ref stability) => {
627                 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
628                        lvl = stability.level.to_string(),
629                        reason = stability.text)
630             }
631             None => Ok(())
632         }
633     }
634 }
635
636 impl<'a> fmt::Show for ConciseStability<'a> {
637     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638         let ConciseStability(stab) = *self;
639         match *stab {
640             Some(ref stability) => {
641                 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
642                        lvl = stability.level.to_string(),
643                        colon = if stability.text.len() > 0 { ": " } else { "" },
644                        reason = stability.text)
645             }
646             None => {
647                 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
648             }
649         }
650     }
651 }
652
653 impl fmt::Show for ModuleSummary {
654     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
655         fn fmt_inner<'a>(f: &mut fmt::Formatter,
656                          context: &mut Vec<&'a str>,
657                          m: &'a ModuleSummary)
658                      -> fmt::Result {
659             let cnt = m.counts;
660             let tot = cnt.total();
661             if tot == 0 { return Ok(()) }
662
663             context.push(m.name.as_slice());
664             let path = context.connect("::");
665
666             // the total width of each row's stability summary, in pixels
667             let width = 500;
668
669             try!(write!(f, "<tr>"));
670             try!(write!(f, "<td class='summary'>\
671                             <a class='summary' href='{}'>{}</a></td>",
672                         Vec::from_slice(context.slice_from(1))
673                             .append_one("index.html").connect("/"),
674                         path));
675             try!(write!(f, "<td>"));
676             try!(write!(f, "<span class='summary Stable' \
677                             style='width: {}px; display: inline-block'>&nbsp</span>",
678                         (width * cnt.stable)/tot));
679             try!(write!(f, "<span class='summary Unstable' \
680                             style='width: {}px; display: inline-block'>&nbsp</span>",
681                         (width * cnt.unstable)/tot));
682             try!(write!(f, "<span class='summary Experimental' \
683                             style='width: {}px; display: inline-block'>&nbsp</span>",
684                         (width * cnt.experimental)/tot));
685             try!(write!(f, "<span class='summary Deprecated' \
686                             style='width: {}px; display: inline-block'>&nbsp</span>",
687                         (width * cnt.deprecated)/tot));
688             try!(write!(f, "<span class='summary Unmarked' \
689                             style='width: {}px; display: inline-block'>&nbsp</span>",
690                         (width * cnt.unmarked)/tot));
691             try!(write!(f, "</td></tr>"));
692
693             for submodule in m.submodules.iter() {
694                 try!(fmt_inner(f, context, submodule));
695             }
696             context.pop();
697             Ok(())
698         }
699
700         let mut context = Vec::new();
701
702         try!(write!(f,
703 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
704 This dashboard summarizes the stability levels for all of the public modules of
705 the crate, according to the total number of items at each level in the module and its children:
706 <blockquote>
707 <a class='stability Stable'></a> stable,<br/>
708 <a class='stability Unstable'></a> unstable,<br/>
709 <a class='stability Experimental'></a> experimental,<br/>
710 <a class='stability Deprecated'></a> deprecated,<br/>
711 <a class='stability Unmarked'></a> unmarked
712 </blockquote>
713 The counts do not include methods or trait
714 implementations that are visible only through a re-exported type.",
715 self.name));
716         try!(write!(f, "<table>"))
717         try!(fmt_inner(f, &mut context, self));
718         write!(f, "</table>")
719     }
720 }