]> git.lizzy.rs Git - rust.git/blob - src/mod.rs
Use strings.rs rather than local modules
[rust.git] / src / mod.rs
1 // Copyright 2015 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 #![feature(box_syntax)]
12 #![feature(box_patterns)]
13 #![feature(rustc_private)]
14 #![feature(collections)]
15 #![feature(exit_status)]
16 #![feature(str_char)]
17
18 // TODO we're going to allocate a whole bunch of temp Strings, is it worth
19 // keeping some scratch mem for this and running our own StrPool?
20 // TODO for lint violations of names, emit a refactor script
21
22 // TODO priorities
23 // Fix fns and methods properly - need visibility in visit
24 // Writing output
25 // Working on multiple files, inclding empty ones
26 // Smoke testing till we can use it
27
28 #[macro_use]
29 extern crate log;
30
31 extern crate getopts;
32 extern crate rustc;
33 extern crate rustc_driver;
34 extern crate syntax;
35
36 extern crate strings;
37
38 use rustc::session::Session;
39 use rustc::session::config::{self, Input};
40 use rustc_driver::{driver, CompilerCalls, Compilation};
41
42 use syntax::{ast, ptr, abi};
43 use syntax::codemap::{self, CodeMap, Span, Pos, BytePos};
44 use syntax::diagnostics;
45 use syntax::parse::token;
46 use syntax::print::pprust;
47 use syntax::visit;
48
49 use std::path::PathBuf;
50
51 use changes::ChangeSet;
52
53 mod changes;
54
55 const IDEAL_WIDTH: usize = 80;
56 const LEEWAY: usize = 5;
57 const MAX_WIDTH: usize = 100;
58 const MIN_STRING: usize = 10;
59 const TAB_SPACES: usize = 4;
60
61 // Formatting which depends on the AST.
62 fn fmt_ast<'a>(krate: &ast::Crate, codemap: &'a CodeMap) -> ChangeSet<'a> {
63     let mut visitor = FmtVisitor::from_codemap(codemap);
64     visit::walk_crate(&mut visitor, krate);
65     let files = codemap.files.borrow();
66     if let Some(last) = files.last() {
67         visitor.format_missing(last.end_pos);
68     }
69
70     visitor.changes
71 }
72
73 // Formatting done on a char by char basis.
74 fn fmt_lines(changes: &mut ChangeSet) {
75     // Iterate over the chars in the change set.
76     for (f, text) in changes.text() {
77         let mut trims = vec![];
78         let mut last_wspace: Option<usize> = None;
79         let mut line_len = 0;
80         let mut cur_line = 1;
81         for (c, b) in text.chars() {
82             if c == '\n' { // TOOD test for \r too
83                 // Check for (and record) trailing whitespace.
84                 if let Some(lw) = last_wspace {
85                     trims.push((cur_line, lw, b));
86                     line_len -= b - lw;
87                 }
88                 // Check for any line width errors we couldn't correct.
89                 if line_len > MAX_WIDTH {
90                     // FIXME store the error rather than reporting immediately.
91                     println!("Rustfmt couldn't fix (sorry). {}:{}: line longer than {} characters",
92                              f, cur_line, MAX_WIDTH);
93                 }
94                 line_len = 0;
95                 cur_line += 1;
96                 last_wspace = None;
97             } else {
98                 line_len += 1;
99                 if c.is_whitespace() {
100                     if last_wspace.is_none() {
101                         last_wspace = Some(b);
102                     }
103                 } else {
104                     last_wspace = None;
105                 }
106             }
107         }
108
109         for &(l, _, _) in trims.iter() {
110             // FIXME store the error rather than reporting immediately.
111             println!("Rustfmt left trailing whitespace at {}:{} (sorry)", f, l);
112         }
113     }
114 }
115
116 struct FmtVisitor<'a> {
117     codemap: &'a CodeMap,
118     changes: ChangeSet<'a>,
119     last_pos: BytePos,
120     // TODO RAII util for indenting
121     block_indent: usize,
122 }
123
124 impl<'a, 'v> visit::Visitor<'v> for FmtVisitor<'a> {
125     fn visit_expr(&mut self, ex: &'v ast::Expr) {
126         // TODO uncomment
127         // debug!("visit_expr: {:?} {:?}",
128         //        self.codemap.lookup_char_pos(ex.span.lo),
129         //        self.codemap.lookup_char_pos(ex.span.hi));
130         self.format_missing(ex.span.lo);
131         let offset = self.changes.cur_offset_span(ex.span);
132         let new_str = self.rewrite_expr(ex, MAX_WIDTH - offset, offset);
133         self.changes.push_str_span(ex.span, &new_str);
134         self.last_pos = ex.span.hi;
135     }
136
137     fn visit_block(&mut self, b: &'v ast::Block) {
138         // TODO uncomment
139         // debug!("visit_block: {:?} {:?}",
140         //        self.codemap.lookup_char_pos(b.span.lo),
141         //        self.codemap.lookup_char_pos(b.span.hi));
142         self.format_missing(b.span.lo);
143
144         self.changes.push_str_span(b.span, "{");
145         self.last_pos = self.last_pos + BytePos(1);
146         self.block_indent += TAB_SPACES;
147
148         for stmt in &b.stmts {
149             self.format_missing_with_indent(stmt.span.lo);
150             self.visit_stmt(&stmt)
151         }
152         match b.expr {
153             Some(ref e) => {
154                 self.format_missing_with_indent(e.span.lo);
155                 self.visit_expr(e);
156             }
157             None => {}
158         }
159
160         self.block_indent -= TAB_SPACES;
161         // TODO we should compress any newlines here to just one
162         self.format_missing_with_indent(b.span.hi - BytePos(1));
163         self.changes.push_str_span(b.span, "}");
164         self.last_pos = b.span.hi;
165     }
166
167     fn visit_fn(&mut self,
168                 fk: visit::FnKind<'v>,
169                 fd: &'v ast::FnDecl,
170                 b: &'v ast::Block,
171                 s: Span,
172                 _: ast::NodeId) {
173         // TODO need to get the visibility from somewhere
174         self.format_missing(s.lo);
175         self.last_pos = s.lo;
176
177         // TODO need to check against expected indent
178         let indent = self.codemap.lookup_char_pos(s.lo).col.0;
179         match fk {
180             visit::FkItemFn(ident, ref generics, ref unsafety, ref abi) => {
181                 let new_fn = self.rewrite_fn(indent,
182                                              ident,
183                                              fd,
184                                              None,
185                                              generics,
186                                              unsafety,
187                                              abi,
188                                              ast::Visibility::Inherited);
189                 self.changes.push_str_span(s, &new_fn);
190             }
191             visit::FkMethod(ident, ref sig) => {
192                 let new_fn = self.rewrite_fn(indent,
193                                              ident,
194                                              fd,
195                                              Some(&sig.explicit_self),
196                                              &sig.generics,
197                                              &sig.unsafety,
198                                              &sig.abi,
199                                              ast::Visibility::Inherited);
200                 self.changes.push_str_span(s, &new_fn);
201             }
202             visit::FkFnBlock(..) => {}
203         }
204
205         // FIXME we'll miss anything between the end of the signature and the start
206         // of the body, but we need more spans from the compiler to solve this.
207         self.changes.push_str_span(s, "\n");
208         self.changes.push_str_span(s, &make_indent(self.block_indent));
209         self.last_pos = b.span.lo;
210         self.visit_block(b)
211     }
212
213     fn visit_item(&mut self, item: &'v ast::Item) {
214         match item.node {
215             ast::Item_::ItemUse(ref vp) => {
216                 match vp.node {
217                     ast::ViewPath_::ViewPathList(ref path, ref path_list) => {
218                         self.format_missing(item.span.lo);
219                         let new_str = self.rewrite_use_list(path, path_list, vp.span);
220                         self.changes.push_str_span(item.span, &new_str);
221                         self.last_pos = item.span.hi;
222                     }
223                     ast::ViewPath_::ViewPathGlob(_) => {
224                         // FIXME convert to list?
225                     }
226                     _ => {}
227                 }
228                 visit::walk_item(self, item);
229             }
230             ast::Item_::ItemImpl(..) => {
231                 self.block_indent += TAB_SPACES;
232                 visit::walk_item(self, item);
233                 self.block_indent -= TAB_SPACES;
234             }
235             _ => {
236                 visit::walk_item(self, item);
237             }
238         }
239     }
240
241     fn visit_mac(&mut self, mac: &'v ast::Mac) {
242         visit::walk_mac(self, mac)
243     }
244 }
245
246 fn make_indent(width: usize) -> String {
247     let mut indent = String::with_capacity(width);
248     for _ in 0..width {
249         indent.push(' ')
250     }
251     indent
252 }
253
254 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
255 enum ListTactic {
256     // One item per row.
257     Vertical,
258     // All items on one row.
259     Horizontal,
260     // Try Horizontal layout, if that fails then vertical
261     HorizontalVertical,
262     // Pack as many items as possible per row over (possibly) many rows.
263     Mixed,
264 }
265
266 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
267 enum SeparatorTactic {
268     Always,
269     Never,
270     Vertical,
271 }
272
273 struct ListFormatting<'a> {
274     tactic: ListTactic,
275     separator: &'a str,
276     trailing_separator: SeparatorTactic,
277     indent: usize,
278     // Available width if we layout horizontally.
279     h_width: usize,
280     // Available width if we layout vertically
281     v_width: usize,
282 }
283
284 // Format a list of strings into a string.
285 fn write_list<'b>(items:&[(String, String)], formatting: &ListFormatting<'b>) -> String {
286     if items.len() == 0 {
287         return String::new();
288     }
289
290     let mut tactic = formatting.tactic;
291
292     let h_width = formatting.h_width;
293     let v_width = formatting.v_width;
294     let sep_len = formatting.separator.len();
295
296     // Conservatively overestimates because of the changing separator tactic.
297     let sep_count = if formatting.trailing_separator != SeparatorTactic::Never {
298         items.len()
299     } else {
300         items.len() - 1
301     };
302
303     // TODO count dead space too.
304     let total_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l);
305
306     // Check if we need to fallback from horizontal listing, if possible.
307     if tactic == ListTactic::HorizontalVertical { 
308         if (total_width + (sep_len + 1) * sep_count) > h_width {
309             tactic = ListTactic::Vertical;
310         } else {
311             tactic = ListTactic::Horizontal;
312         }
313     }
314
315     // Now that we know how we will layout, we can decide for sure if there
316     // will be a trailing separator.
317     let trailing_separator = match formatting.trailing_separator {
318         SeparatorTactic::Always => true,
319         SeparatorTactic::Vertical => tactic == ListTactic::Vertical,
320         SeparatorTactic::Never => false,
321     };
322
323     // Create a buffer for the result.
324     // TODO could use a StringBuffer or rope for this
325     let alloc_width = if tactic == ListTactic::Horizontal {
326         total_width + (sep_len + 1) * sep_count
327     } else {
328         total_width + items.len() * (formatting.indent + 1)
329     };
330     let mut result = String::with_capacity(alloc_width);
331
332     let mut line_len = 0;
333     let indent_str = &make_indent(formatting.indent);
334     for (i, &(ref item, _)) in items.iter().enumerate() {
335         let first = i == 0;
336         let separate = i != items.len() - 1 || trailing_separator;
337
338         match tactic {
339             ListTactic::Horizontal if !first => {
340                 result.push(' ');
341             }
342             ListTactic::Vertical if !first => {
343                 result.push('\n');
344                 result.push_str(indent_str);
345             }
346             ListTactic::Mixed => {
347                 let mut item_width = item.len();
348                 if separate {
349                     item_width += sep_len;
350                 }
351
352                 if line_len > 0 && line_len + item_width > v_width {
353                     result.push('\n');
354                     result.push_str(indent_str);
355                     line_len = 0;
356                 }
357
358                 if line_len > 0 {
359                     result.push(' ');
360                     line_len += 1;
361                 }
362
363                 line_len += item_width;
364             }
365             _ => {}
366         }
367
368         result.push_str(item);
369         
370         if separate {
371             result.push_str(formatting.separator);
372         }
373         // TODO dead spans
374     }
375
376     result
377 }
378
379 impl<'a> FmtVisitor<'a> {
380     fn from_codemap<'b>(codemap: &'b CodeMap) -> FmtVisitor<'b> {
381         FmtVisitor {
382             codemap: codemap,
383             changes: ChangeSet::from_codemap(codemap),
384             last_pos: BytePos(0),
385             block_indent: 0,
386         }
387     }
388
389     // TODO these format_missing methods are ugly. Refactor and add unit tests
390     // for the central whitespace stripping loop.
391     fn format_missing(&mut self, end: BytePos) {
392         self.format_missing_inner(end, |this, last_snippet, span, _| {
393             this.changes.push_str_span(span, last_snippet)
394         })
395     }
396
397     fn format_missing_with_indent(&mut self, end: BytePos) {
398         self.format_missing_inner(end, |this, last_snippet, span, snippet| {
399             if last_snippet == snippet {
400                 // No new lines
401                 this.changes.push_str_span(span, last_snippet);
402                 this.changes.push_str_span(span, "\n");
403             } else {
404                 this.changes.push_str_span(span, last_snippet.trim_right());
405             }
406             let indent = make_indent(this.block_indent);
407             this.changes.push_str_span(span, &indent);
408         })
409     }
410
411     fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, Span, &str)>(&mut self,
412                                                                       end: BytePos,
413                                                                       process_last_snippet: F)
414     {
415         let start = self.last_pos;
416         // TODO uncomment
417         // debug!("format_missing_inner: {:?} to {:?}",
418         //        self.codemap.lookup_char_pos(start),
419         //        self.codemap.lookup_char_pos(end));
420
421         // TODO(#11) gets tricky if we're missing more than one file
422         // assert!(self.codemap.lookup_char_pos(start).file.name == self.codemap.lookup_char_pos(end).file.name,
423         //         "not implemented: unformated span across files: {} and {}",
424         //         self.codemap.lookup_char_pos(start).file.name,
425         //         self.codemap.lookup_char_pos(end).file.name);
426         // assert!(start <= end,
427         //         "Request to format inverted span: {:?} to {:?}",
428         //         self.codemap.lookup_char_pos(start),
429         //         self.codemap.lookup_char_pos(end));
430
431         if start == end {
432             return;
433         }
434
435         self.last_pos = end;
436         let span = codemap::mk_sp(start, end);
437         let snippet = self.snippet(span);
438
439         // Trim whitespace from the right hand side of each line.
440         // Annoyingly, the library functions for splitting by lines etc. are not
441         // quite right, so we must do it ourselves.
442         let mut line_start = 0;
443         let mut last_wspace = None;
444         for (i, c) in snippet.char_indices() {
445             if c == '\n' {
446                 if let Some(lw) = last_wspace {
447                     self.changes.push_str_span(span, &snippet[line_start..lw]);
448                     self.changes.push_str_span(span, "\n");
449                 } else {
450                     self.changes.push_str_span(span, &snippet[line_start..i+1]);
451                 }
452
453                 line_start = i + 1;
454                 last_wspace = None;
455             } else {
456                 if c.is_whitespace() {
457                     if last_wspace.is_none() {
458                         last_wspace = Some(i);
459                     }
460                 } else {
461                     last_wspace = None;
462                 }
463             }
464         }
465         process_last_snippet(self, &snippet[line_start..], span, &snippet);
466     }
467
468     fn snippet(&self, span: Span) -> String {
469         match self.codemap.span_to_snippet(span) {
470             Ok(s) => s,
471             Err(_) => {
472                 println!("Couldn't make snippet for span {:?}", span);
473                 "".to_string()
474             }
475         }
476     }
477
478     // TODO NEEDS TESTS
479     fn rewrite_string_lit(&mut self, s: &str, span: Span, width: usize, offset: usize) -> String {
480         // FIXME I bet this stomps unicode escapes in the source string
481
482         // Check if there is anything to fix: we always try to fixup multi-line
483         // strings, or if the string is too long for the line.
484         let l_loc = self.codemap.lookup_char_pos(span.lo);
485         let r_loc = self.codemap.lookup_char_pos(span.hi);
486         if l_loc.line == r_loc.line && r_loc.col.to_usize() <= MAX_WIDTH {
487             return self.snippet(span);
488         }
489
490         // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
491
492         let s = s.escape_default();
493
494         let offset = offset + 1;
495         let indent = make_indent(offset);
496         let indent = &indent;
497
498         let max_chars = width - 1;
499
500         let mut cur_start = 0;
501         let mut result = String::new();
502         result.push('"');
503         loop {
504             let mut cur_end = cur_start + max_chars;
505
506             if cur_end >= s.len() {
507                 result.push_str(&s[cur_start..]);
508                 break;
509             }
510
511             // Make sure we're on a char boundary.
512             cur_end = next_char(&s, cur_end);
513
514             // Push cur_end left until we reach whitespace
515             while !s.char_at(cur_end-1).is_whitespace() {
516                 cur_end = prev_char(&s, cur_end);
517
518                 if cur_end - cur_start < MIN_STRING {
519                     // We can't break at whitespace, fall back to splitting
520                     // anywhere that doesn't break an escape sequence
521                     cur_end = next_char(&s, cur_start + max_chars);
522                     while s.char_at(cur_end) == '\\' {
523                         cur_end = prev_char(&s, cur_end);
524                     }
525                 }
526             }
527             // Make sure there is no whitespace to the right of the break.
528             while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
529                 cur_end = next_char(&s, cur_end+1);
530             }
531             result.push_str(&s[cur_start..cur_end]);
532             result.push_str("\\\n");
533             result.push_str(indent);
534
535             cur_start = cur_end;
536         }
537         result.push('"');
538
539         result
540     }
541
542     // Basically just pretty prints a multi-item import.
543     fn rewrite_use_list(&mut self,
544                         path: &ast::Path,
545                         path_list: &[ast::PathListItem],
546                         vp_span: Span) -> String {
547         // FIXME remove unused imports
548
549         // FIXME check indentation
550         let l_loc = self.codemap.lookup_char_pos(vp_span.lo);
551
552         let path_str = pprust::path_to_string(&path);
553
554         // 3 = :: + {
555         let indent = l_loc.col.0 + path_str.len() + 3;
556         let fmt = ListFormatting {
557             tactic: ListTactic::Mixed,
558             separator: ",",
559             trailing_separator: SeparatorTactic::Never,
560             indent: indent,
561             // 2 = } + ;
562             h_width: IDEAL_WIDTH - (indent + path_str.len() + 2),
563             v_width: IDEAL_WIDTH - (indent + path_str.len() + 2),
564         };
565
566         // TODO handle any comments inbetween items.
567         // If `self` is in the list, put it first.
568         let head = if path_list.iter().any(|vpi|
569             if let ast::PathListItem_::PathListMod{ .. } = vpi.node {
570                 true
571             } else {
572                 false
573             }
574         ) {
575             Some(("self".to_string(), String::new()))
576         } else {
577             None
578         };
579
580         let items: Vec<_> = head.into_iter().chain(path_list.iter().filter_map(|vpi| {
581             match vpi.node {
582                 ast::PathListItem_::PathListIdent{ name, .. } => {
583                     Some((token::get_ident(name).to_string(), String::new()))
584                 }
585                 // Skip `self`, because we added it above.
586                 ast::PathListItem_::PathListMod{ .. } => None,
587             }
588         })).collect();
589
590         format!("use {}::{{{}}};", path_str, write_list(&items, &fmt))
591     }
592
593     fn rewrite_fn(&mut self,
594                   indent: usize,
595                   ident: ast::Ident,
596                   fd: &ast::FnDecl,
597                   explicit_self: Option<&ast::ExplicitSelf>,
598                   generics: &ast::Generics,
599                   unsafety: &ast::Unsafety,
600                   abi: &abi::Abi,
601                   vis: ast::Visibility)
602         -> String
603     {
604         // FIXME we'll lose any comments in between parts of the function decl, but anyone
605         // who comments there probably deserves what they get.
606
607         let mut result = String::with_capacity(1024);
608         // Vis unsafety abi.
609         if vis == ast::Visibility::Public {
610             result.push_str("pub ");
611         }
612         if let &ast::Unsafety::Unsafe = unsafety {
613             result.push_str("unsafe ");
614         }
615         if *abi != abi::Rust {
616             result.push_str("extern ");
617             result.push_str(&abi.to_string());
618             result.push(' ');
619         }
620
621         // fn foo
622         result.push_str("fn ");
623         result.push_str(&token::get_ident(ident));
624
625         // Generics.
626         // FIXME convert bounds to where clauses where they get too big or if
627         // there is a where clause at all.
628         let lifetimes: &[_] = &generics.lifetimes;
629         let tys: &[_] = &generics.ty_params;
630         let where_clause = &generics.where_clause;
631         if lifetimes.len() + tys.len() > 0 {
632             let budget = MAX_WIDTH - indent - result.len() - 2;
633             // TODO might need to insert a newline if the generics are really long
634             result.push('<');
635
636             let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
637             let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
638             let generics_strs: Vec<_> = lt_strs.chain(ty_strs).map(|s| (s, String::new())).collect();
639             let fmt = ListFormatting {
640                 tactic: ListTactic::HorizontalVertical,
641                 separator: ",",
642                 trailing_separator: SeparatorTactic::Never,
643                 indent: indent + result.len() + 1,
644                 h_width: budget,
645                 v_width: budget,
646             };
647             result.push_str(&write_list(&generics_strs, &fmt));
648
649             result.push('>');
650         }
651
652         let ret_str = match fd.output {
653             ast::FunctionRetTy::DefaultReturn(_) => String::new(),
654             ast::FunctionRetTy::NoReturn(_) => "-> !".to_string(),
655             ast::FunctionRetTy::Return(ref ty) => "-> ".to_string() + &pprust::ty_to_string(ty),
656         };
657
658         // Args.
659         let args = &fd.inputs;
660
661         let mut budgets = None;
662
663         // Try keeping everything on the same line
664         if !result.contains("\n") {
665             // 3 = `() `, space is before ret_string
666             let used_space = indent + result.len() + 3 + ret_str.len();
667             let one_line_budget = if used_space > MAX_WIDTH {
668                 0
669             } else {
670                 MAX_WIDTH - used_space
671             };
672
673             let used_space = indent + result.len() + 2;
674             let max_space = IDEAL_WIDTH + LEEWAY;
675             if used_space < max_space {
676                 budgets = Some((one_line_budget,
677                                 // 2 = `()`
678                                 max_space - used_space,
679                                 indent + result.len() + 1));
680             }
681         }
682
683         // Didn't work. we must force vertical layout and put args on a newline.
684         if let None = budgets {
685             result.push('\n');
686             result.push_str(&make_indent(indent + 4));
687             // 6 = new indent + `()`
688             let used_space = indent + 6;
689             let max_space = IDEAL_WIDTH + LEEWAY;
690             if used_space > max_space {
691                 // Whoops! bankrupt.
692                 // TODO take evasive action, perhaps kill the indent or something.
693             } else {
694                 // 5 = new indent + `(`
695                 budgets = Some((0, max_space - used_space, indent + 5));
696             }
697         }
698
699         let (one_line_budget, multi_line_budget, arg_indent) = budgets.unwrap();
700         result.push('(');
701
702         let fmt = ListFormatting {
703             tactic: ListTactic::HorizontalVertical,
704             separator: ",",
705             trailing_separator: SeparatorTactic::Never,
706             indent: arg_indent,
707             h_width: one_line_budget,
708             v_width: multi_line_budget,
709         };
710         // TODO dead spans
711         let mut arg_strs: Vec<_> = args.iter().map(|a| (self.rewrite_fn_input(a), String::new())).collect();
712         // Account for sugary self.
713         if let Some(explicit_self) = explicit_self {
714             match explicit_self.node {
715                 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
716                     let lt_str = match lt {
717                         &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
718                         &None => String::new(),
719                     };
720                     let mut_str = match m {
721                         &ast::Mutability::MutMutable => "mut ".to_string(),
722                         &ast::Mutability::MutImmutable => String::new(),
723                     };
724                     arg_strs[0].0 = format!("&{}{}self", lt_str, mut_str)
725                 }
726                 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
727                     arg_strs[0].0 = format!("self: {}", pprust::ty_to_string(ty))
728                 }
729                 _ => {}
730             }
731         }
732         result.push_str(&write_list(&arg_strs, &fmt));
733
734         result.push(')');
735
736         // Where clause.
737         if where_clause.predicates.len() > 0 {
738             result.push('\n');
739             result.push_str(&make_indent(indent + 4));
740             result.push_str("where ");
741
742             let budget = IDEAL_WIDTH + LEEWAY - indent - 10;
743             let fmt = ListFormatting {
744                 tactic: ListTactic::Vertical,
745                 separator: ",",
746                 trailing_separator: SeparatorTactic::Always,
747                 indent: indent + 10,
748                 h_width: budget,
749                 v_width: budget,
750             };
751             let where_strs: Vec<_> = where_clause.predicates.iter().map(|p| (self.rewrite_pred(p), String::new())).collect();
752             result.push_str(&write_list(&where_strs, &fmt));
753         }
754
755         // Return type.
756         if ret_str.len() > 0 {
757             // If we've already gone multi-line, or the return type would push
758             // over the max width, then put the return type on a new line.
759             if result.contains("\n") ||
760                result.len() + indent + ret_str.len() > MAX_WIDTH {
761                 let indent = indent + 4;
762                 result.push('\n');
763                 result.push_str(&make_indent(indent));
764             } else {
765                 result.push(' ');
766             }
767             result.push_str(&ret_str);
768         }
769
770         result
771     }
772
773     // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
774     fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
775         format!("{}: {}",
776                 pprust::pat_to_string(&arg.pat),
777                 pprust::ty_to_string(&arg.ty))
778     }
779
780     fn rewrite_pred(&self, predicate: &ast::WherePredicate) -> String
781     {
782         // TODO dead spans
783         // TODO assumes we'll always fit on one line...
784         match predicate {
785             &ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate{ref bound_lifetimes,
786                                                                           ref bounded_ty,
787                                                                           ref bounds,
788                                                                           ..}) => {
789                 if bound_lifetimes.len() > 0 {
790                     format!("for<{}> {}: {}",
791                             bound_lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)).collect::<Vec<_>>().connect(", "),
792                             pprust::ty_to_string(bounded_ty),
793                             bounds.iter().map(|b| self.rewrite_ty_bound(b)).collect::<Vec<_>>().connect("+"))
794
795                 } else {
796                     format!("{}: {}",
797                             pprust::ty_to_string(bounded_ty),
798                             bounds.iter().map(|b| self.rewrite_ty_bound(b)).collect::<Vec<_>>().connect("+"))
799                 }
800             }
801             &ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate{ref lifetime,
802                                                                             ref bounds,
803                                                                             ..}) => {
804                 format!("{}: {}",
805                         pprust::lifetime_to_string(lifetime),
806                         bounds.iter().map(|l| pprust::lifetime_to_string(l)).collect::<Vec<_>>().connect("+"))
807             }
808             &ast::WherePredicate::EqPredicate(ast::WhereEqPredicate{ref path, ref ty, ..}) => {
809                 format!("{} = {}", pprust::path_to_string(path), pprust::ty_to_string(ty))
810             }
811         }
812     }
813
814     fn rewrite_lifetime_def(&self, lifetime: &ast::LifetimeDef) -> String
815     {
816         if lifetime.bounds.len() == 0 {
817             return pprust::lifetime_to_string(&lifetime.lifetime);
818         }
819
820         format!("{}: {}",
821                 pprust::lifetime_to_string(&lifetime.lifetime),
822                 lifetime.bounds.iter().map(|l| pprust::lifetime_to_string(l)).collect::<Vec<_>>().connect("+"))
823     }
824
825     fn rewrite_ty_bound(&self, bound: &ast::TyParamBound) -> String
826     {
827         match *bound {
828             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => {
829                 self.rewrite_poly_trait_ref(tref)
830             }
831             ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::Maybe) => {
832                 format!("?{}", self.rewrite_poly_trait_ref(tref))
833             }
834             ast::TyParamBound::RegionTyParamBound(ref l) => {
835                 pprust::lifetime_to_string(l)
836             }
837         }
838     }
839
840     fn rewrite_ty_param(&self, ty_param: &ast::TyParam) -> String
841     {
842         let mut result = String::with_capacity(128);
843         result.push_str(&token::get_ident(ty_param.ident));
844         if ty_param.bounds.len() > 0 {
845             result.push_str(": ");
846             result.push_str(&ty_param.bounds.iter().map(|b| self.rewrite_ty_bound(b)).collect::<Vec<_>>().connect(", "));
847         }
848         if let Some(ref def) = ty_param.default {
849             result.push_str(" = ");
850             result.push_str(&pprust::ty_to_string(&def));
851         }
852
853         result
854     }
855
856     fn rewrite_poly_trait_ref(&self, t: &ast::PolyTraitRef) -> String
857     {
858         if t.bound_lifetimes.len() > 0 {
859             format!("for<{}> {}",
860                     t.bound_lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)).collect::<Vec<_>>().connect(", "),
861                     pprust::path_to_string(&t.trait_ref.path))
862
863         } else {
864             pprust::path_to_string(&t.trait_ref.path)
865         }
866     }
867
868     fn rewrite_call(&mut self,
869                     callee: &ast::Expr,
870                     args: &[ptr::P<ast::Expr>],
871                     width: usize,
872                     offset: usize)
873         -> String
874     {
875         debug!("rewrite_call, width: {}, offset: {}", width, offset);
876
877         // TODO using byte lens instead of char lens (and probably all over the place too)
878         let callee_str = self.rewrite_expr(callee, width, offset);
879         debug!("rewrite_call, callee_str: `{}`", callee_str);
880         // 2 is for parens.
881         let remaining_width = width - callee_str.len() - 2;
882         let offset = callee_str.len() + 1 + offset;
883         let arg_count = args.len();
884
885         let args_str = if arg_count > 0 {
886             let args: Vec<_> = args.iter().map(|e| (self.rewrite_expr(e,
887                                                                       remaining_width,
888                                                                       offset), String::new())).collect();
889             // TODO move this into write_list
890             let tactics = if args.iter().any(|&(ref s, _)| s.contains('\n')) {
891                 ListTactic::Vertical
892             } else {
893                 ListTactic::HorizontalVertical
894             };
895             let fmt = ListFormatting {
896                 tactic: tactics,
897                 separator: ",",
898                 trailing_separator: SeparatorTactic::Never,
899                 indent: offset,
900                 h_width: remaining_width,
901                 v_width: remaining_width,
902             };
903             write_list(&args, &fmt)
904         } else {
905             String::new()
906         };
907
908         format!("{}({})", callee_str, args_str)
909     }
910
911     fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) -> String {
912         match expr.node {
913             ast::Expr_::ExprLit(ref l) => {
914                 match l.node {
915                     ast::Lit_::LitStr(ref is, _) => {
916                         return self.rewrite_string_lit(&is, l.span, width, offset);
917                     }
918                     _ => {}
919                 }
920             }
921             ast::Expr_::ExprCall(ref callee, ref args) => {
922                 return self.rewrite_call(callee, args, width, offset);
923             }
924             _ => {}
925         }
926
927         let result = self.snippet(expr.span);
928         debug!("snippet: {}", result);
929         result
930     }
931 }
932
933 #[inline]
934 fn prev_char(s: &str, mut i: usize) -> usize {
935     if i == 0 { return 0; }
936
937     i -= 1;
938     while !s.is_char_boundary(i) {
939         i -= 1;
940     }
941     i
942 }
943
944 #[inline]
945 fn next_char(s: &str, mut i: usize) -> usize {
946     if i >= s.len() { return s.len(); }
947
948     while !s.is_char_boundary(i) {
949         i += 1;
950     }
951     i
952 }
953
954 struct RustFmtCalls {
955     input_path: Option<PathBuf>,
956 }
957
958 impl<'a> CompilerCalls<'a> for RustFmtCalls {
959     fn early_callback(&mut self,
960                       _: &getopts::Matches,
961                       _: &diagnostics::registry::Registry)
962                       -> Compilation {
963         Compilation::Continue
964     }
965
966     fn some_input(&mut self, input: Input, input_path: Option<PathBuf>) -> (Input, Option<PathBuf>) {
967         match input_path {
968             Some(ref ip) => self.input_path = Some(ip.clone()),
969             _ => {
970                 // FIXME should handle string input and write to stdout or something
971                 panic!("No input path");
972             }
973         }
974         (input, input_path)
975     }
976
977     fn no_input(&mut self,
978                 _: &getopts::Matches,
979                 _: &config::Options,
980                 _: &Option<PathBuf>,
981                 _: &Option<PathBuf>,
982                 _: &diagnostics::registry::Registry)
983                 -> Option<(Input, Option<PathBuf>)> {
984         panic!("No input supplied to RustFmt");
985     }
986
987     fn late_callback(&mut self,
988                      _: &getopts::Matches,
989                      _: &Session,
990                      _: &Input,
991                      _: &Option<PathBuf>,
992                      _: &Option<PathBuf>)
993                      -> Compilation {
994         Compilation::Continue
995     }
996
997     fn build_controller(&mut self, _: &Session) -> driver::CompileController<'a> {
998         let mut control = driver::CompileController::basic();
999         control.after_parse.stop = Compilation::Stop;
1000         control.after_parse.callback = box |state| {
1001             let krate = state.krate.unwrap();
1002             let codemap = state.session.codemap();
1003             let mut changes = fmt_ast(krate, codemap);
1004             fmt_lines(&mut changes);
1005
1006             println!("{}", changes);
1007             // FIXME(#5) Should be user specified whether to show or replace.
1008         };
1009
1010         control
1011     }
1012 }
1013
1014 fn main() {
1015     let args: Vec<_> = std::env::args().collect();
1016     let mut call_ctxt = RustFmtCalls { input_path: None };
1017     rustc_driver::run_compiler(&args, &mut call_ctxt);
1018     std::env::set_exit_status(0);
1019
1020     // TODO unit tests
1021     // let fmt = ListFormatting {
1022     //     tactic: ListTactic::Horizontal,
1023     //     separator: ",",
1024     //     trailing_separator: SeparatorTactic::Vertical,
1025     //     indent: 2,
1026     //     h_width: 80,
1027     //     v_width: 100,
1028     // };
1029     // let inputs = vec![(format!("foo"), String::new()),
1030     //                   (format!("foo"), String::new()),
1031     //                   (format!("foo"), String::new()),
1032     //                   (format!("foo"), String::new()),
1033     //                   (format!("foo"), String::new()),
1034     //                   (format!("foo"), String::new()),
1035     //                   (format!("foo"), String::new()),
1036     //                   (format!("foo"), String::new())];
1037     // let s = write_list(&inputs, &fmt);
1038     // println!("  {}", s);
1039 }
1040
1041 // FIXME comments
1042 // comments aren't in the AST, which makes processing them difficult, but then
1043 // comments are complicated anyway. I think I am happy putting off tackling them
1044 // for now. Long term the soluton is for comments to be in the AST, but that means
1045 // only the libsyntax AST, not the rustc one, which means waiting for the ASTs
1046 // to diverge one day....
1047
1048 // Once we do have comments, we just have to implement a simple word wrapping
1049 // algorithm to keep the width under IDEAL_WIDTH. We should also convert multiline
1050 // /* ... */ comments to // and check doc comments are in the right place and of
1051 // the right kind.
1052
1053 // Should also make sure comments have the right indent