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