]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/html/format.rs
auto merge of #13600 : brandonw/rust/master, r=brson
[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::io;
20 use std::local_data;
21 use std::strbuf::StrBuf;
22
23 use syntax::ast;
24 use syntax::ast_util;
25
26 use clean;
27 use html::item_type;
28 use html::item_type::ItemType;
29 use html::render;
30 use html::render::{cache_key, current_location_key};
31
32 /// Helper to render an optional visibility with a space after it (if the
33 /// visibility is preset)
34 pub struct VisSpace(pub Option<ast::Visibility>);
35 /// Similarly to VisSpace, this structure is used to render a function style with a
36 /// space after it.
37 pub struct FnStyleSpace(pub ast::FnStyle);
38 /// Wrapper struct for properly emitting a method declaration.
39 pub struct Method<'a>(pub &'a clean::SelfTy, pub &'a clean::FnDecl);
40
41 impl VisSpace {
42     pub fn get(&self) -> Option<ast::Visibility> {
43         let VisSpace(v) = *self; v
44     }
45 }
46
47 impl FnStyleSpace {
48     pub fn get(&self) -> ast::FnStyle {
49         let FnStyleSpace(v) = *self; v
50     }
51 }
52
53 impl fmt::Show for clean::Generics {
54     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55         if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) }
56         try!(f.buf.write("&lt;".as_bytes()));
57
58         for (i, life) in self.lifetimes.iter().enumerate() {
59             if i > 0 {
60                 try!(f.buf.write(", ".as_bytes()));
61             }
62             try!(write!(f.buf, "{}", *life));
63         }
64
65         if self.type_params.len() > 0 {
66             if self.lifetimes.len() > 0 {
67                 try!(f.buf.write(", ".as_bytes()));
68             }
69
70             for (i, tp) in self.type_params.iter().enumerate() {
71                 if i > 0 {
72                     try!(f.buf.write(", ".as_bytes()))
73                 }
74                 try!(f.buf.write(tp.name.as_bytes()));
75
76                 if tp.bounds.len() > 0 {
77                     try!(f.buf.write(": ".as_bytes()));
78                     for (i, bound) in tp.bounds.iter().enumerate() {
79                         if i > 0 {
80                             try!(f.buf.write(" + ".as_bytes()));
81                         }
82                         try!(write!(f.buf, "{}", *bound));
83                     }
84                 }
85             }
86         }
87         try!(f.buf.write("&gt;".as_bytes()));
88         Ok(())
89     }
90 }
91
92 impl fmt::Show for clean::Lifetime {
93     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94         try!(f.buf.write("'".as_bytes()));
95         try!(f.buf.write(self.get_ref().as_bytes()));
96         Ok(())
97     }
98 }
99
100 impl fmt::Show for clean::TyParamBound {
101     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102         match *self {
103             clean::RegionBound => {
104                 f.buf.write("'static".as_bytes())
105             }
106             clean::TraitBound(ref ty) => {
107                 write!(f.buf, "{}", *ty)
108             }
109         }
110     }
111 }
112
113 impl fmt::Show for clean::Path {
114     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115         if self.global {
116             try!(f.buf.write("::".as_bytes()))
117         }
118         for (i, seg) in self.segments.iter().enumerate() {
119             if i > 0 {
120                 try!(f.buf.write("::".as_bytes()))
121             }
122             try!(f.buf.write(seg.name.as_bytes()));
123
124             if seg.lifetimes.len() > 0 || seg.types.len() > 0 {
125                 try!(f.buf.write("&lt;".as_bytes()));
126                 let mut comma = false;
127                 for lifetime in seg.lifetimes.iter() {
128                     if comma {
129                         try!(f.buf.write(", ".as_bytes()));
130                     }
131                     comma = true;
132                     try!(write!(f.buf, "{}", *lifetime));
133                 }
134                 for ty in seg.types.iter() {
135                     if comma {
136                         try!(f.buf.write(", ".as_bytes()));
137                     }
138                     comma = true;
139                     try!(write!(f.buf, "{}", *ty));
140                 }
141                 try!(f.buf.write("&gt;".as_bytes()));
142             }
143         }
144         Ok(())
145     }
146 }
147
148 /// Used when rendering a `ResolvedPath` structure. This invokes the `path`
149 /// rendering function with the necessary arguments for linking to a local path.
150 fn resolved_path(w: &mut io::Writer, id: ast::NodeId, p: &clean::Path,
151                  print_all: bool) -> fmt::Result {
152     path(w, p, print_all,
153         |_cache, loc| { Some("../".repeat(loc.len())) },
154         |cache| {
155             match cache.paths.find(&id) {
156                 None => None,
157                 Some(&(ref fqp, shortty)) => Some((fqp.clone(), shortty))
158             }
159         })
160 }
161
162 /// Used when rendering an `ExternalPath` structure. Like `resolved_path` this
163 /// will invoke `path` with proper linking-style arguments.
164 fn external_path(w: &mut io::Writer, p: &clean::Path, print_all: bool,
165                  fqn: &[~str], kind: clean::TypeKind,
166                  krate: ast::CrateNum) -> fmt::Result {
167     path(w, p, print_all,
168         |cache, loc| {
169             match *cache.extern_locations.get(&krate) {
170                 render::Remote(ref s) => Some(s.clone()),
171                 render::Local => Some("../".repeat(loc.len())),
172                 render::Unknown => None,
173             }
174         },
175         |_cache| {
176             Some((Vec::from_slice(fqn), match kind {
177                 clean::TypeStruct => item_type::Struct,
178                 clean::TypeEnum => item_type::Enum,
179                 clean::TypeFunction => item_type::Function,
180                 clean::TypeTrait => item_type::Trait,
181             }))
182         })
183 }
184
185 fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool,
186         root: |&render::Cache, &[~str]| -> Option<~str>,
187         info: |&render::Cache| -> Option<(Vec<~str> , ItemType)>)
188     -> fmt::Result
189 {
190     // The generics will get written to both the title and link
191     let mut generics = StrBuf::new();
192     let last = path.segments.last().unwrap();
193     if last.lifetimes.len() > 0 || last.types.len() > 0 {
194         let mut counter = 0;
195         generics.push_str("&lt;");
196         for lifetime in last.lifetimes.iter() {
197             if counter > 0 { generics.push_str(", "); }
198             counter += 1;
199             generics.push_str(format!("{}", *lifetime));
200         }
201         for ty in last.types.iter() {
202             if counter > 0 { generics.push_str(", "); }
203             counter += 1;
204             generics.push_str(format!("{}", *ty));
205         }
206         generics.push_str("&gt;");
207     }
208
209     // Did someone say rightward-drift?
210     local_data::get(current_location_key, |loc| {
211         let loc = loc.unwrap();
212
213         local_data::get(cache_key, |cache| {
214             let cache = cache.unwrap();
215             let abs_root = root(&**cache, loc.as_slice());
216             let rel_root = match path.segments.get(0).name.as_slice() {
217                 "self" => Some(~"./"),
218                 _ => None,
219             };
220
221             if print_all {
222                 let amt = path.segments.len() - 1;
223                 match rel_root {
224                     Some(root) => {
225                         let mut root = StrBuf::from_str(root);
226                         for seg in path.segments.slice_to(amt).iter() {
227                             if "super" == seg.name || "self" == seg.name {
228                                 try!(write!(w, "{}::", seg.name));
229                             } else {
230                                 root.push_str(seg.name);
231                                 root.push_str("/");
232                                 try!(write!(w, "<a class='mod'
233                                                     href='{}index.html'>{}</a>::",
234                                               root.as_slice(),
235                                               seg.name));
236                             }
237                         }
238                     }
239                     None => {
240                         for seg in path.segments.slice_to(amt).iter() {
241                             try!(write!(w, "{}::", seg.name));
242                         }
243                     }
244                 }
245             }
246
247             match info(&**cache) {
248                 // This is a documented path, link to it!
249                 Some((ref fqp, shortty)) if abs_root.is_some() => {
250                     let mut url = StrBuf::from_str(abs_root.unwrap());
251                     let to_link = fqp.slice_to(fqp.len() - 1);
252                     for component in to_link.iter() {
253                         url.push_str(*component);
254                         url.push_str("/");
255                     }
256                     match shortty {
257                         item_type::Module => {
258                             url.push_str(*fqp.last().unwrap());
259                             url.push_str("/index.html");
260                         }
261                         _ => {
262                             url.push_str(shortty.to_static_str());
263                             url.push_str(".");
264                             url.push_str(*fqp.last().unwrap());
265                             url.push_str(".html");
266                         }
267                     }
268
269                     try!(write!(w, "<a class='{}' href='{}' title='{}'>{}</a>",
270                                   shortty, url, fqp.connect("::"), last.name));
271                 }
272
273                 _ => {
274                     try!(write!(w, "{}", last.name));
275                 }
276             }
277             try!(write!(w, "{}", generics.as_slice()));
278             Ok(())
279         })
280     })
281 }
282
283 /// Helper to render type parameters
284 fn tybounds(w: &mut io::Writer,
285             typarams: &Option<Vec<clean::TyParamBound> >) -> fmt::Result {
286     match *typarams {
287         Some(ref params) => {
288             try!(write!(w, ":"));
289             for (i, param) in params.iter().enumerate() {
290                 if i > 0 {
291                     try!(write!(w, " + "));
292                 }
293                 try!(write!(w, "{}", *param));
294             }
295             Ok(())
296         }
297         None => Ok(())
298     }
299 }
300
301 impl fmt::Show for clean::Type {
302     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303         match *self {
304             clean::TyParamBinder(id) | clean::Generic(id) => {
305                 local_data::get(cache_key, |cache| {
306                     let m = cache.unwrap();
307                     f.buf.write(m.typarams.get(&id).as_bytes())
308                 })
309             }
310             clean::ResolvedPath{id, typarams: ref tp, path: ref path} => {
311                 try!(resolved_path(f.buf, id, path, false));
312                 tybounds(f.buf, tp)
313             }
314             clean::ExternalPath{path: ref path, typarams: ref tp,
315                                 fqn: ref fqn, kind, krate} => {
316                 try!(external_path(f.buf, path, false, fqn.as_slice(), kind,
317                                      krate))
318                 tybounds(f.buf, tp)
319             }
320             clean::Self(..) => f.buf.write("Self".as_bytes()),
321             clean::Primitive(prim) => {
322                 let s = match prim {
323                     ast::TyInt(ast::TyI) => "int",
324                     ast::TyInt(ast::TyI8) => "i8",
325                     ast::TyInt(ast::TyI16) => "i16",
326                     ast::TyInt(ast::TyI32) => "i32",
327                     ast::TyInt(ast::TyI64) => "i64",
328                     ast::TyUint(ast::TyU) => "uint",
329                     ast::TyUint(ast::TyU8) => "u8",
330                     ast::TyUint(ast::TyU16) => "u16",
331                     ast::TyUint(ast::TyU32) => "u32",
332                     ast::TyUint(ast::TyU64) => "u64",
333                     ast::TyFloat(ast::TyF32) => "f32",
334                     ast::TyFloat(ast::TyF64) => "f64",
335                     ast::TyStr => "str",
336                     ast::TyBool => "bool",
337                     ast::TyChar => "char",
338                 };
339                 f.buf.write(s.as_bytes())
340             }
341             clean::Closure(ref decl, ref region) => {
342                 write!(f.buf, "{style}{lifetimes}|{args}|{bounds}\
343                                {arrow, select, yes{ -&gt; {ret}} other{}}",
344                        style = FnStyleSpace(decl.fn_style),
345                        lifetimes = if decl.lifetimes.len() == 0 {
346                            ~""
347                        } else {
348                            format!("&lt;{:#}&gt;", decl.lifetimes)
349                        },
350                        args = decl.decl.inputs,
351                        arrow = match decl.decl.output { clean::Unit => "no", _ => "yes" },
352                        ret = decl.decl.output,
353                        bounds = {
354                            let mut ret = StrBuf::new();
355                            match *region {
356                                Some(ref lt) => {
357                                    ret.push_str(format!(": {}", *lt));
358                                }
359                                None => {}
360                            }
361                            for bound in decl.bounds.iter() {
362                                 match *bound {
363                                     clean::RegionBound => {}
364                                     clean::TraitBound(ref t) => {
365                                         if ret.len() == 0 {
366                                             ret.push_str(": ");
367                                         } else {
368                                             ret.push_str(" + ");
369                                         }
370                                         ret.push_str(format!("{}", *t));
371                                     }
372                                 }
373                            }
374                            ret.into_owned()
375                        })
376             }
377             clean::Proc(ref decl) => {
378                 write!(f.buf, "{style}{lifetimes}proc({args}){bounds}\
379                                {arrow, select, yes{ -&gt; {ret}} other{}}",
380                        style = FnStyleSpace(decl.fn_style),
381                        lifetimes = if decl.lifetimes.len() == 0 {
382                            ~""
383                        } else {
384                            format!("&lt;{:#}&gt;", decl.lifetimes)
385                        },
386                        args = decl.decl.inputs,
387                        bounds = if decl.bounds.len() == 0 {
388                            ~""
389                        } else {
390                            let mut m = decl.bounds.iter().map(|s| s.to_str());
391                            ": " + m.collect::<~[~str]>().connect(" + ")
392                        },
393                        arrow = match decl.decl.output { clean::Unit => "no", _ => "yes" },
394                        ret = decl.decl.output)
395             }
396             clean::BareFunction(ref decl) => {
397                 write!(f.buf, "{}{}fn{}{}",
398                        FnStyleSpace(decl.fn_style),
399                        match decl.abi {
400                            ref x if "" == *x => ~"",
401                            ref x if "\"Rust\"" == *x => ~"",
402                            ref s => " " + *s + " ",
403                        },
404                        decl.generics,
405                        decl.decl)
406             }
407             clean::Tuple(ref typs) => {
408                 try!(f.buf.write("(".as_bytes()));
409                 for (i, typ) in typs.iter().enumerate() {
410                     if i > 0 {
411                         try!(f.buf.write(", ".as_bytes()))
412                     }
413                     try!(write!(f.buf, "{}", *typ));
414                 }
415                 f.buf.write(")".as_bytes())
416             }
417             clean::Vector(ref t) => write!(f.buf, "[{}]", **t),
418             clean::FixedVector(ref t, ref s) => {
419                 write!(f.buf, "[{}, ..{}]", **t, *s)
420             }
421             clean::String => f.buf.write("str".as_bytes()),
422             clean::Bool => f.buf.write("bool".as_bytes()),
423             clean::Unit => f.buf.write("()".as_bytes()),
424             clean::Bottom => f.buf.write("!".as_bytes()),
425             clean::Unique(ref t) => write!(f.buf, "~{}", **t),
426             clean::Managed(ref t) => write!(f.buf, "@{}", **t),
427             clean::RawPointer(m, ref t) => {
428                 write!(f.buf, "*{}{}",
429                        match m {
430                            clean::Mutable => "mut ",
431                            clean::Immutable => "",
432                        }, **t)
433             }
434             clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
435                 let lt = match *l { Some(ref l) => format!("{} ", *l), _ => ~"" };
436                 write!(f.buf, "&amp;{}{}{}",
437                        lt,
438                        match mutability {
439                            clean::Mutable => "mut ",
440                            clean::Immutable => "",
441                        },
442                        **ty)
443             }
444         }
445     }
446 }
447
448 impl fmt::Show for clean::Arguments {
449     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
450         for (i, input) in self.values.iter().enumerate() {
451             if i > 0 { try!(write!(f.buf, ", ")); }
452             if input.name.len() > 0 {
453                 try!(write!(f.buf, "{}: ", input.name));
454             }
455             try!(write!(f.buf, "{}", input.type_));
456         }
457         Ok(())
458     }
459 }
460
461 impl fmt::Show for clean::FnDecl {
462     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
463         write!(f.buf, "({args}){arrow, select, yes{ -&gt; {ret}} other{}}",
464                args = self.inputs,
465                arrow = match self.output { clean::Unit => "no", _ => "yes" },
466                ret = self.output)
467     }
468 }
469
470 impl<'a> fmt::Show for Method<'a> {
471     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472         let Method(selfty, d) = *self;
473         let mut args = StrBuf::new();
474         match *selfty {
475             clean::SelfStatic => {},
476             clean::SelfValue => args.push_str("self"),
477             clean::SelfOwned => args.push_str("~self"),
478             clean::SelfBorrowed(Some(ref lt), clean::Immutable) => {
479                 args.push_str(format!("&amp;{} self", *lt));
480             }
481             clean::SelfBorrowed(Some(ref lt), clean::Mutable) => {
482                 args.push_str(format!("&amp;{} mut self", *lt));
483             }
484             clean::SelfBorrowed(None, clean::Mutable) => {
485                 args.push_str("&amp;mut self");
486             }
487             clean::SelfBorrowed(None, clean::Immutable) => {
488                 args.push_str("&amp;self");
489             }
490         }
491         for (i, input) in d.inputs.values.iter().enumerate() {
492             if i > 0 || args.len() > 0 { args.push_str(", "); }
493             if input.name.len() > 0 {
494                 args.push_str(format!("{}: ", input.name));
495             }
496             args.push_str(format!("{}", input.type_));
497         }
498         write!(f.buf,
499                "({args}){arrow, select, yes{ -&gt; {ret}} other{}}",
500                args = args,
501                arrow = match d.output { clean::Unit => "no", _ => "yes" },
502                ret = d.output)
503     }
504 }
505
506 impl fmt::Show for VisSpace {
507     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
508         match self.get() {
509             Some(ast::Public) => write!(f.buf, "pub "),
510             Some(ast::Inherited) | None => Ok(())
511         }
512     }
513 }
514
515 impl fmt::Show for FnStyleSpace {
516     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
517         match self.get() {
518             ast::UnsafeFn => write!(f.buf, "unsafe "),
519             ast::ExternFn => write!(f.buf, "extern "),
520             ast::NormalFn => Ok(())
521         }
522     }
523 }
524
525 impl fmt::Show for clean::ViewPath {
526     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
527         match *self {
528             clean::SimpleImport(ref name, ref src) => {
529                 if *name == src.path.segments.last().unwrap().name {
530                     write!(f.buf, "use {};", *src)
531                 } else {
532                     write!(f.buf, "use {} = {};", *name, *src)
533                 }
534             }
535             clean::GlobImport(ref src) => {
536                 write!(f.buf, "use {}::*;", *src)
537             }
538             clean::ImportList(ref src, ref names) => {
539                 try!(write!(f.buf, "use {}::\\{", *src));
540                 for (i, n) in names.iter().enumerate() {
541                     if i > 0 {
542                         try!(write!(f.buf, ", "));
543                     }
544                     try!(write!(f.buf, "{}", *n));
545                 }
546                 write!(f.buf, "\\};")
547             }
548         }
549     }
550 }
551
552 impl fmt::Show for clean::ImportSource {
553     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554         match self.did {
555             // FIXME: shouldn't be restricted to just local imports
556             Some(did) if ast_util::is_local(did) => {
557                 resolved_path(f.buf, did.node, &self.path, true)
558             }
559             _ => {
560                 for (i, seg) in self.path.segments.iter().enumerate() {
561                     if i > 0 {
562                         try!(write!(f.buf, "::"))
563                     }
564                     try!(write!(f.buf, "{}", seg.name));
565                 }
566                 Ok(())
567             }
568         }
569     }
570 }
571
572 impl fmt::Show for clean::ViewListIdent {
573     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
574         match self.source {
575             // FIXME: shouldn't be limited to just local imports
576             Some(did) if ast_util::is_local(did) => {
577                 let path = clean::Path {
578                     global: false,
579                     segments: vec!(clean::PathSegment {
580                         name: self.name.clone(),
581                         lifetimes: Vec::new(),
582                         types: Vec::new(),
583                     })
584                 };
585                 resolved_path(f.buf, did.node, &path, false)
586             }
587             _ => write!(f.buf, "{}", self.name),
588         }
589     }
590 }