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.get(&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::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'>",
291 needs_termination = true;
294 let path = m.paths.get(&ast::DefId {
296 node: ast::CRATE_NODE_ID,
298 let loc = match *m.extern_locations.get(&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.get(&ast_util::local_def(id)).as_bytes())
348 clean::Generic(did) => {
349 let m = cache_key.get().unwrap();
350 f.write(m.typarams.get(&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, ref region) => {
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();
375 ret.push_str(format!(": {}",
380 for bound in decl.bounds.iter() {
382 clean::RegionBound => {}
383 clean::TraitBound(ref t) => {
389 ret.push_str(format!("{}",
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 {
403 format!("<{:#}>", decl.lifetimes)
405 args = decl.decl.inputs,
406 bounds = if decl.bounds.len() == 0 {
409 let mut m = decl.bounds
411 .map(|s| s.to_string());
414 m.collect::<Vec<String>>().connect(" + "))
416 arrow = match decl.decl.output {
417 clean::Primitive(clean::Unit) => "".to_string(),
418 _ => format!(" -> {}", decl.decl.output)
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)
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)
439 clean::Vector(ref t) => {
440 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
442 clean::FixedVector(ref t, ref s) => {
443 primitive_link(f, clean::Slice,
444 format!("[{}, ..{}]", **t, *s).as_slice())
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)
452 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
454 Some(ref l) => format!("{} ", *l),
457 write!(f, "&{}{}{}", lt, MutableSpace(mutability), **ty)
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));
470 try!(write!(f, "{}", input.type_));
476 impl fmt::Show for clean::FnDecl {
477 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478 write!(f, "({args}){arrow}",
480 arrow = match self.output {
481 clean::Primitive(clean::Unit) => "".to_string(),
482 _ => format!(" -> {}", self.output),
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();
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!("&{} {}self", *lt,
497 MutableSpace(mtbl)).as_slice());
499 clean::SelfBorrowed(None, mtbl) => {
500 args.push_str(format!("&{}self",
501 MutableSpace(mtbl)).as_slice());
503 clean::SelfExplicit(ref typ) => {
504 args.push_str(format!("self: {}", *typ).as_slice());
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());
512 args.push_str(format!("{}", input.type_).as_slice());
514 write!(f, "({args}){arrow}",
516 arrow = match d.output {
517 clean::Primitive(clean::Unit) => "".to_string(),
518 _ => format!(" -> {}", d.output),
523 impl fmt::Show for VisSpace {
524 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
526 Some(ast::Public) => write!(f, "pub "),
527 Some(ast::Inherited) | None => Ok(())
532 impl fmt::Show for FnStyleSpace {
533 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
535 ast::UnsafeFn => write!(f, "unsafe "),
536 ast::NormalFn => Ok(())
541 impl fmt::Show for clean::ViewPath {
542 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
544 clean::SimpleImport(ref name, ref src) => {
545 if *name == src.path.segments.last().unwrap().name {
546 write!(f, "use {};", *src)
548 write!(f, "use {} = {};", *name, *src)
551 clean::GlobImport(ref src) => {
552 write!(f, "use {}::*;", *src)
554 clean::ImportList(ref src, ref names) => {
555 try!(write!(f, "use {}::{{", *src));
556 for (i, n) in names.iter().enumerate() {
558 try!(write!(f, ", "));
560 try!(write!(f, "{}", *n));
568 impl fmt::Show for clean::ImportSource {
569 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
571 Some(did) => resolved_path(f, did, &self.path, true),
573 for (i, seg) in self.path.segments.iter().enumerate() {
575 try!(write!(f, "::"))
577 try!(write!(f, "{}", seg.name));
585 impl fmt::Show for clean::ViewListIdent {
586 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
589 let path = clean::Path {
591 segments: vec!(clean::PathSegment {
592 name: self.name.clone(),
593 lifetimes: Vec::new(),
597 resolved_path(f, did, &path, false)
599 _ => write!(f, "{}", self.name),
604 impl fmt::Show for MutableSpace {
605 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
607 MutableSpace(clean::Immutable) => Ok(()),
608 MutableSpace(clean::Mutable) => write!(f, "mut "),
613 impl fmt::Show for RawMutableSpace {
614 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
616 RawMutableSpace(clean::Immutable) => write!(f, "const "),
617 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
622 impl<'a> fmt::Show for Stability<'a> {
623 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
624 let Stability(stab) = *self;
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)
636 impl<'a> fmt::Show for ConciseStability<'a> {
637 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
638 let ConciseStability(stab) = *self;
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)
647 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
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)
660 let tot = cnt.total();
661 if tot == 0 { return Ok(()) }
663 context.push(m.name.as_slice());
664 let path = context.connect("::");
666 // the total width of each row's stability summary, in pixels
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("/"),
675 try!(write!(f, "<td>"));
676 try!(write!(f, "<span class='summary Stable' \
677 style='width: {}px; display: inline-block'> </span>",
678 (width * cnt.stable)/tot));
679 try!(write!(f, "<span class='summary Unstable' \
680 style='width: {}px; display: inline-block'> </span>",
681 (width * cnt.unstable)/tot));
682 try!(write!(f, "<span class='summary Experimental' \
683 style='width: {}px; display: inline-block'> </span>",
684 (width * cnt.experimental)/tot));
685 try!(write!(f, "<span class='summary Deprecated' \
686 style='width: {}px; display: inline-block'> </span>",
687 (width * cnt.deprecated)/tot));
688 try!(write!(f, "<span class='summary Unmarked' \
689 style='width: {}px; display: inline-block'> </span>",
690 (width * cnt.unmarked)/tot));
691 try!(write!(f, "</td></tr>"));
693 for submodule in m.submodules.iter() {
694 try!(fmt_inner(f, context, submodule));
700 let mut context = Vec::new();
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:
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
713 The counts do not include methods or trait
714 implementations that are visible only through a re-exported type.",
716 try!(write!(f, "<table>"))
717 try!(fmt_inner(f, &mut context, self));
718 write!(f, "</table>")