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::Display` 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
::iter
::repeat
;
26 use html
::item_type
::ItemType
;
28 use html
::render
::{cache, CURRENT_LOCATION_KEY}
;
30 /// Helper to render an optional visibility with a space after it (if the
31 /// visibility is preset)
32 #[derive(Copy, Clone)]
33 pub struct VisSpace(pub Option
<ast
::Visibility
>);
34 /// Similarly to VisSpace, this structure is used to render a function style with a
36 #[derive(Copy, Clone)]
37 pub struct UnsafetySpace(pub ast
::Unsafety
);
38 /// Similarly to VisSpace, this structure is used to render a function constness
39 /// with a space after it.
40 #[derive(Copy, Clone)]
41 pub struct ConstnessSpace(pub ast
::Constness
);
42 /// Wrapper struct for properly emitting a method declaration.
43 pub struct Method
<'a
>(pub &'a clean
::SelfTy
, pub &'a clean
::FnDecl
);
44 /// Similar to VisSpace, but used for mutability
45 #[derive(Copy, Clone)]
46 pub struct MutableSpace(pub clean
::Mutability
);
47 /// Similar to VisSpace, but used for mutability
48 #[derive(Copy, Clone)]
49 pub struct RawMutableSpace(pub clean
::Mutability
);
50 /// Wrapper struct for emitting a where clause from Generics.
51 pub struct WhereClause
<'a
>(pub &'a clean
::Generics
);
52 /// Wrapper struct for emitting type parameter bounds.
53 pub struct TyParamBounds
<'a
>(pub &'a
[clean
::TyParamBound
]);
54 /// Wrapper struct for emitting a comma-separated list of items
55 pub struct CommaSep
<'a
, T
: 'a
>(pub &'a
[T
]);
56 pub struct AbiSpace(pub Abi
);
59 pub fn get(&self) -> Option
<ast
::Visibility
> {
60 let VisSpace(v
) = *self; v
65 pub fn get(&self) -> ast
::Unsafety
{
66 let UnsafetySpace(v
) = *self; v
71 pub fn get(&self) -> ast
::Constness
{
72 let ConstnessSpace(v
) = *self; v
76 impl<'a
, T
: fmt
::Display
> fmt
::Display
for CommaSep
<'a
, T
> {
77 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
78 for (i
, item
) in self.0.iter
().enumerate() {
79 if i
!= 0 { try!(write!(f, ", ")); }
80 try
!(write
!(f
, "{}", item
));
86 impl<'a
> fmt
::Display
for TyParamBounds
<'a
> {
87 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
88 let &TyParamBounds(bounds
) = self;
89 for (i
, bound
) in bounds
.iter().enumerate() {
91 try
!(f
.write_str(" + "));
93 try
!(write
!(f
, "{}", *bound
));
99 impl fmt
::Display
for clean
::Generics
{
100 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
101 if self.lifetimes
.is_empty() && self.type_params
.is_empty() { return Ok(()) }
102 try
!(f
.write_str("<"));
104 for (i
, life
) in self.lifetimes
.iter().enumerate() {
106 try
!(f
.write_str(", "));
108 try
!(write
!(f
, "{}", *life
));
111 if !self.type_params
.is_empty() {
112 if !self.lifetimes
.is_empty() {
113 try
!(f
.write_str(", "));
115 for (i
, tp
) in self.type_params
.iter().enumerate() {
117 try
!(f
.write_str(", "))
119 try
!(f
.write_str(&tp
.name
));
121 if !tp
.bounds
.is_empty() {
122 try
!(write
!(f
, ": {}", TyParamBounds(&tp
.bounds
)));
126 Some(ref ty
) => { try!(write!(f, " = {}
", ty)); },
131 try!(f.write_str(">
;"));
136 impl<'a> fmt::Display for WhereClause<'a> {
137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
138 let &WhereClause(gens) = self;
139 if gens.where_predicates.is_empty() {
142 try!(f.write_str(" <span class
='
where'
>where "));
143 for (i, pred) in gens.where_predicates.iter().enumerate() {
145 try!(f.write_str(", "));
148 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
150 try!(write!(f, "{}
: {}
", ty, TyParamBounds(bounds)));
152 &clean::WherePredicate::RegionPredicate { ref lifetime,
154 try!(write!(f, "{}
: ", lifetime));
155 for (i, lifetime) in bounds.iter().enumerate() {
157 try!(f.write_str(" + "));
160 try!(write!(f, "{}
", lifetime));
163 &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
164 try!(write!(f, "{}
== {}
", lhs, rhs));
168 try!(f.write_str("</span
>"));
173 impl fmt::Display for clean::Lifetime {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 try!(f.write_str(self.get_ref()));
180 impl fmt::Display for clean::PolyTrait {
181 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
182 if !self.lifetimes.is_empty() {
183 try!(f.write_str("for<
;"));
184 for (i, lt) in self.lifetimes.iter().enumerate() {
186 try!(f.write_str(", "));
188 try!(write!(f, "{}
", lt));
190 try!(f.write_str(">
; "));
192 write!(f, "{}
", self.trait_)
196 impl fmt::Display for clean::TyParamBound {
197 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
199 clean::RegionBound(ref lt) => {
202 clean::TraitBound(ref ty, modifier) => {
203 let modifier_str = match modifier {
204 ast::TraitBoundModifier::None => "",
205 ast::TraitBoundModifier::Maybe => "?
",
207 write!(f, "{}{}
", modifier_str, *ty)
213 impl fmt::Display for clean::PathParameters {
214 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216 clean::PathParameters::AngleBracketed {
217 ref lifetimes, ref types, ref bindings
219 if !lifetimes.is_empty() || !types.is_empty() || !bindings.is_empty() {
220 try!(f.write_str("<
;"));
221 let mut comma = false;
222 for lifetime in lifetimes {
224 try!(f.write_str(", "));
227 try!(write!(f, "{}
", *lifetime));
231 try!(f.write_str(", "));
234 try!(write!(f, "{}
", *ty));
236 for binding in bindings {
238 try!(f.write_str(", "));
241 try!(write!(f, "{}
", *binding));
243 try!(f.write_str(">
;"));
246 clean::PathParameters::Parenthesized { ref inputs, ref output } => {
247 try!(f.write_str("("));
248 let mut comma = false;
251 try!(f.write_str(", "));
254 try!(write!(f, "{}
", *ty));
256 try!(f.write_str(")"));
257 if let Some(ref ty) = *output {
258 try!(f.write_str(" ->
; "));
259 try!(write!(f, "{}
", ty));
267 impl fmt::Display for clean::PathSegment {
268 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269 try!(f.write_str(&self.name));
270 write!(f, "{}
", self.params)
274 impl fmt::Display for clean::Path {
275 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 try!(f.write_str("::"))
280 for (i, seg) in self.segments.iter().enumerate() {
282 try!(f.write_str("::"))
284 try!(write!(f, "{}
", seg));
290 pub fn href(did: ast::DefId) -> Option<(String, ItemType, Vec<String>)> {
292 let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
293 let &(ref fqp, shortty) = match cache.paths.get(&did) {
297 let mut url = if ast_util::is_local(did) || cache.inlined.contains(&did) {
298 repeat("../").take(loc.len()).collect::<String>()
300 match cache.extern_locations[&did.krate] {
301 (_, render::Remote(ref s)) => s.to_string(),
302 (_, render::Local) => repeat("../").take(loc.len()).collect(),
303 (_, render::Unknown) => return None,
306 for component in &fqp[..fqp.len() - 1] {
307 url.push_str(component);
311 ItemType::Module => {
312 url.push_str(fqp.last().unwrap());
313 url.push_str("/index
.html
");
316 url.push_str(shortty.to_static_str());
318 url.push_str(fqp.last().unwrap());
319 url.push_str(".html
");
322 Some((url, shortty, fqp.to_vec()))
325 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
326 /// rendering function with the necessary arguments for linking to a local path.
327 fn resolved_path(w: &mut fmt::Formatter, did: ast::DefId, path: &clean::Path,
328 print_all: bool) -> fmt::Result {
329 let last = path.segments.last().unwrap();
330 let rel_root = match &*path.segments[0].name {
331 "self" => Some("./".to_string()),
336 let amt = path.segments.len() - 1;
339 for seg in &path.segments[..amt] {
340 if "super" == seg.name || "self" == seg.name {
341 try!(write!(w, "{}
::", seg.name));
343 root.push_str(&seg.name);
345 try!(write!(w, "<a class
='
mod'
346 href
='{}index
.html'
>{}
</a
>::",
353 for seg in &path.segments[..amt] {
354 try!(write!(w, "{}
::", seg.name));
361 Some((url, shortty, fqp)) => {
362 try!(write!(w, "<a class
='{}' href
='{}' title
='{}'
>{}
</a
>",
363 shortty, url, fqp.connect("::"), last.name));
365 _ => try!(write!(w, "{}
", last.name)),
367 try!(write!(w, "{}
", last.params));
371 fn primitive_link(f: &mut fmt::Formatter,
372 prim: clean::PrimitiveType,
373 name: &str) -> fmt::Result {
375 let mut needs_termination = false;
376 match m.primitive_locations.get(&prim) {
377 Some(&ast::LOCAL_CRATE) => {
378 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
379 let len = if len == 0 {0} else {len - 1};
380 try!(write!(f, "<a href
='{}primitive
.{}
.html'
>",
381 repeat("../").take(len).collect::<String>(),
383 needs_termination = true;
386 let path = &m.paths[&ast::DefId {
388 node: ast::CRATE_NODE_ID,
390 let loc = match m.extern_locations[&cnum] {
391 (_, render::Remote(ref s)) => Some(s.to_string()),
392 (_, render::Local) => {
393 let len = CURRENT_LOCATION_KEY.with(|s| s.borrow().len());
394 Some(repeat("../").take(len).collect::<String>())
396 (_, render::Unknown) => None,
400 try!(write!(f, "<a href
='{}{}
/primitive
.{}
.html'
>",
402 path.0.first().unwrap(),
404 needs_termination = true;
411 try!(write!(f, "{}
", name));
412 if needs_termination {
413 try!(write!(f, "</a
>"));
418 /// Helper to render type parameters
419 fn tybounds(w: &mut fmt::Formatter,
420 typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
422 Some(ref params) => {
423 for param in params {
424 try!(write!(w, " + "));
425 try!(write!(w, "{}
", *param));
433 impl fmt::Display for clean::Type {
434 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
436 clean::Generic(ref name) => {
439 clean::ResolvedPath{ did, ref typarams, ref path, is_generic } => {
440 // Paths like T::Output and Self::Output should be rendered with all segments
441 try!(resolved_path(f, did, path, is_generic));
442 tybounds(f, typarams)
444 clean::Infer => write!(f, "_
"),
445 clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
446 clean::BareFunction(ref decl) => {
447 write!(f, "{}{}
fn{}{}
",
448 UnsafetySpace(decl.unsafety),
450 "" => " extern ".to_string(),
451 "\"Rust
\"" => "".to_string(),
452 s => format!(" extern {}
", s)
457 clean::Tuple(ref typs) => {
458 primitive_link(f, clean::PrimitiveTuple,
460 [ref one] => format!("({}
,)", one),
461 many => format!("({}
)", CommaSep(&many)),
464 clean::Vector(ref t) => {
465 primitive_link(f, clean::Slice, &format!("[{}
]", **t))
467 clean::FixedVector(ref t, ref s) => {
468 primitive_link(f, clean::PrimitiveType::Array,
469 &format!("[{}
; {}
]", **t, *s))
471 clean::Bottom => f.write_str("!"),
472 clean::RawPointer(m, ref t) => {
473 primitive_link(f, clean::PrimitiveType::PrimitiveRawPointer,
474 &format!("*{}{}
", RawMutableSpace(m), **t))
476 clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
478 Some(ref l) => format!("{}
", *l),
481 let m = MutableSpace(mutability);
483 clean::Vector(ref bt) => { // BorrowedRef{ ... Vector(T) } is &[T]
486 primitive_link(f, clean::Slice,
487 &format!("&
;{}{}
[{}
]", lt, m, **bt)),
489 try!(primitive_link(f, clean::Slice,
490 &format!("&
;{}{}
[", lt, m)));
491 try!(write!(f, "{}
", **bt));
492 primitive_link(f, clean::Slice, "]")
497 write!(f, "&
;{}{}{}
", lt, m, **ty)
501 clean::PolyTraitRef(ref bounds) => {
502 for (i, bound) in bounds.iter().enumerate() {
504 try!(write!(f, " + "));
506 try!(write!(f, "{}
", *bound));
510 // It's pretty unsightly to look at `<A as B>::C` in output, and
511 // we've got hyperlinking on our side, so try to avoid longer
512 // notation as much as possible by making `C` a hyperlink to trait
513 // `B` to disambiguate.
515 // FIXME: this is still a lossy conversion and there should probably
516 // be a better way of representing this in general? Most of
517 // the ugliness comes from inlining across crates where
518 // everything comes in as a fully resolved QPath (hard to
523 trait_: box clean::ResolvedPath { did, ref typarams, .. },
525 try!(write!(f, "{}
::", self_type));
526 let path = clean::Path::singleton(name.clone());
527 try!(resolved_path(f, did, &path, false));
529 // FIXME: `typarams` are not rendered, and this seems bad?
533 clean::QPath { ref name, ref self_type, ref trait_ } => {
534 write!(f, "<
;{}
as {}
>
;::{}
", self_type, trait_, name)
536 clean::Unique(..) => {
537 panic!("should have been cleaned
")
543 impl fmt::Display for clean::Arguments {
544 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
545 for (i, input) in self.values.iter().enumerate() {
546 if i > 0 { try!(write!(f, ", ")); }
547 if !input.name.is_empty() {
548 try!(write!(f, "{}
: ", input.name));
550 try!(write!(f, "{}
", input.type_));
556 impl fmt::Display for clean::FunctionRetTy {
557 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
559 clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
560 clean::Return(ref ty) => write!(f, " ->
; {}
", ty),
561 clean::DefaultReturn => Ok(()),
562 clean::NoReturn => write!(f, " ->
; !")
567 impl fmt::Display for clean::FnDecl {
568 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
569 write!(f, "({args}
){arrow}
", args = self.inputs, arrow = self.output)
573 impl<'a> fmt::Display for Method<'a> {
574 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
575 let Method(selfty, d) = *self;
576 let mut args = String::new();
578 clean::SelfStatic => {},
579 clean::SelfValue => args.push_str("self"),
580 clean::SelfBorrowed(Some(ref lt), mtbl) => {
581 args.push_str(&format!("&
;{} {}
self", *lt, MutableSpace(mtbl)));
583 clean::SelfBorrowed(None, mtbl) => {
584 args.push_str(&format!("&
;{}
self", MutableSpace(mtbl)));
586 clean::SelfExplicit(ref typ) => {
587 args.push_str(&format!("self: {}
", *typ));
590 for (i, input) in d.inputs.values.iter().enumerate() {
591 if i > 0 || !args.is_empty() { args.push_str(", "); }
592 if !input.name.is_empty() {
593 args.push_str(&format!("{}
: ", input.name));
595 args.push_str(&format!("{}
", input.type_));
597 write!(f, "({args}
){arrow}
", args = args, arrow = d.output)
601 impl fmt::Display for VisSpace {
602 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604 Some(ast::Public) => write!(f, "pub "),
605 Some(ast::Inherited) | None => Ok(())
610 impl fmt::Display for UnsafetySpace {
611 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
613 ast::Unsafety::Unsafe => write!(f, "unsafe "),
614 ast::Unsafety::Normal => Ok(())
619 impl fmt::Display for ConstnessSpace {
620 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
622 ast::Constness::Const => write!(f, "const "),
623 ast::Constness::NotConst => Ok(())
628 impl fmt::Display for clean::Import {
629 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
631 clean::SimpleImport(ref name, ref src) => {
632 if *name == src.path.segments.last().unwrap().name {
633 write!(f, "use {}
;", *src)
635 write!(f, "use {}
as {}
;", *src, *name)
638 clean::GlobImport(ref src) => {
639 write!(f, "use {}
::*;", *src)
641 clean::ImportList(ref src, ref names) => {
642 try!(write!(f, "use {}
::{{", *src));
643 for (i, n) in names.iter().enumerate() {
645 try!(write!(f, ", "));
647 try!(write!(f, "{}
", *n));
655 impl fmt::Display for clean::ImportSource {
656 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
658 Some(did) => resolved_path(f, did, &self.path, true),
660 for (i, seg) in self.path.segments.iter().enumerate() {
662 try!(write!(f, "::"))
664 try!(write!(f, "{}
", seg.name));
672 impl fmt::Display for clean::ViewListIdent {
673 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
676 let path = clean::Path::singleton(self.name.clone());
677 resolved_path(f, did, &path, false)
679 _ => write!(f, "{}
", self.name),
684 impl fmt::Display for clean::TypeBinding {
685 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
686 write!(f, "{}
={}
", self.name, self.ty)
690 impl fmt::Display for MutableSpace {
691 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
693 MutableSpace(clean::Immutable) => Ok(()),
694 MutableSpace(clean::Mutable) => write!(f, "mut "),
699 impl fmt::Display for RawMutableSpace {
700 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702 RawMutableSpace(clean::Immutable) => write!(f, "const "),
703 RawMutableSpace(clean::Mutable) => write!(f, "mut "),
708 impl fmt::Display for AbiSpace {
709 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
712 Abi::C => write!(f, "extern "),
713 abi => write!(f, "extern {}
", abi),