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.get(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 format!("({:#})", typs).as_slice())
436 clean::Vector(ref t) => {
437 primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice())
439 clean::FixedVector(ref t, ref s) => {
440 primitive_link(f, clean::Slice,
441 format!("[{}, ..{}]", **t, *s).as_slice())
443 clean::Bottom => f.write("!".as_bytes()),
444 clean::Unique(ref t) => write!(f, "Box<{}>", **t),
445 clean::Managed(ref t) => write!(f, "Gc<{}>", **t),
446 clean::RawPointer(m, ref t) => {
447 write!(f, "*{}{}", RawMutableSpace(m), **t)
449 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
451 Some(ref l) => format!("{} ", *l),
454 write!(f, "&{}{}{}", lt, MutableSpace(mutability), **ty)
460 impl fmt::Show for clean::Arguments {
461 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
462 for (i, input) in self.values.iter().enumerate() {
463 if i > 0 { try!(write!(f, ", ")); }
464 if input.name.len() > 0 {
465 try!(write!(f, "{}: ", input.name));
467 try!(write!(f, "{}", input.type_));
473 impl fmt::Show for clean::FnDecl {
474 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
475 write!(f, "({args}){arrow}",
477 arrow = match self.output {
478 clean::Primitive(clean::Unit) => "".to_string(),
479 _ => format!(" -> {}", self.output),
484 impl<'a> fmt::Show for Method<'a> {
485 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
486 let Method(selfty, d) = *self;
487 let mut args = String::new();
489 clean::SelfStatic => {},
490 clean::SelfValue => args.push_str("self"),
491 clean::SelfOwned => args.push_str("~self"),
492 clean::SelfBorrowed(Some(ref lt), mtbl) => {
493 args.push_str(format!("&{} {}self", *lt,
494 MutableSpace(mtbl)).as_slice());
496 clean::SelfBorrowed(None, mtbl) => {
497 args.push_str(format!("&{}self",
498 MutableSpace(mtbl)).as_slice());
501 for (i, input) in d.inputs.values.iter().enumerate() {
502 if i > 0 || args.len() > 0 { args.push_str(", "); }
503 if input.name.len() > 0 {
504 args.push_str(format!("{}: ", input.name).as_slice());
506 args.push_str(format!("{}", input.type_).as_slice());
508 write!(f, "({args}){arrow}",
510 arrow = match d.output {
511 clean::Primitive(clean::Unit) => "".to_string(),
512 _ => format!(" -> {}", d.output),
517 impl fmt::Show for VisSpace {
518 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
520 Some(ast::Public) => write!(f, "pub "),
521 Some(ast::Inherited) | None => Ok(())
526 impl fmt::Show for FnStyleSpace {
527 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
529 ast::UnsafeFn => write!(f, "unsafe "),
530 ast::NormalFn => Ok(())
535 impl fmt::Show for clean::ViewPath {
536 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
538 clean::SimpleImport(ref name, ref src) => {
539 if *name == src.path.segments.last().unwrap().name {
540 write!(f, "use {};", *src)
542 write!(f, "use {} = {};", *name, *src)
545 clean::GlobImport(ref src) => {
546 write!(f, "use {}::*;", *src)
548 clean::ImportList(ref src, ref names) => {
549 try!(write!(f, "use {}::{{", *src));
550 for (i, n) in names.iter().enumerate() {
552 try!(write!(f, ", "));
554 try!(write!(f, "{}", *n));
562 impl fmt::Show for clean::ImportSource {
563 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
565 Some(did) => resolved_path(f, did, &self.path, true),
567 for (i, seg) in self.path.segments.iter().enumerate() {
569 try!(write!(f, "::"))
571 try!(write!(f, "{}", seg.name));
579 impl fmt::Show for clean::ViewListIdent {
580 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
583 let path = clean::Path {
585 segments: vec!(clean::PathSegment {
586 name: self.name.clone(),
587 lifetimes: Vec::new(),
591 resolved_path(f, did, &path, false)
593 _ => write!(f, "{}", self.name),
598 impl fmt::Show for MutableSpace {
599 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
601 MutableSpace(clean::Immutable) => Ok(()),
602 MutableSpace(clean::Mutable) => write!(f, "mut "),
607 impl fmt::Show for RawMutableSpace {
608 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
610 RawMutableSpace(clean::Immutable) => write!(f, "const "),
611 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
616 impl<'a> fmt::Show for Stability<'a> {
617 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
618 let Stability(stab) = *self;
620 Some(ref stability) => {
621 write!(f, "<a class='stability {lvl}' title='{reason}'>{lvl}</a>",
622 lvl = stability.level.to_string(),
623 reason = stability.text)
630 impl<'a> fmt::Show for ConciseStability<'a> {
631 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
632 let ConciseStability(stab) = *self;
634 Some(ref stability) => {
635 write!(f, "<a class='stability {lvl}' title='{lvl}{colon}{reason}'></a>",
636 lvl = stability.level.to_string(),
637 colon = if stability.text.len() > 0 { ": " } else { "" },
638 reason = stability.text)
641 write!(f, "<a class='stability Unmarked' title='No stability level'></a>")
647 impl fmt::Show for ModuleSummary {
648 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
649 fn fmt_inner<'a>(f: &mut fmt::Formatter,
650 context: &mut Vec<&'a str>,
651 m: &'a ModuleSummary)
654 let tot = cnt.total();
655 if tot == 0 { return Ok(()) }
657 context.push(m.name.as_slice());
658 let path = context.connect("::");
660 // the total width of each row's stability summary, in pixels
663 try!(write!(f, "<tr>"));
664 try!(write!(f, "<td class='summary'>\
665 <a class='summary' href='{}'>{}</a></td>",
666 Vec::from_slice(context.slice_from(1))
667 .append_one("index.html").connect("/"),
669 try!(write!(f, "<td>"));
670 try!(write!(f, "<span class='summary Stable' \
671 style='width: {}px; display: inline-block'> </span>",
672 (width * cnt.stable)/tot));
673 try!(write!(f, "<span class='summary Unstable' \
674 style='width: {}px; display: inline-block'> </span>",
675 (width * cnt.unstable)/tot));
676 try!(write!(f, "<span class='summary Experimental' \
677 style='width: {}px; display: inline-block'> </span>",
678 (width * cnt.experimental)/tot));
679 try!(write!(f, "<span class='summary Deprecated' \
680 style='width: {}px; display: inline-block'> </span>",
681 (width * cnt.deprecated)/tot));
682 try!(write!(f, "<span class='summary Unmarked' \
683 style='width: {}px; display: inline-block'> </span>",
684 (width * cnt.unmarked)/tot));
685 try!(write!(f, "</td></tr>"));
687 for submodule in m.submodules.iter() {
688 try!(fmt_inner(f, context, submodule));
694 let mut context = Vec::new();
697 r"<h1 class='fqn'>Stability dashboard: crate <a class='mod' href='index.html'>{}</a></h1>
698 This dashboard summarizes the stability levels for all of the public modules of
699 the crate, according to the total number of items at each level in the module and its children:
701 <a class='stability Stable'></a> stable,<br/>
702 <a class='stability Unstable'></a> unstable,<br/>
703 <a class='stability Experimental'></a> experimental,<br/>
704 <a class='stability Deprecated'></a> deprecated,<br/>
705 <a class='stability Unmarked'></a> unmarked
707 The counts do not include methods or trait
708 implementations that are visible only through a re-exported type.",
710 try!(write!(f, "<table>"))
711 try!(fmt_inner(f, &mut context, self));
712 write!(f, "</table>")