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