]> git.lizzy.rs Git - rust.git/blob - src/functions.rs
Trivial reformatting
[rust.git] / src / functions.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 {FmtVisitor, ReturnIndent, make_indent, MAX_WIDTH, BraceStyle,
12      IDEAL_WIDTH, LEEWAY, FN_BRACE_STYLE, FN_RETURN_INDENT};
13 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
14 use syntax::{ast, abi};
15 use syntax::print::pprust;
16 use syntax::parse::token;
17
18 impl<'a> FmtVisitor<'a> {
19     pub fn rewrite_fn(&mut self,
20                       indent: usize,
21                       ident: ast::Ident,
22                       fd: &ast::FnDecl,
23                       explicit_self: Option<&ast::ExplicitSelf>,
24                       generics: &ast::Generics,
25                       unsafety: &ast::Unsafety,
26                       abi: &abi::Abi,
27                       vis: ast::Visibility)
28         -> String
29     {
30         // FIXME we'll lose any comments in between parts of the function decl, but anyone
31         // who comments there probably deserves what they get.
32
33         let where_clause = &generics.where_clause;
34         let newline_brace = match FN_BRACE_STYLE {
35             BraceStyle::AlwaysNextLine => true,
36             BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
37             _ => false,
38         };
39
40         let mut result = String::with_capacity(1024);
41         // Vis unsafety abi.
42         if vis == ast::Visibility::Public {
43             result.push_str("pub ");
44         }
45         if let &ast::Unsafety::Unsafe = unsafety {
46             result.push_str("unsafe ");
47         }
48         if *abi != abi::Rust {
49             result.push_str("extern ");
50             result.push_str(&abi.to_string());
51             result.push(' ');
52         }
53
54         // fn foo
55         result.push_str("fn ");
56         result.push_str(&token::get_ident(ident));
57
58         // Generics.
59         // FIXME convert bounds to where clauses where they get too big or if
60         // there is a where clause at all.
61         let lifetimes: &[_] = &generics.lifetimes;
62         let tys: &[_] = &generics.ty_params;
63         if lifetimes.len() + tys.len() > 0 {
64             let budget = MAX_WIDTH - indent - result.len() - 2;
65             // TODO might need to insert a newline if the generics are really long
66             result.push('<');
67
68             let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
69             let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
70             let generics_strs: Vec<_> = lt_strs.chain(ty_strs).map(|s| (s, String::new())).collect();
71             let fmt = ListFormatting {
72                 tactic: ListTactic::HorizontalVertical,
73                 separator: ",",
74                 trailing_separator: SeparatorTactic::Never,
75                 indent: indent + result.len() + 1,
76                 h_width: budget,
77                 v_width: budget,
78             };
79             result.push_str(&write_list(&generics_strs, &fmt));
80
81             result.push('>');
82         }
83
84         let ret_str = match fd.output {
85             ast::FunctionRetTy::DefaultReturn(_) => String::new(),
86             ast::FunctionRetTy::NoReturn(_) => "-> !".to_string(),
87             ast::FunctionRetTy::Return(ref ty) => "-> ".to_string() + &pprust::ty_to_string(ty),
88         };
89
90         // Args.
91         let args = &fd.inputs;
92
93         let mut budgets = None;
94
95         // Try keeping everything on the same line
96         if !result.contains("\n") {
97             // 3 = `() `, space is before ret_string
98             let mut used_space = indent + result.len() + 3 + ret_str.len();
99             if newline_brace {
100                 used_space += 2;
101             }
102             let one_line_budget = if used_space > MAX_WIDTH {
103                 0
104             } else {
105                 MAX_WIDTH - used_space
106             };
107
108             let used_space = indent + result.len() + 2;
109             let max_space = IDEAL_WIDTH + LEEWAY;
110             if used_space < max_space {
111                 budgets = Some((one_line_budget,
112                                 // 2 = `()`
113                                 max_space - used_space,
114                                 indent + result.len() + 1));
115             }
116         }
117
118         // Didn't work. we must force vertical layout and put args on a newline.
119         if let None = budgets {
120             result.push('\n');
121             result.push_str(&make_indent(indent + 4));
122             // 6 = new indent + `()`
123             let used_space = indent + 6;
124             let max_space = IDEAL_WIDTH + LEEWAY;
125             if used_space > max_space {
126                 // Whoops! bankrupt.
127                 // TODO take evasive action, perhaps kill the indent or something.
128             } else {
129                 // 5 = new indent + `(`
130                 budgets = Some((0, max_space - used_space, indent + 5));
131             }
132         }
133
134         let (one_line_budget, multi_line_budget, arg_indent) = budgets.unwrap();
135         result.push('(');
136
137         let fmt = ListFormatting {
138             tactic: ListTactic::HorizontalVertical,
139             separator: ",",
140             trailing_separator: SeparatorTactic::Never,
141             indent: arg_indent,
142             h_width: one_line_budget,
143             v_width: multi_line_budget,
144         };
145         // TODO dead spans
146         let mut arg_strs: Vec<_> = args.iter().map(|a| (self.rewrite_fn_input(a), String::new())).collect();
147         // Account for sugary self.
148         if let Some(explicit_self) = explicit_self {
149             match explicit_self.node {
150                 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
151                     let lt_str = match lt {
152                         &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
153                         &None => String::new(),
154                     };
155                     let mut_str = match m {
156                         &ast::Mutability::MutMutable => "mut ".to_string(),
157                         &ast::Mutability::MutImmutable => String::new(),
158                     };
159                     arg_strs[0].0 = format!("&{}{}self", lt_str, mut_str);
160                 }
161                 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
162                     arg_strs[0].0 = format!("self: {}", pprust::ty_to_string(ty));
163                 }
164                 ast::ExplicitSelf_::SelfValue(_) => {
165                     arg_strs[0].0 = "self".to_string();
166                 }
167                 _ => {}
168             }
169         }
170         result.push_str(&write_list(&arg_strs, &fmt));
171
172         result.push(')');
173
174         // Where clause.
175         if where_clause.predicates.len() > 0 {
176             result.push('\n');
177             result.push_str(&make_indent(indent + 4));
178             result.push_str("where ");
179
180             let budget = IDEAL_WIDTH + LEEWAY - indent - 10;
181             let fmt = ListFormatting {
182                 tactic: ListTactic::Vertical,
183                 separator: ",",
184                 trailing_separator: SeparatorTactic::Always,
185                 indent: indent + 10,
186                 h_width: budget,
187                 v_width: budget,
188             };
189             let where_strs: Vec<_> = where_clause.predicates.iter().map(|p| (self.rewrite_pred(p), String::new())).collect();
190             result.push_str(&write_list(&where_strs, &fmt));
191         }
192
193         // Return type.
194         if ret_str.len() > 0 {
195             // If we've already gone multi-line, or the return type would push
196             // over the max width, then put the return type on a new line.
197             if result.contains("\n") ||
198                result.len() + indent + ret_str.len() > MAX_WIDTH {
199                 let indent = match FN_RETURN_INDENT {
200                     ReturnIndent::WithWhereClause => indent + 4,
201                     // TODO we might want to check that using the arg indent doesn't
202                     // blow our budget, and if it does, then fallback to the where
203                     // clause indent.
204                     ReturnIndent::WithArgs => arg_indent,
205                 };
206
207                 result.push('\n');
208                 result.push_str(&make_indent(indent));
209             } else {
210                 result.push(' ');
211             }
212             result.push_str(&ret_str);
213         }
214
215         // Prepare for the function body by possibly adding a newline and indent.
216         // FIXME we'll miss anything between the end of the signature and the start
217         // of the body, but we need more spans from the compiler to solve this.
218         if newline_brace {
219             result.push('\n');
220             result.push_str(&make_indent(self.block_indent));
221         } else {
222             result.push(' ');
223         }
224
225         result
226     }
227
228     // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
229     fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
230         format!("{}: {}",
231                 pprust::pat_to_string(&arg.pat),
232                 pprust::ty_to_string(&arg.ty))
233     }
234 }