]> git.lizzy.rs Git - rust.git/blob - src/expr.rs
Use config file for constants
[rust.git] / src / expr.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 use visitor::FmtVisitor;
12 use utils::*;
13 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
14
15 use syntax::{ast, ptr};
16 use syntax::codemap::{Span, Pos};
17
18 use MIN_STRING;
19
20 impl<'a> FmtVisitor<'a> {
21     // TODO NEEDS TESTS
22     fn rewrite_string_lit(&mut self, s: &str, span: Span, width: usize, offset: usize) -> String {
23         // FIXME I bet this stomps unicode escapes in the source string
24
25         // Check if there is anything to fix: we always try to fixup multi-line
26         // strings, or if the string is too long for the line.
27         let l_loc = self.codemap.lookup_char_pos(span.lo);
28         let r_loc = self.codemap.lookup_char_pos(span.hi);
29         if l_loc.line == r_loc.line && r_loc.col.to_usize() <= config!(max_width) {
30             return self.snippet(span);
31         }
32
33         // TODO if lo.col > IDEAL - 10, start a new line (need cur indent for that)
34
35         let s = s.escape_default();
36
37         let offset = offset + 1;
38         let indent = make_indent(offset);
39         let indent = &indent;
40
41         let max_chars = width - 1;
42
43         let mut cur_start = 0;
44         let mut result = String::new();
45         result.push('"');
46         loop {
47             let mut cur_end = cur_start + max_chars;
48
49             if cur_end >= s.len() {
50                 result.push_str(&s[cur_start..]);
51                 break;
52             }
53
54             // Make sure we're on a char boundary.
55             cur_end = next_char(&s, cur_end);
56
57             // Push cur_end left until we reach whitespace
58             while !s.char_at(cur_end-1).is_whitespace() {
59                 cur_end = prev_char(&s, cur_end);
60
61                 if cur_end - cur_start < MIN_STRING {
62                     // We can't break at whitespace, fall back to splitting
63                     // anywhere that doesn't break an escape sequence
64                     cur_end = next_char(&s, cur_start + max_chars);
65                     while s.char_at(cur_end) == '\\' {
66                         cur_end = prev_char(&s, cur_end);
67                     }
68                 }
69             }
70             // Make sure there is no whitespace to the right of the break.
71             while cur_end < s.len() && s.char_at(cur_end).is_whitespace() {
72                 cur_end = next_char(&s, cur_end+1);
73             }
74             result.push_str(&s[cur_start..cur_end]);
75             result.push_str("\\\n");
76             result.push_str(indent);
77
78             cur_start = cur_end;
79         }
80         result.push('"');
81
82         result
83     }
84
85     fn rewrite_call(&mut self,
86                     callee: &ast::Expr,
87                     args: &[ptr::P<ast::Expr>],
88                     width: usize,
89                     offset: usize)
90         -> String
91     {
92         debug!("rewrite_call, width: {}, offset: {}", width, offset);
93
94         // TODO using byte lens instead of char lens (and probably all over the place too)
95         let callee_str = self.rewrite_expr(callee, width, offset);
96         debug!("rewrite_call, callee_str: `{}`", callee_str);
97         // 2 is for parens.
98         let remaining_width = width - callee_str.len() - 2;
99         let offset = callee_str.len() + 1 + offset;
100         let arg_count = args.len();
101
102         let args_str = if arg_count > 0 {
103             let args: Vec<_> = args.iter().map(|e| (self.rewrite_expr(e,
104                                                                       remaining_width,
105                                                                       offset), String::new())).collect();
106             // TODO move this into write_list
107             let tactics = if args.iter().any(|&(ref s, _)| s.contains('\n')) {
108                 ListTactic::Vertical
109             } else {
110                 ListTactic::HorizontalVertical
111             };
112             let fmt = ListFormatting {
113                 tactic: tactics,
114                 separator: ",",
115                 trailing_separator: SeparatorTactic::Never,
116                 indent: offset,
117                 h_width: remaining_width,
118                 v_width: remaining_width,
119             };
120             write_list(&args, &fmt)
121         } else {
122             String::new()
123         };
124
125         format!("{}({})", callee_str, args_str)
126     }
127
128     pub fn rewrite_expr(&mut self, expr: &ast::Expr, width: usize, offset: usize) -> String {
129         match expr.node {
130             ast::Expr_::ExprLit(ref l) => {
131                 match l.node {
132                     ast::Lit_::LitStr(ref is, _) => {
133                         let result = self.rewrite_string_lit(&is, l.span, width, offset);
134                         debug!("string lit: `{}`", result);
135                         return result;
136                     }
137                     _ => {}
138                 }
139             }
140             ast::Expr_::ExprCall(ref callee, ref args) => {
141                 return self.rewrite_call(callee, args, width, offset);
142             }
143             _ => {}
144         }
145
146         let result = self.snippet(expr.span);
147         result
148     }
149 }