diff --git a/src/functions.rs b/src/functions.rs new file mode 100644 index 000000000000..f1a9c3954705 --- /dev/null +++ b/src/functions.rs @@ -0,0 +1,234 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use {FmtVisitor, ReturnIndent, make_indent, MAX_WIDTH, BraceStyle, + IDEAL_WIDTH, LEEWAY, FN_BRACE_STYLE, FN_RETURN_INDENT}; +use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic}; +use syntax::{ast, abi}; +use syntax::print::pprust; +use syntax::parse::token; + +impl<'a> FmtVisitor<'a> { + pub fn rewrite_fn(&mut self, + indent: usize, + ident: ast::Ident, + fd: &ast::FnDecl, + explicit_self: Option<&ast::ExplicitSelf>, + generics: &ast::Generics, + unsafety: &ast::Unsafety, + abi: &abi::Abi, + vis: ast::Visibility) + -> String + { + // FIXME we'll lose any comments in between parts of the function decl, but anyone + // who comments there probably deserves what they get. + + let where_clause = &generics.where_clause; + let newline_brace = match FN_BRACE_STYLE { + BraceStyle::AlwaysNextLine => true, + BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true, + _ => false, + }; + + let mut result = String::with_capacity(1024); + // Vis unsafety abi. + if vis == ast::Visibility::Public { + result.push_str("pub "); + } + if let &ast::Unsafety::Unsafe = unsafety { + result.push_str("unsafe "); + } + if *abi != abi::Rust { + result.push_str("extern "); + result.push_str(&abi.to_string()); + result.push(' '); + } + + // fn foo + result.push_str("fn "); + result.push_str(&token::get_ident(ident)); + + // Generics. + // FIXME convert bounds to where clauses where they get too big or if + // there is a where clause at all. + let lifetimes: &[_] = &generics.lifetimes; + let tys: &[_] = &generics.ty_params; + if lifetimes.len() + tys.len() > 0 { + let budget = MAX_WIDTH - indent - result.len() - 2; + // TODO might need to insert a newline if the generics are really long + result.push('<'); + + let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)); + let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty)); + let generics_strs: Vec<_> = lt_strs.chain(ty_strs).map(|s| (s, String::new())).collect(); + let fmt = ListFormatting { + tactic: ListTactic::HorizontalVertical, + separator: ",", + trailing_separator: SeparatorTactic::Never, + indent: indent + result.len() + 1, + h_width: budget, + v_width: budget, + }; + result.push_str(&write_list(&generics_strs, &fmt)); + + result.push('>'); + } + + let ret_str = match fd.output { + ast::FunctionRetTy::DefaultReturn(_) => String::new(), + ast::FunctionRetTy::NoReturn(_) => "-> !".to_string(), + ast::FunctionRetTy::Return(ref ty) => "-> ".to_string() + &pprust::ty_to_string(ty), + }; + + // Args. + let args = &fd.inputs; + + let mut budgets = None; + + // Try keeping everything on the same line + if !result.contains("\n") { + // 3 = `() `, space is before ret_string + let mut used_space = indent + result.len() + 3 + ret_str.len(); + if newline_brace { + used_space += 2; + } + let one_line_budget = if used_space > MAX_WIDTH { + 0 + } else { + MAX_WIDTH - used_space + }; + + let used_space = indent + result.len() + 2; + let max_space = IDEAL_WIDTH + LEEWAY; + if used_space < max_space { + budgets = Some((one_line_budget, + // 2 = `()` + max_space - used_space, + indent + result.len() + 1)); + } + } + + // Didn't work. we must force vertical layout and put args on a newline. + if let None = budgets { + result.push('\n'); + result.push_str(&make_indent(indent + 4)); + // 6 = new indent + `()` + let used_space = indent + 6; + let max_space = IDEAL_WIDTH + LEEWAY; + if used_space > max_space { + // Whoops! bankrupt. + // TODO take evasive action, perhaps kill the indent or something. + } else { + // 5 = new indent + `(` + budgets = Some((0, max_space - used_space, indent + 5)); + } + } + + let (one_line_budget, multi_line_budget, arg_indent) = budgets.unwrap(); + result.push('('); + + let fmt = ListFormatting { + tactic: ListTactic::HorizontalVertical, + separator: ",", + trailing_separator: SeparatorTactic::Never, + indent: arg_indent, + h_width: one_line_budget, + v_width: multi_line_budget, + }; + // TODO dead spans + let mut arg_strs: Vec<_> = args.iter().map(|a| (self.rewrite_fn_input(a), String::new())).collect(); + // Account for sugary self. + if let Some(explicit_self) = explicit_self { + match explicit_self.node { + ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => { + let lt_str = match lt { + &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)), + &None => String::new(), + }; + let mut_str = match m { + &ast::Mutability::MutMutable => "mut ".to_string(), + &ast::Mutability::MutImmutable => String::new(), + }; + arg_strs[0].0 = format!("&{}{}self", lt_str, mut_str); + } + ast::ExplicitSelf_::SelfExplicit(ref ty, _) => { + arg_strs[0].0 = format!("self: {}", pprust::ty_to_string(ty)); + } + ast::ExplicitSelf_::SelfValue(_) => { + arg_strs[0].0 = "self".to_string(); + } + _ => {} + } + } + result.push_str(&write_list(&arg_strs, &fmt)); + + result.push(')'); + + // Where clause. + if where_clause.predicates.len() > 0 { + result.push('\n'); + result.push_str(&make_indent(indent + 4)); + result.push_str("where "); + + let budget = IDEAL_WIDTH + LEEWAY - indent - 10; + let fmt = ListFormatting { + tactic: ListTactic::Vertical, + separator: ",", + trailing_separator: SeparatorTactic::Always, + indent: indent + 10, + h_width: budget, + v_width: budget, + }; + let where_strs: Vec<_> = where_clause.predicates.iter().map(|p| (self.rewrite_pred(p), String::new())).collect(); + result.push_str(&write_list(&where_strs, &fmt)); + } + + // Return type. + if ret_str.len() > 0 { + // If we've already gone multi-line, or the return type would push + // over the max width, then put the return type on a new line. + if result.contains("\n") || + result.len() + indent + ret_str.len() > MAX_WIDTH { + let indent = match FN_RETURN_INDENT { + ReturnIndent::WithWhereClause => indent + 4, + // TODO we might want to check that using the arg indent doesn't + // blow our budget, and if it does, then fallback to the where + // clause indent. + ReturnIndent::WithArgs => arg_indent, + }; + + result.push('\n'); + result.push_str(&make_indent(indent)); + } else { + result.push(' '); + } + result.push_str(&ret_str); + } + + // Prepare for the function body by possibly adding a newline and indent. + // FIXME we'll miss anything between the end of the signature and the start + // of the body, but we need more spans from the compiler to solve this. + if newline_brace { + result.push('\n'); + result.push_str(&make_indent(self.block_indent)); + } else { + result.push(' '); + } + + result + } + + // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly + fn rewrite_fn_input(&self, arg: &ast::Arg) -> String { + format!("{}: {}", + pprust::pat_to_string(&arg.pat), + pprust::ty_to_string(&arg.ty)) + } +} diff --git a/src/lists.rs b/src/lists.rs new file mode 100644 index 000000000000..5800f76b95ae --- /dev/null +++ b/src/lists.rs @@ -0,0 +1,136 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use make_indent; + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum ListTactic { + // One item per row. + Vertical, + // All items on one row. + Horizontal, + // Try Horizontal layout, if that fails then vertical + HorizontalVertical, + // Pack as many items as possible per row over (possibly) many rows. + Mixed, +} + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +pub enum SeparatorTactic { + Always, + Never, + Vertical, +} + +pub struct ListFormatting<'a> { + pub tactic: ListTactic, + pub separator: &'a str, + pub trailing_separator: SeparatorTactic, + pub indent: usize, + // Available width if we layout horizontally. + pub h_width: usize, + // Available width if we layout vertically + pub v_width: usize, +} + +// Format a list of strings into a string. +pub fn write_list<'b>(items:&[(String, String)], formatting: &ListFormatting<'b>) -> String { + if items.len() == 0 { + return String::new(); + } + + let mut tactic = formatting.tactic; + + let h_width = formatting.h_width; + let v_width = formatting.v_width; + let sep_len = formatting.separator.len(); + + // Conservatively overestimates because of the changing separator tactic. + let sep_count = if formatting.trailing_separator != SeparatorTactic::Never { + items.len() + } else { + items.len() - 1 + }; + + // TODO count dead space too. + let total_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l); + + // Check if we need to fallback from horizontal listing, if possible. + if tactic == ListTactic::HorizontalVertical { + if (total_width + (sep_len + 1) * sep_count) > h_width { + tactic = ListTactic::Vertical; + } else { + tactic = ListTactic::Horizontal; + } + } + + // Now that we know how we will layout, we can decide for sure if there + // will be a trailing separator. + let trailing_separator = match formatting.trailing_separator { + SeparatorTactic::Always => true, + SeparatorTactic::Vertical => tactic == ListTactic::Vertical, + SeparatorTactic::Never => false, + }; + + // Create a buffer for the result. + // TODO could use a StringBuffer or rope for this + let alloc_width = if tactic == ListTactic::Horizontal { + total_width + (sep_len + 1) * sep_count + } else { + total_width + items.len() * (formatting.indent + 1) + }; + let mut result = String::with_capacity(alloc_width); + + let mut line_len = 0; + let indent_str = &make_indent(formatting.indent); + for (i, &(ref item, _)) in items.iter().enumerate() { + let first = i == 0; + let separate = i != items.len() - 1 || trailing_separator; + + match tactic { + ListTactic::Horizontal if !first => { + result.push(' '); + } + ListTactic::Vertical if !first => { + result.push('\n'); + result.push_str(indent_str); + } + ListTactic::Mixed => { + let mut item_width = item.len(); + if separate { + item_width += sep_len; + } + + if line_len > 0 && line_len + item_width > v_width { + result.push('\n'); + result.push_str(indent_str); + line_len = 0; + } + + if line_len > 0 { + result.push(' '); + line_len += 1; + } + + line_len += item_width; + } + _ => {} + } + + result.push_str(item); + + if separate { + result.push_str(formatting.separator); + } + // TODO dead spans + } + + result +} diff --git a/src/missed_spans.rs b/src/missed_spans.rs new file mode 100644 index 000000000000..bea83a4f90fa --- /dev/null +++ b/src/missed_spans.rs @@ -0,0 +1,95 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use {FmtVisitor, make_indent}; +use syntax::codemap::{self, Span, BytePos}; + +impl<'a> FmtVisitor<'a> { + // TODO these format_missing methods are ugly. Refactor and add unit tests + // for the central whitespace stripping loop. + pub fn format_missing(&mut self, end: BytePos) { + self.format_missing_inner(end, |this, last_snippet, span, _| { + this.changes.push_str_span(span, last_snippet) + }) + } + + pub fn format_missing_with_indent(&mut self, end: BytePos) { + self.format_missing_inner(end, |this, last_snippet, span, snippet| { + if last_snippet == snippet { + // No new lines + this.changes.push_str_span(span, last_snippet); + this.changes.push_str_span(span, "\n"); + } else { + this.changes.push_str_span(span, last_snippet.trim_right()); + } + let indent = make_indent(this.block_indent); + this.changes.push_str_span(span, &indent); + }) + } + + fn format_missing_inner(&mut self, + end: BytePos, + process_last_snippet: F) + { + let start = self.last_pos; + debug!("format_missing_inner: {:?} to {:?}", + self.codemap.lookup_char_pos(start), + self.codemap.lookup_char_pos(end)); + + if start == end { + return; + } + + assert!(start < end, + "Request to format inverted span: {:?} to {:?}", + self.codemap.lookup_char_pos(start), + self.codemap.lookup_char_pos(end)); + + + self.last_pos = end; + let spans = self.changes.filespans_for_span(start, end); + for (i, &(start, end)) in spans.iter().enumerate() { + let span = codemap::mk_sp(BytePos(start), BytePos(end)); + let snippet = self.snippet(span); + + // Trim whitespace from the right hand side of each line. + // Annoyingly, the library functions for splitting by lines etc. are not + // quite right, so we must do it ourselves. + let mut line_start = 0; + let mut last_wspace = None; + for (i, c) in snippet.char_indices() { + if c == '\n' { + if let Some(lw) = last_wspace { + self.changes.push_str_span(span, &snippet[line_start..lw]); + self.changes.push_str_span(span, "\n"); + } else { + self.changes.push_str_span(span, &snippet[line_start..i+1]); + } + + line_start = i + 1; + last_wspace = None; + } else { + if c.is_whitespace() { + if last_wspace.is_none() { + last_wspace = Some(i); + } + } else { + last_wspace = None; + } + } + } + if i == spans.len() - 1 { + process_last_snippet(self, &snippet[line_start..], span, &snippet); + } else { + self.changes.push_str_span(span, &snippet[line_start..]); + } + } + } +} diff --git a/src/mod.rs b/src/mod.rs index f8263e6b0ae8..750b3159a932 100644 --- a/src/mod.rs +++ b/src/mod.rs @@ -41,8 +41,8 @@ use rustc::session::Session; use rustc::session::config::{self, Input}; use rustc_driver::{driver, CompilerCalls, Compilation}; -use syntax::{ast, ptr, abi}; -use syntax::codemap::{self, CodeMap, Span, Pos, BytePos}; +use syntax::{ast, ptr}; +use syntax::codemap::{CodeMap, Span, Pos, BytePos}; use syntax::diagnostics; use syntax::parse::token; use syntax::print::pprust; @@ -51,8 +51,12 @@ use syntax::visit; use std::path::PathBuf; use changes::ChangeSet; +use lists::{write_list, ListFormatting, SeparatorTactic, ListTactic}; mod changes; +mod functions; +mod missed_spans; +mod lists; const IDEAL_WIDTH: usize = 80; const LEEWAY: usize = 5; @@ -286,130 +290,6 @@ fn make_indent(width: usize) -> String { indent } -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -enum ListTactic { - // One item per row. - Vertical, - // All items on one row. - Horizontal, - // Try Horizontal layout, if that fails then vertical - HorizontalVertical, - // Pack as many items as possible per row over (possibly) many rows. - Mixed, -} - -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -enum SeparatorTactic { - Always, - Never, - Vertical, -} - -struct ListFormatting<'a> { - tactic: ListTactic, - separator: &'a str, - trailing_separator: SeparatorTactic, - indent: usize, - // Available width if we layout horizontally. - h_width: usize, - // Available width if we layout vertically - v_width: usize, -} - -// Format a list of strings into a string. -fn write_list<'b>(items:&[(String, String)], formatting: &ListFormatting<'b>) -> String { - if items.len() == 0 { - return String::new(); - } - - let mut tactic = formatting.tactic; - - let h_width = formatting.h_width; - let v_width = formatting.v_width; - let sep_len = formatting.separator.len(); - - // Conservatively overestimates because of the changing separator tactic. - let sep_count = if formatting.trailing_separator != SeparatorTactic::Never { - items.len() - } else { - items.len() - 1 - }; - - // TODO count dead space too. - let total_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l); - - // Check if we need to fallback from horizontal listing, if possible. - if tactic == ListTactic::HorizontalVertical { - if (total_width + (sep_len + 1) * sep_count) > h_width { - tactic = ListTactic::Vertical; - } else { - tactic = ListTactic::Horizontal; - } - } - - // Now that we know how we will layout, we can decide for sure if there - // will be a trailing separator. - let trailing_separator = match formatting.trailing_separator { - SeparatorTactic::Always => true, - SeparatorTactic::Vertical => tactic == ListTactic::Vertical, - SeparatorTactic::Never => false, - }; - - // Create a buffer for the result. - // TODO could use a StringBuffer or rope for this - let alloc_width = if tactic == ListTactic::Horizontal { - total_width + (sep_len + 1) * sep_count - } else { - total_width + items.len() * (formatting.indent + 1) - }; - let mut result = String::with_capacity(alloc_width); - - let mut line_len = 0; - let indent_str = &make_indent(formatting.indent); - for (i, &(ref item, _)) in items.iter().enumerate() { - let first = i == 0; - let separate = i != items.len() - 1 || trailing_separator; - - match tactic { - ListTactic::Horizontal if !first => { - result.push(' '); - } - ListTactic::Vertical if !first => { - result.push('\n'); - result.push_str(indent_str); - } - ListTactic::Mixed => { - let mut item_width = item.len(); - if separate { - item_width += sep_len; - } - - if line_len > 0 && line_len + item_width > v_width { - result.push('\n'); - result.push_str(indent_str); - line_len = 0; - } - - if line_len > 0 { - result.push(' '); - line_len += 1; - } - - line_len += item_width; - } - _ => {} - } - - result.push_str(item); - - if separate { - result.push_str(formatting.separator); - } - // TODO dead spans - } - - result -} impl<'a> FmtVisitor<'a> { fn from_codemap<'b>(codemap: &'b CodeMap) -> FmtVisitor<'b> { @@ -421,87 +301,6 @@ impl<'a> FmtVisitor<'a> { } } - // TODO these format_missing methods are ugly. Refactor and add unit tests - // for the central whitespace stripping loop. - fn format_missing(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, span, _| { - this.changes.push_str_span(span, last_snippet) - }) - } - - fn format_missing_with_indent(&mut self, end: BytePos) { - self.format_missing_inner(end, |this, last_snippet, span, snippet| { - if last_snippet == snippet { - // No new lines - this.changes.push_str_span(span, last_snippet); - this.changes.push_str_span(span, "\n"); - } else { - this.changes.push_str_span(span, last_snippet.trim_right()); - } - let indent = make_indent(this.block_indent); - this.changes.push_str_span(span, &indent); - }) - } - - fn format_missing_inner(&mut self, - end: BytePos, - process_last_snippet: F) - { - let start = self.last_pos; - debug!("format_missing_inner: {:?} to {:?}", - self.codemap.lookup_char_pos(start), - self.codemap.lookup_char_pos(end)); - - if start == end { - return; - } - - assert!(start < end, - "Request to format inverted span: {:?} to {:?}", - self.codemap.lookup_char_pos(start), - self.codemap.lookup_char_pos(end)); - - - self.last_pos = end; - let spans = self.changes.filespans_for_span(start, end); - for (i, &(start, end)) in spans.iter().enumerate() { - let span = codemap::mk_sp(BytePos(start), BytePos(end)); - let snippet = self.snippet(span); - - // Trim whitespace from the right hand side of each line. - // Annoyingly, the library functions for splitting by lines etc. are not - // quite right, so we must do it ourselves. - let mut line_start = 0; - let mut last_wspace = None; - for (i, c) in snippet.char_indices() { - if c == '\n' { - if let Some(lw) = last_wspace { - self.changes.push_str_span(span, &snippet[line_start..lw]); - self.changes.push_str_span(span, "\n"); - } else { - self.changes.push_str_span(span, &snippet[line_start..i+1]); - } - - line_start = i + 1; - last_wspace = None; - } else { - if c.is_whitespace() { - if last_wspace.is_none() { - last_wspace = Some(i); - } - } else { - last_wspace = None; - } - } - } - if i == spans.len() - 1 { - process_last_snippet(self, &snippet[line_start..], span, &snippet); - } else { - self.changes.push_str_span(span, &snippet[line_start..]); - } - } - } - fn snippet(&self, span: Span) -> String { match self.codemap.span_to_snippet(span) { Ok(s) => s, @@ -629,221 +428,6 @@ impl<'a> FmtVisitor<'a> { format!("use {}::{{{}}};", path_str, write_list(&items, &fmt)) } - fn rewrite_fn(&mut self, - indent: usize, - ident: ast::Ident, - fd: &ast::FnDecl, - explicit_self: Option<&ast::ExplicitSelf>, - generics: &ast::Generics, - unsafety: &ast::Unsafety, - abi: &abi::Abi, - vis: ast::Visibility) - -> String - { - // FIXME we'll lose any comments in between parts of the function decl, but anyone - // who comments there probably deserves what they get. - - let where_clause = &generics.where_clause; - let newline_brace = match FN_BRACE_STYLE { - BraceStyle::AlwaysNextLine => true, - BraceStyle::SameLineWhere if where_clause.predicates.len() > 0 => true, - _ => false, - }; - - let mut result = String::with_capacity(1024); - // Vis unsafety abi. - if vis == ast::Visibility::Public { - result.push_str("pub "); - } - if let &ast::Unsafety::Unsafe = unsafety { - result.push_str("unsafe "); - } - if *abi != abi::Rust { - result.push_str("extern "); - result.push_str(&abi.to_string()); - result.push(' '); - } - - // fn foo - result.push_str("fn "); - result.push_str(&token::get_ident(ident)); - - // Generics. - // FIXME convert bounds to where clauses where they get too big or if - // there is a where clause at all. - let lifetimes: &[_] = &generics.lifetimes; - let tys: &[_] = &generics.ty_params; - if lifetimes.len() + tys.len() > 0 { - let budget = MAX_WIDTH - indent - result.len() - 2; - // TODO might need to insert a newline if the generics are really long - result.push('<'); - - let lt_strs = lifetimes.iter().map(|l| self.rewrite_lifetime_def(l)); - let ty_strs = tys.iter().map(|ty| self.rewrite_ty_param(ty)); - let generics_strs: Vec<_> = lt_strs.chain(ty_strs).map(|s| (s, String::new())).collect(); - let fmt = ListFormatting { - tactic: ListTactic::HorizontalVertical, - separator: ",", - trailing_separator: SeparatorTactic::Never, - indent: indent + result.len() + 1, - h_width: budget, - v_width: budget, - }; - result.push_str(&write_list(&generics_strs, &fmt)); - - result.push('>'); - } - - let ret_str = match fd.output { - ast::FunctionRetTy::DefaultReturn(_) => String::new(), - ast::FunctionRetTy::NoReturn(_) => "-> !".to_string(), - ast::FunctionRetTy::Return(ref ty) => "-> ".to_string() + &pprust::ty_to_string(ty), - }; - - // Args. - let args = &fd.inputs; - - let mut budgets = None; - - // Try keeping everything on the same line - if !result.contains("\n") { - // 3 = `() `, space is before ret_string - let mut used_space = indent + result.len() + 3 + ret_str.len(); - if newline_brace { - used_space += 2; - } - let one_line_budget = if used_space > MAX_WIDTH { - 0 - } else { - MAX_WIDTH - used_space - }; - - let used_space = indent + result.len() + 2; - let max_space = IDEAL_WIDTH + LEEWAY; - if used_space < max_space { - budgets = Some((one_line_budget, - // 2 = `()` - max_space - used_space, - indent + result.len() + 1)); - } - } - - // Didn't work. we must force vertical layout and put args on a newline. - if let None = budgets { - result.push('\n'); - result.push_str(&make_indent(indent + 4)); - // 6 = new indent + `()` - let used_space = indent + 6; - let max_space = IDEAL_WIDTH + LEEWAY; - if used_space > max_space { - // Whoops! bankrupt. - // TODO take evasive action, perhaps kill the indent or something. - } else { - // 5 = new indent + `(` - budgets = Some((0, max_space - used_space, indent + 5)); - } - } - - let (one_line_budget, multi_line_budget, arg_indent) = budgets.unwrap(); - result.push('('); - - let fmt = ListFormatting { - tactic: ListTactic::HorizontalVertical, - separator: ",", - trailing_separator: SeparatorTactic::Never, - indent: arg_indent, - h_width: one_line_budget, - v_width: multi_line_budget, - }; - // TODO dead spans - let mut arg_strs: Vec<_> = args.iter().map(|a| (self.rewrite_fn_input(a), String::new())).collect(); - // Account for sugary self. - if let Some(explicit_self) = explicit_self { - match explicit_self.node { - ast::ExplicitSelf_::SelfRegion(ref lt, ref m, _) => { - let lt_str = match lt { - &Some(ref l) => format!("{} ", pprust::lifetime_to_string(l)), - &None => String::new(), - }; - let mut_str = match m { - &ast::Mutability::MutMutable => "mut ".to_string(), - &ast::Mutability::MutImmutable => String::new(), - }; - arg_strs[0].0 = format!("&{}{}self", lt_str, mut_str); - } - ast::ExplicitSelf_::SelfExplicit(ref ty, _) => { - arg_strs[0].0 = format!("self: {}", pprust::ty_to_string(ty)); - } - ast::ExplicitSelf_::SelfValue(_) => { - arg_strs[0].0 = "self".to_string(); - } - _ => {} - } - } - result.push_str(&write_list(&arg_strs, &fmt)); - - result.push(')'); - - // Where clause. - if where_clause.predicates.len() > 0 { - result.push('\n'); - result.push_str(&make_indent(indent + 4)); - result.push_str("where "); - - let budget = IDEAL_WIDTH + LEEWAY - indent - 10; - let fmt = ListFormatting { - tactic: ListTactic::Vertical, - separator: ",", - trailing_separator: SeparatorTactic::Always, - indent: indent + 10, - h_width: budget, - v_width: budget, - }; - let where_strs: Vec<_> = where_clause.predicates.iter().map(|p| (self.rewrite_pred(p), String::new())).collect(); - result.push_str(&write_list(&where_strs, &fmt)); - } - - // Return type. - if ret_str.len() > 0 { - // If we've already gone multi-line, or the return type would push - // over the max width, then put the return type on a new line. - if result.contains("\n") || - result.len() + indent + ret_str.len() > MAX_WIDTH { - let indent = match FN_RETURN_INDENT { - ReturnIndent::WithWhereClause => indent + 4, - // TODO we might want to check that using the arg indent doesn't - // blow our budget, and if it does, then fallback to the where - // clause indent. - ReturnIndent::WithArgs => arg_indent, - }; - - result.push('\n'); - result.push_str(&make_indent(indent)); - } else { - result.push(' '); - } - result.push_str(&ret_str); - } - - // Prepare for the function body by possibly adding a newline and indent. - // FIXME we'll miss anything between the end of the signature and the start - // of the body, but we need more spans from the compiler to solve this. - if newline_brace { - result.push('\n'); - result.push_str(&make_indent(self.block_indent)); - } else { - result.push(' '); - } - - result - } - - // TODO we farm this out, but this could spill over the column limit, so we ought to handle it properly - fn rewrite_fn_input(&self, arg: &ast::Arg) -> String { - format!("{}: {}", - pprust::pat_to_string(&arg.pat), - pprust::ty_to_string(&arg.ty)) - } fn rewrite_pred(&self, predicate: &ast::WherePredicate) -> String {