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.
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.
11 use {ReturnIndent, MAX_WIDTH, BraceStyle,
12 IDEAL_WIDTH, LEEWAY, FN_BRACE_STYLE, FN_RETURN_INDENT};
13 use utils::make_indent;
14 use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic};
15 use visitor::FmtVisitor;
16 use syntax::{ast, abi};
17 use syntax::print::pprust;
18 use syntax::parse::token;
20 impl<'a> FmtVisitor<'a> {
21 // TODO extract methods for args and generics
22 pub fn rewrite_fn(&mut self,
26 explicit_self: Option<&ast::ExplicitSelf>,
27 generics: &ast::Generics,
28 unsafety: &ast::Unsafety,
33 // FIXME we'll lose any comments in between parts of the function decl, but anyone
34 // who comments there probably deserves what they get.
36 let where_clause = &generics.where_clause;
37 let newline_brace = self.newline_for_brace(where_clause);
39 let mut result = String::with_capacity(1024);
41 if vis == ast::Visibility::Public {
42 result.push_str("pub ");
44 if let &ast::Unsafety::Unsafe = unsafety {
45 result.push_str("unsafe ");
47 if *abi != abi::Rust {
48 result.push_str("extern ");
49 result.push_str(&abi.to_string());
54 result.push_str("fn ");
55 result.push_str(&token::get_ident(ident));
58 // FIXME convert bounds to where clauses where they get too big or if
59 // there is a where clause at all.
60 let lifetimes: &[_] = &generics.lifetimes;
61 let tys: &[_] = &generics.ty_params;
62 if lifetimes.len() + tys.len() > 0 {
63 let budget = MAX_WIDTH - indent - result.len() - 2;
64 // TODO might need to insert a newline if the generics are really long
67 let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l));
68 let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty));
69 let generics_strs: Vec<_> = lt_strs.chain(ty_strs).map(|s| (s, String::new())).collect();
70 let fmt = ListFormatting {
71 tactic: ListTactic::HorizontalVertical,
73 trailing_separator: SeparatorTactic::Never,
74 indent: indent + result.len() + 1,
78 result.push_str(&write_list(&generics_strs, &fmt));
83 let ret_str = self.rewrite_return(&fd.output);
86 let args = &fd.inputs;
88 let mut budgets = None;
90 // Try keeping everything on the same line
91 if !result.contains("\n") {
92 // 3 = `() `, space is before ret_string
93 let mut used_space = indent + result.len() + 3 + ret_str.len();
97 let one_line_budget = if used_space > MAX_WIDTH {
100 MAX_WIDTH - used_space
103 let used_space = indent + result.len() + 2;
104 let max_space = IDEAL_WIDTH + LEEWAY;
105 if used_space < max_space {
106 budgets = Some((one_line_budget,
108 max_space - used_space,
109 indent + result.len() + 1));
113 // Didn't work. we must force vertical layout and put args on a newline.
114 if let None = budgets {
116 result.push_str(&make_indent(indent + 4));
117 // 6 = new indent + `()`
118 let used_space = indent + 6;
119 let max_space = IDEAL_WIDTH + LEEWAY;
120 if used_space > max_space {
122 // TODO take evasive action, perhaps kill the indent or something.
124 // 5 = new indent + `(`
125 budgets = Some((0, max_space - used_space, indent + 5));
129 let (one_line_budget, multi_line_budget, arg_indent) = budgets.unwrap();
132 let fmt = ListFormatting {
133 tactic: ListTactic::HorizontalVertical,
135 trailing_separator: SeparatorTactic::Never,
137 h_width: one_line_budget,
138 v_width: multi_line_budget,
141 let mut arg_strs: Vec<_> = args.iter().map(|a| (self.rewrite_fn_input(a), String::new())).collect();
142 // Account for sugary self.
143 if let Some(explicit_self) = explicit_self {
144 match explicit_self.node {
145 ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => {
146 let lt_str = match lt {
147 &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)),
148 &None => String::new(),
150 let mut_str = match m {
151 &ast::Mutability::MutMutable => "mut ".to_string(),
152 &ast::Mutability::MutImmutable => String::new(),
154 arg_strs[0].0 = format!("&{}{}self", lt_str, mut_str);
156 ast::ExplicitSelf_::SelfExplicit(ref ty, _) => {
157 arg_strs[0].0 = format!("self: {}", pprust::ty_to_string(ty));
159 ast::ExplicitSelf_::SelfValue(_) => {
160 arg_strs[0].0 = "self".to_string();
165 result.push_str(&write_list(&arg_strs, &fmt));
170 result.push_str(&self.rewrite_where_clause(where_clause, indent));
173 if ret_str.len() > 0 {
174 // If we've already gone multi-line, or the return type would push
175 // over the max width, then put the return type on a new line.
176 if result.contains("\n") ||
177 result.len() + indent + ret_str.len() > MAX_WIDTH {
178 let indent = match FN_RETURN_INDENT {
179 ReturnIndent::WithWhereClause => indent + 4,
180 // TODO we might want to check that using the arg indent doesn't
181 // blow our budget, and if it does, then fallback to the where
183 ReturnIndent::WithArgs => arg_indent,
187 result.push_str(&make_indent(indent));
191 result.push_str(&ret_str);
194 // Prepare for the function body by possibly adding a newline and indent.
195 // FIXME we'll miss anything between the end of the signature and the start
196 // of the body, but we need more spans from the compiler to solve this.
199 result.push_str(&make_indent(self.block_indent));
207 fn newline_for_brace(&self, where_clause: &ast::WhereClause) -> bool {
208 match FN_BRACE_STYLE {
209 BraceStyle::AlwaysNextLine => true,
210 BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true,
215 fn rewrite_where_clause(&self, where_clause: &ast::WhereClause, indent: usize) -> String {
216 let mut result = String::new();
217 if where_clause.predicates.len() == 0 {
222 result.push_str(&make_indent(indent + 4));
223 result.push_str("where ");
225 let budget = IDEAL_WIDTH + LEEWAY - indent - 10;
226 let fmt = ListFormatting {
227 tactic: ListTactic::Vertical,
229 trailing_separator: SeparatorTactic::Never,
234 let where_strs: Vec<_> = where_clause.predicates.iter().map(|p| (self.rewrite_pred(p), String::new())).collect();
235 result.push_str(&write_list(&where_strs, &fmt));
240 fn rewrite_return(&self, ret: &ast::FunctionRetTy) -> String {
242 ast::FunctionRetTy::DefaultReturn(_) => String::new(),
243 ast::FunctionRetTy::NoReturn(_) => "-> !".to_string(),
244 ast::FunctionRetTy::Return(ref ty) => "-> ".to_string() + &pprust::ty_to_string(ty),
248 // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly
249 fn rewrite_fn_input(&self, arg: &ast::Arg) -> String {
251 pprust::pat_to_string(&arg.pat),
252 pprust::ty_to_string(&arg.ty))