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