1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! HTML formatting module
13 //! This module contains a large number of `fmt::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.
19 use std::string::String;
25 use stability_summary::ModuleSummary;
27 use html::item_type::ItemType;
29 use html::render::{cache_key, current_location_key};
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
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>);
49 pub fn get(&self) -> Option<ast::Visibility> {
50 let VisSpace(v) = *self; v
55 pub fn get(&self) -> ast::FnStyle {
56 let FnStyleSpace(v) = *self; v
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("<".as_bytes()));
65 for (i, life) in self.lifetimes.iter().enumerate() {
67 try!(f.write(", ".as_bytes()));
69 try!(write!(f, "{}", *life));
72 if self.type_params.len() > 0 {
73 if self.lifetimes.len() > 0 {
74 try!(f.write(", ".as_bytes()));
77 for (i, tp) in self.type_params.iter().enumerate() {
79 try!(f.write(", ".as_bytes()))
81 try!(f.write(tp.name.as_bytes()));
83 if tp.bounds.len() > 0 {
84 try!(f.write(": ".as_bytes()));
85 for (i, bound) in tp.bounds.iter().enumerate() {
87 try!(f.write(" + ".as_bytes()));
89 try!(write!(f, "{}", *bound));
94 Some(ref ty) => { try!(write!(f, " = {}", ty)); },
99 try!(f.write(">".as_bytes()));
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()));
111 impl fmt::Show for clean::TyParamBound {
112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114 clean::RegionBound => {
115 f.write("'static".as_bytes())
117 clean::TraitBound(ref ty) => {
124 impl fmt::Show for clean::Path {
125 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 try!(f.write("::".as_bytes()))
130 for (i, seg) in self.segments.iter().enumerate() {
132 try!(f.write("::".as_bytes()))
134 try!(f.write(seg.name.as_bytes()));
136 if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
137 try!(f.write("<".as_bytes()));
138 let mut comma = false;
139 for lifetime in seg.lifetimes.iter() {
141 try!(f.write(", ".as_bytes()));
144 try!(write!(f, "{}", *lifetime));
146 for ty in seg.types.iter() {
148 try!(f.write(", ".as_bytes()));
151 try!(write!(f, "{}", *ty));
153 try!(f.write(">".as_bytes()));
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,
166 if ast_util::is_local(did) || cache.inlined.contains(&did) {
167 Some(("../".repeat(loc.len())).to_string())
169 match cache.extern_locations[did.krate] {
170 render::Remote(ref s) => Some(s.to_string()),
172 Some(("../".repeat(loc.len())).to_string())
174 render::Unknown => None,
179 match cache.paths.find(&did) {
181 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
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)>)
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("<");
197 for lifetime in last.lifetimes.iter() {
198 if counter > 0 { generics.push_str(", "); }
200 generics.push_str(format!("{}", *lifetime).as_slice());
202 for ty in last.types.iter() {
203 if counter > 0 { generics.push_str(", "); }
205 generics.push_str(format!("{}", *ty).as_slice());
207 generics.push_str(">");
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()),
219 let amt = path.segments.len() - 1;
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));
228 root.push_str(seg.name.as_slice());
230 try!(write!(w, "<a class='mod'
231 href='{}index.html'>{}</a>::",
238 for seg in path.segments.slice_to(amt).iter() {
239 try!(write!(w, "{}::", seg.name));
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());
255 item_type::Module => {
256 url.push_str(fqp.last().unwrap().as_slice());
257 url.push_str("/index.html");
260 url.push_str(shortty.to_static_str());
262 url.push_str(fqp.last().unwrap().as_slice());
263 url.push_str(".html");
267 try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
268 shortty, url, fqp.connect("::"), last.name));
272 try!(write!(w, "{}", last.name));
275 try!(write!(w, "{}", generics.as_slice()));
279 fn primitive_link(f: &mut fmt::Formatter,
280 prim: clean::PrimitiveType,
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'>",
291 needs_termination = true;
294 let path = &m.paths[ast::DefId {
296 node: ast::CRATE_NODE_ID,
298 let loc = match m.extern_locations[cnum] {
299 render::Remote(ref s) => Some(s.to_string()),
301 let loc = current_location_key.get().unwrap();
302 Some("../".repeat(loc.len()))
304 render::Unknown => None,
308 try!(write!(f, "<a href='{}{}/primitive.{}.html'>",
310 path.ref0().as_slice().head().unwrap(),
312 needs_termination = true;
319 try!(write!(f, "{}", name));
320 if needs_termination {
321 try!(write!(f, "</a>"));
326 /// Helper to render type parameters
327 fn tybounds(w: &mut fmt::Formatter,
328 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
330 Some(ref params) => {
331 for param in params.iter() {
332 try!(write!(w, " + "));
333 try!(write!(w, "{}", *param));
341 impl fmt::Show for clean::Type {
342 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
344 clean::TyParamBinder(id) => {
345 let m = cache_key.get().unwrap();
346 f.write(m.typarams[ast_util::local_def(id)].as_bytes())
348 clean::Generic(did) => {
349 let m = cache_key.get().unwrap();
350 f.write(m.typarams[did].as_bytes())
352 clean::ResolvedPath{ did, ref typarams, ref path } => {
353 try!(resolved_path(f, did, path, false));
354 tybounds(f, typarams)
356 clean::Self(..) => f.write("Self".as_bytes()),
357 clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
358 clean::Closure(ref decl) => {
359 write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}",
360 style = FnStyleSpace(decl.fn_style),
361 lifetimes = if decl.lifetimes.len() == 0 {
364 format!("<{:#}>", decl.lifetimes)
366 args = decl.decl.inputs,
367 arrow = match decl.decl.output {
368 clean::Primitive(clean::Unit) => "".to_string(),
369 _ => format!(" -> {}", decl.decl.output),
372 let mut ret = String::new();
373 for bound in decl.bounds.iter() {
375 clean::RegionBound => {}
376 clean::TraitBound(ref t) => {
382 ret.push_str(format!("{}",
390 clean::Proc(ref decl) => {
391 write!(f, "{style}{lifetimes}proc({args}){bounds}{arrow}",
392 style = FnStyleSpace(decl.fn_style),
393 lifetimes = if decl.lifetimes.len() == 0 {
396 format!("<{:#}>", decl.lifetimes)
398 args = decl.decl.inputs,
399 bounds = if decl.bounds.len() == 0 {
402 let mut m = decl.bounds
404 .map(|s| s.to_string());
407 m.collect::<Vec<String>>().connect(" + "))
409 arrow = match decl.decl.output {
410 clean::Primitive(clean::Unit) => "".to_string(),
411 _ => format!(" -> {}", decl.decl.output)
414 clean::BareFunction(ref decl) => {
415 write!(f, "{}{}fn{}{}",
416 FnStyleSpace(decl.fn_style),
417 match decl.abi.as_slice() {
418 "" => " extern ".to_string(),
419 "\"Rust\"" => "".to_string(),
420 s => format!(" extern {} ", s)
425 clean::Tuple(ref typs) => {
426 primitive_link(f, clean::PrimitiveTuple,
427 match typs.as_slice() {
428 [ref one] => format!("({},)", one),
429 many => format!("({:#})", many)
432 clean::Vector(ref t) => {
433 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
435 clean::FixedVector(ref t, ref s) => {
436 primitive_link(f, clean::Slice,
437 format!("[{}, ..{}]", **t, *s).as_slice())
439 clean::Bottom => f.write("!".as_bytes()),
440 clean::RawPointer(m, ref t) => {
441 write!(f, "*{}{}", RawMutableSpace(m), **t)
443 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
445 Some(ref l) => format!("{} ", *l),
448 write!(f, "&{}{}{}", lt, MutableSpace(mutability), **ty)
450 clean::Unique(..) | clean::Managed(..) => {
451 fail!("should have been cleaned")
457 impl fmt::Show for clean::Arguments {
458 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
459 for (i, input) in self.values.iter().enumerate() {
460 if i > 0 { try!(write!(f, ", ")); }
461 if input.name.len() > 0 {
462 try!(write!(f, "{}: ", input.name));
464 try!(write!(f, "{}", input.type_));
470 impl fmt::Show for clean::FnDecl {
471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 write!(f, "({args}){arrow}",
474 arrow = match self.output {
475 clean::Primitive(clean::Unit) => "".to_string(),
476 _ => format!(" -> {}", self.output),
481 impl<'a> fmt::Show for Method<'a> {
482 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
483 let Method(selfty, d) = *self;
484 let mut args = String::new();
486 clean::SelfStatic => {},
487 clean::SelfValue => args.push_str("self"),
488 clean::SelfBorrowed(Some(ref lt), mtbl) => {
489 args.push_str(format!("&{} {}self", *lt,
490 MutableSpace(mtbl)).as_slice());
492 clean::SelfBorrowed(None, mtbl) => {
493 args.push_str(format!("&{}self",
494 MutableSpace(mtbl)).as_slice());
496 clean::SelfExplicit(ref typ) => {
497 args.push_str(format!("self: {}", *typ).as_slice());
500 for (i, input) in d.inputs.values.iter().enumerate() {
501 if i > 0 || args.len() > 0 { args.push_str(", "); }
502 if input.name.len() > 0 {
503 args.push_str(format!("{}: ", input.name).as_slice());
505 args.push_str(format!("{}", input.type_).as_slice());
507 write!(f, "({args}){arrow}",
509 arrow = match d.output {
510 clean::Primitive(clean::Unit) => "".to_string(),
511 _ => format!(" -> {}", d.output),
516 impl fmt::Show for VisSpace {
517 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519 Some(ast::Public) => write!(f, "pub "),
520 Some(ast::Inherited) | None => Ok(())
525 impl fmt::Show for FnStyleSpace {
526 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528 ast::UnsafeFn => write!(f, "unsafe "),
529 ast::NormalFn => Ok(())
534 impl fmt::Show for clean::ViewPath {
535 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
537 clean::SimpleImport(ref name, ref src) => {
538 if *name == src.path.segments.last().unwrap().name {
539 write!(f, "use {};", *src)
541 write!(f, "use {} as {};", *src, *name)
544 clean::GlobImport(ref src) => {
545 write!(f, "use {}::*;", *src)
547 clean::ImportList(ref src, ref names) => {
548 try!(write!(f, "use {}::{{", *src));
549 for (i, n) in names.iter().enumerate() {
551 try!(write!(f, ", "));
553 try!(write!(f, "{}", *n));
561 impl fmt::Show for clean::ImportSource {
562 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
564 Some(did) => resolved_path(f, did, &self.path, true),
566 for (i, seg) in self.path.segments.iter().enumerate() {
568 try!(write!(f, "::"))
570 try!(write!(f, "{}", seg.name));
578 impl fmt::Show for clean::ViewListIdent {
579 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
582 let path = clean::Path {
584 segments: vec!(clean::PathSegment {
585 name: self.name.clone(),
586 lifetimes: Vec::new(),
590 resolved_path(f, did, &path, false)
592 _ => write!(f, "{}", self.name),
597 impl fmt::Show for MutableSpace {
598 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
600 MutableSpace(clean::Immutable) => Ok(()),
601 MutableSpace(clean::Mutable) => write!(f, "mut "),
606 impl fmt::Show for RawMutableSpace {
607 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
609 RawMutableSpace(clean::Immutable) => write!(f, "const "),
610 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
615 impl<'a> fmt::Show for Stability<'a> {
616 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
617 let Stability(stab) = *self;
619 Some(ref stability) => {
620 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
621 lvl = stability.level.to_string(),
622 reason = stability.text)
629 impl<'a> fmt::Show for ConciseStability<'a> {
630 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
631 let ConciseStability(stab) = *self;
633 Some(ref stability) => {
634 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
635 lvl = stability.level.to_string(),
636 colon = if stability.text.len() > 0 { ": " } else { "" },
637 reason = stability.text)
640 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
646 impl fmt::Show for ModuleSummary {
647 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
648 fn fmt_inner<'a>(f: &mut fmt::Formatter,
649 context: &mut Vec<&'a str>,
650 m: &'a ModuleSummary)
653 let tot = cnt.total();
654 if tot == 0 { return Ok(()) }
656 context.push(m.name.as_slice());
657 let path = context.connect("::");
659 try!(write!(f, "<tr>"));
660 try!(write!(f, "<td><a href='{}'>{}</a></td>",
661 Vec::from_slice(context.slice_from(1))
662 .append_one("index.html").connect("/"),
664 try!(write!(f, "<td class='summary-column'>"));
665 try!(write!(f, "<span class='summary Stable' \
666 style='width: {:.4}%; display: inline-block'> </span>",
667 (100 * cnt.stable) as f64/tot as f64));
668 try!(write!(f, "<span class='summary Unstable' \
669 style='width: {:.4}%; display: inline-block'> </span>",
670 (100 * cnt.unstable) as f64/tot as f64));
671 try!(write!(f, "<span class='summary Experimental' \
672 style='width: {:.4}%; display: inline-block'> </span>",
673 (100 * cnt.experimental) as f64/tot as f64));
674 try!(write!(f, "<span class='summary Deprecated' \
675 style='width: {:.4}%; display: inline-block'> </span>",
676 (100 * cnt.deprecated) as f64/tot as f64));
677 try!(write!(f, "<span class='summary Unmarked' \
678 style='width: {:.4}%; display: inline-block'> </span>",
679 (100 * cnt.unmarked) as f64/tot as f64));
680 try!(write!(f, "</td></tr>"));
682 for submodule in m.submodules.iter() {
683 try!(fmt_inner(f, context, submodule));
689 let mut context = Vec::new();
691 let tot = self.counts.total();
692 let (stable, unstable, experimental, deprecated, unmarked) = if tot == 0 {
695 ((100 * self.counts.stable)/tot,
696 (100 * self.counts.unstable)/tot,
697 (100 * self.counts.experimental)/tot,
698 (100 * self.counts.deprecated)/tot,
699 (100 * self.counts.unmarked)/tot)
703 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{name}</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
706 its children (percentages total for {name}):
708 <a class='stability Stable'></a> stable ({}%),<br/>
709 <a class='stability Unstable'></a> unstable ({}%),<br/>
710 <a class='stability Experimental'></a> experimental ({}%),<br/>
711 <a class='stability Deprecated'></a> deprecated ({}%),<br/>
712 <a class='stability Unmarked'></a> unmarked ({}%)
714 The counts do not include methods or trait
715 implementations that are visible only through a re-exported type.",
716 stable, unstable, experimental, deprecated, unmarked,
718 try!(write!(f, "<table>"))
719 try!(fmt_inner(f, &mut context, self));
720 write!(f, "</table>")