diff --git a/src/chains.rs b/src/chains.rs index bd9f302824b..c59638d9e50 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -19,8 +19,9 @@ // we put each subexpression on a separate, much like the (default) function // argument function argument strategy. +use Indent; use rewrite::{Rewrite, RewriteContext}; -use utils::{first_line_width, make_indent}; +use utils::first_line_width; use expr::rewrite_call; use syntax::{ast, ptr}; @@ -30,7 +31,7 @@ pub fn rewrite_chain(mut expr: &ast::Expr, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option { let total_span = expr.span; let mut subexpr_list = vec![expr]; @@ -116,7 +117,7 @@ pub fn rewrite_chain(mut expr: &ast::Expr, let connector = if fits_single_line { String::new() } else { - format!("\n{}", make_indent(indent)) + format!("\n{}", indent.to_string(context.config)) }; let first_connector = if extend { @@ -145,7 +146,7 @@ fn rewrite_chain_expr(expr: &ast::Expr, span: Span, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option { match expr.node { ast::Expr_::ExprMethodCall(ref method_name, ref types, ref expressions) => { @@ -179,7 +180,7 @@ fn rewrite_method_call(method_name: ast::Ident, span: Span, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option { let type_str = if types.is_empty() { String::new() diff --git a/src/comment.rs b/src/comment.rs index 05117a7cb76..765d61cfa11 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -12,10 +12,16 @@ use std::iter; +use Indent; +use config::Config; use string::{StringFormat, rewrite_string}; -use utils::make_indent; -pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usize) -> String { +pub fn rewrite_comment(orig: &str, + block_style: bool, + width: usize, + offset: Indent, + config: &Config) + -> String { let s = orig.trim(); // Edge case: block comments. Let's not trim their lines (for now). @@ -33,11 +39,12 @@ pub fn rewrite_comment(orig: &str, block_style: bool, width: usize, offset: usiz line_start: line_start, line_end: "", width: max_chars, - offset: offset + opener.len() - line_start.len(), + offset: offset + (opener.len() - line_start.len()), trim_end: true, + config: config, }; - let indent_str = make_indent(offset); + let indent_str = offset.to_string(config); let line_breaks = s.chars().filter(|&c| c == '\n').count(); let (_, mut s) = s.lines() @@ -288,27 +295,32 @@ fn next(&mut self) -> Option<(CodeCharKind, T::Item)> { mod test { use super::{CharClasses, CodeCharKind, contains_comment, rewrite_comment, FindUncommented}; - // FIXME(#217): prevent string literal from going over the limit. + use Indent; #[test] #[rustfmt_skip] fn format_comments() { - assert_eq!("/* test */", rewrite_comment(" //test", true, 100, 100)); - assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10, 0)); + let config = Default::default(); + assert_eq!("/* test */", rewrite_comment(" //test", true, 100, Indent::new(0, 100), + &config)); + assert_eq!("// comment\n// on a", rewrite_comment("// comment on a", false, 10, + Indent::empty(), &config)); assert_eq!("// A multi line comment\n // between args.", rewrite_comment("// A multi line comment\n // between args.", false, 60, - 12)); + Indent::new(0, 12), + &config)); let input = "// comment"; let expected = "/* com\n \ * men\n \ * t */"; - assert_eq!(expected, rewrite_comment(input, true, 9, 69)); + assert_eq!(expected, rewrite_comment(input, true, 9, Indent::new(0, 69), &config)); - assert_eq!("/* trimmed */", rewrite_comment("/* trimmed */", true, 100, 100)); + assert_eq!("/* trimmed */", rewrite_comment("/* trimmed */", true, 100, + Indent::new(0, 100), &config)); } // This is probably intended to be a non-test fn, but it is not used. I'm diff --git a/src/config.rs b/src/config.rs index 2ecee9f98fe..c5b777e1d81 100644 --- a/src/config.rs +++ b/src/config.rs @@ -246,6 +246,7 @@ pub fn get_docs() -> Vec { format_strings: bool, "Format string literals, or leave as is", chains_overflow_last: bool, "Allow last call in method chain to break the line", take_source_hints: bool, "Retain some formatting characteristics from the source code", + hard_tabs: bool, "Use tab characters for indentation, spaces for alignment", } impl Default for Config { @@ -279,6 +280,7 @@ fn default() -> Config { format_strings: true, chains_overflow_last: true, take_source_hints: true, + hard_tabs: false, } } } diff --git a/src/expr.rs b/src/expr.rs index a91c6c38572..74d5522cc59 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -11,11 +11,11 @@ use std::cmp::Ordering; use std::borrow::Borrow; +use Indent; use rewrite::{Rewrite, RewriteContext}; use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic}; use string::{StringFormat, rewrite_string}; -use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width, wrap_str, - binary_search}; +use utils::{span_after, extra_offset, first_line_width, last_line_width, wrap_str, binary_search}; use visitor::FmtVisitor; use config::{StructLitStyle, MultilineStyle}; use comment::{FindUncommented, rewrite_comment, contains_comment}; @@ -29,7 +29,7 @@ use syntax::visit::Visitor; impl Rewrite for ast::Expr { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { match self.node { ast::Expr_::ExprVec(ref expr_vec) => { rewrite_array(expr_vec.iter().map(|e| &**e), self.span, context, width, offset) @@ -164,7 +164,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I, span: Span, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option where I: Iterator { @@ -201,6 +201,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I, h_width: max_item_width, v_width: max_item_width, ends_with_newline: false, + config: context.config, }; let list_str = try_opt!(write_list(&items, &fmt)); @@ -215,7 +216,7 @@ fn rewrite_closure(capture: ast::CaptureClause, span: Span, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option { let mover = if capture == ast::CaptureClause::CaptureByValue { "move " @@ -258,6 +259,7 @@ fn rewrite_closure(capture: ast::CaptureClause, h_width: horizontal_budget, v_width: budget, ends_with_newline: false, + config: context.config, }; let list_str = try_opt!(write_list(&arg_items.collect::>(), &fmt)); let mut prefix = format!("{}|{}|", mover, list_str); @@ -265,7 +267,7 @@ fn rewrite_closure(capture: ast::CaptureClause, if !ret_str.is_empty() { if prefix.contains('\n') { prefix.push('\n'); - prefix.push_str(&make_indent(argument_offset)); + prefix.push_str(&argument_offset.to_string(context.config)); } else { prefix.push(' '); } @@ -308,18 +310,18 @@ fn rewrite_closure(capture: ast::CaptureClause, .as_ref() .and_then(|body_expr| { if let ast::Expr_::ExprBlock(ref inner) = body_expr.node { - Some(inner.rewrite(&context, 2, 0)) + Some(inner.rewrite(&context, 2, Indent::empty())) } else { None } }) - .unwrap_or_else(|| body.rewrite(&context, 2, 0)); + .unwrap_or_else(|| body.rewrite(&context, 2, Indent::empty())); Some(format!("{} {}", prefix, try_opt!(body_rewrite))) } impl Rewrite for ast::Block { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { let user_str = context.snippet(self.span); if user_str == "{}" && width >= 2 { return Some(user_str); @@ -341,7 +343,8 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti let prefix = if !trimmed.is_empty() { // 9 = "unsafe {".len(), 7 = "unsafe ".len() let budget = try_opt!(width.checked_sub(9)); - format!("unsafe {} ", rewrite_comment(trimmed, true, budget, offset + 7)) + format!("unsafe {} ", + rewrite_comment(trimmed, true, budget, offset + 7, context.config)) } else { "unsafe ".to_owned() }; @@ -381,7 +384,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti // FIXME(#18): implement pattern formatting impl Rewrite for ast::Pat { - fn rewrite(&self, context: &RewriteContext, _: usize, _: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, _: usize, _: Indent) -> Option { Some(context.snippet(self.span)) } } @@ -447,7 +450,7 @@ fn new_for(pat: &'a ast::Pat, } impl<'a> Rewrite for Loop<'a> { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { let label_string = rewrite_label(self.label); // 2 = " {".len() let inner_width = try_opt!(width.checked_sub(self.keyword.len() + 2 + label_string.len())); @@ -483,7 +486,7 @@ fn rewrite_range(context: &RewriteContext, left: Option<&ast::Expr>, right: Option<&ast::Expr>, width: usize, - offset: usize) + offset: Indent) -> Option { let left_string = match left { Some(expr) => { @@ -513,7 +516,7 @@ fn rewrite_if_else(context: &RewriteContext, else_block_opt: Option<&ast::Expr>, pat: Option<&ast::Pat>, width: usize, - offset: usize, + offset: Indent, allow_single_line: bool) -> Option { // 3 = "if ", 2 = " {" @@ -588,11 +591,11 @@ fn single_line_if_else(context: &RewriteContext, let new_width = try_opt!(width.checked_sub(pat_expr_str.len() + fixed_cost)); let if_expr = if_node.expr.as_ref().unwrap(); - let if_str = try_opt!(if_expr.rewrite(context, new_width, 0)); + let if_str = try_opt!(if_expr.rewrite(context, new_width, Indent::empty())); let new_width = try_opt!(new_width.checked_sub(if_str.len())); let else_expr = else_node.expr.as_ref().unwrap(); - let else_str = try_opt!(else_expr.rewrite(context, new_width, 0)); + let else_str = try_opt!(else_expr.rewrite(context, new_width, Indent::empty())); // FIXME: this check shouldn't be necessary. Rewrites should either fail // or wrap to a newline when the object does not fit the width. @@ -621,7 +624,7 @@ fn rewrite_match(context: &RewriteContext, cond: &ast::Expr, arms: &[ast::Arm], width: usize, - offset: usize) + offset: Indent) -> Option { if arms.is_empty() { return None; @@ -634,7 +637,7 @@ fn rewrite_match(context: &RewriteContext, let nested_context = context.nested_context(); let arm_indent = nested_context.block_indent + context.overflow_indent; - let arm_indent_str = make_indent(arm_indent); + let arm_indent_str = arm_indent.to_string(context.config); let open_brace_pos = span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{", @@ -669,7 +672,7 @@ fn rewrite_match(context: &RewriteContext, result.push_str(&arm_indent_str); let arm_str = arm.rewrite(&nested_context, - context.config.max_width - arm_indent, + context.config.max_width - arm_indent.width(), arm_indent); if let Some(ref arm_str) = arm_str { result.push_str(arm_str); @@ -684,7 +687,7 @@ fn rewrite_match(context: &RewriteContext, // match expression, but meh. result.push('\n'); - result.push_str(&make_indent(context.block_indent + context.overflow_indent)); + result.push_str(&(context.block_indent + context.overflow_indent).to_string(context.config)); result.push('}'); Some(result) } @@ -704,9 +707,9 @@ fn arm_end_pos(arm: &ast::Arm) -> BytePos { // Match arms. impl Rewrite for ast::Arm { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { let &ast::Arm { ref attrs, ref pats, ref guard, ref body } = self; - let indent_str = make_indent(offset); + let indent_str = offset.to_string(context.config); // FIXME this is all a bit grotty, would be nice to abstract out the // treatment of attributes. @@ -734,7 +737,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti .map(|p| { p.rewrite(context, pat_budget, - offset + context.config.tab_spaces) + offset.block_indent(context.config)) }) .collect::>>()); @@ -775,7 +778,12 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti // Where the next text can start. let mut line_start = last_line_width(&pats_str); if pats_str.find('\n').is_none() { - line_start += offset; + line_start += offset.width(); + } + + let mut line_indent = offset + pats_width; + if vertical { + line_indent = line_indent.block_indent(context.config); } let comma = if let ast::ExprBlock(_) = body.node { @@ -788,7 +796,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti // 4 = ` => `.len() if context.config.max_width > line_start + comma.len() + 4 { let budget = context.config.max_width - line_start - comma.len() - 4; - if let Some(ref body_str) = body.rewrite(context, budget, line_start + 4) { + if let Some(ref body_str) = body.rewrite(context, budget, line_indent + 4) { if first_line_width(body_str) <= budget { return Some(format!("{}{} => {}{}", attr_str.trim_left(), @@ -810,7 +818,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti Some(format!("{}{} =>\n{}{},", attr_str.trim_left(), pats_str, - make_indent(offset + context.config.tab_spaces), + offset.block_indent(context.config).to_string(context.config), body_str)) } } @@ -819,7 +827,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti fn rewrite_guard(context: &RewriteContext, guard: &Option>, width: usize, - offset: usize, + offset: Indent, // The amount of space used up on this line for the pattern in // the arm (excludes offset). pattern_width: usize) @@ -840,10 +848,10 @@ fn rewrite_guard(context: &RewriteContext, if overhead < width { let cond_str = guard.rewrite(context, width - overhead, - offset + context.config.tab_spaces); + offset.block_indent(context.config)); if let Some(cond_str) = cond_str { return Some(format!("\n{}if {}", - make_indent(offset + context.config.tab_spaces), + offset.block_indent(context.config).to_string(context.config), cond_str)); } } @@ -862,7 +870,7 @@ fn rewrite_pat_expr(context: &RewriteContext, // *without* trailing space. connector: &str, width: usize, - offset: usize) + offset: Indent) -> Option { let pat_offset = offset + matcher.len(); let mut result = match pat { @@ -898,9 +906,11 @@ fn rewrite_pat_expr(context: &RewriteContext, // The expression won't fit on the current line, jump to next. result.push('\n'); - result.push_str(&make_indent(pat_offset)); + result.push_str(&pat_offset.to_string(context.config)); - let expr_rewrite = expr.rewrite(context, context.config.max_width - pat_offset, pat_offset); + let expr_rewrite = expr.rewrite(context, + context.config.max_width - pat_offset.width(), + pat_offset); result.push_str(&&try_opt!(expr_rewrite)); Some(result) @@ -909,7 +919,7 @@ fn rewrite_pat_expr(context: &RewriteContext, fn rewrite_string_lit(context: &RewriteContext, span: Span, width: usize, - offset: usize) + offset: Indent) -> Option { if !context.config.format_strings { return Some(context.snippet(span)); @@ -923,6 +933,7 @@ fn rewrite_string_lit(context: &RewriteContext, width: width, offset: offset, trim_end: false, + config: context.config, }; let string_lit = context.snippet(span); @@ -936,7 +947,7 @@ pub fn rewrite_call(context: &RewriteContext, args: &[ptr::P], span: Span, width: usize, - offset: usize) + offset: Indent) -> Option where R: Rewrite { @@ -955,7 +966,7 @@ fn rewrite_call_inner(context: &RewriteContext, args: &[ptr::P], span: Span, width: usize, - offset: usize) + offset: Indent) -> Result where R: Rewrite { @@ -1003,7 +1014,7 @@ fn rewrite_call_inner(context: &RewriteContext, span.lo, span.hi); - let fmt = ListFormatting::for_fn(remaining_width, offset); + let fmt = ListFormatting::for_fn(remaining_width, offset, context.config); let list_str = match write_list(&items.collect::>(), &fmt) { Some(str) => str, None => return Err(Ordering::Less), @@ -1015,9 +1026,9 @@ fn rewrite_call_inner(context: &RewriteContext, fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, width: usize, - offset: usize) + offset: Indent) -> Option { - debug!("rewrite_paren, width: {}, offset: {}", width, offset); + debug!("rewrite_paren, width: {}, offset: {:?}", width, offset); // 1 is for opening paren, 2 is for opening+closing, we want to keep the closing // paren on the same line as the subexpr. let subexpr_str = subexpr.rewrite(context, try_opt!(width.checked_sub(2)), offset + 1); @@ -1031,9 +1042,9 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, base: Option<&'a ast::Expr>, span: Span, width: usize, - offset: usize) + offset: Indent) -> Option { - debug!("rewrite_struct_lit: width {}, offset {}", width, offset); + debug!("rewrite_struct_lit: width {}, offset {:?}", width, offset); assert!(!fields.is_empty() || base.is_some()); enum StructLitField<'a> { @@ -1054,8 +1065,8 @@ enum StructLitField<'a> { StructLitStyle::Block => { // If we are all on one line, then we'll ignore the indent, and we // have a smaller budget. - let indent = context.block_indent + context.config.tab_spaces; - let v_budget = context.config.max_width.checked_sub(indent).unwrap_or(0); + let indent = context.block_indent.block_indent(context.config); + let v_budget = context.config.max_width.checked_sub(indent.width()).unwrap_or(0); (indent, v_budget) } }; @@ -1121,12 +1132,15 @@ enum StructLitField<'a> { h_width: h_budget, v_width: v_budget, ends_with_newline: false, + config: context.config, }; let fields_str = try_opt!(write_list(&items.collect::>(), &fmt)); let format_on_newline = || { - let inner_indent = make_indent(context.block_indent + context.config.tab_spaces); - let outer_indent = make_indent(context.block_indent); + let inner_indent = context.block_indent + .block_indent(context.config) + .to_string(context.config); + let outer_indent = context.block_indent.to_string(context.config); Some(format!("{} {{\n{}{}\n{}}}", path_str, inner_indent, fields_str, outer_indent)) }; @@ -1143,7 +1157,7 @@ enum StructLitField<'a> { fn rewrite_field(context: &RewriteContext, field: &ast::Field, width: usize, - offset: usize) + offset: Indent) -> Option { let name = &field.ident.node.to_string(); let overhead = name.len() + 2; @@ -1156,9 +1170,9 @@ fn rewrite_tuple_lit(context: &RewriteContext, items: &[ptr::P], span: Span, width: usize, - offset: usize) + offset: Indent) -> Option { - debug!("rewrite_tuple_lit: width: {}, offset: {}", width, offset); + debug!("rewrite_tuple_lit: width: {}, offset: {:?}", width, offset); let indent = offset + 1; // In case of length 1, need a trailing comma if items.len() == 1 { @@ -1173,7 +1187,7 @@ fn rewrite_tuple_lit(context: &RewriteContext, |item| item.span.lo, |item| item.span.hi, |item| { - let inner_width = context.config.max_width - indent - 1; + let inner_width = context.config.max_width - indent.width() - 1; item.rewrite(context, inner_width, indent) .unwrap_or(context.snippet(item.span)) }, @@ -1181,7 +1195,7 @@ fn rewrite_tuple_lit(context: &RewriteContext, span.hi - BytePos(1)); let budget = try_opt!(width.checked_sub(2)); - let fmt = ListFormatting::for_fn(budget, indent); + let fmt = ListFormatting::for_fn(budget, indent, context.config); let list_str = try_opt!(write_list(&items.collect::>(), &fmt)); Some(format!("({})", list_str)) @@ -1192,7 +1206,7 @@ fn rewrite_binary_op(context: &RewriteContext, lhs: &ast::Expr, rhs: &ast::Expr, width: usize, - offset: usize) + offset: Indent) -> Option { // FIXME: format comments between operands and operator @@ -1233,11 +1247,13 @@ fn rewrite_binary_op(context: &RewriteContext, // We have to use multiple lines. // Re-evaluate the lhs because we have more space now: - let budget = try_opt!(context.config.max_width.checked_sub(offset + 1 + operator_str.len())); + let budget = try_opt!(context.config + .max_width + .checked_sub(offset.width() + 1 + operator_str.len())); Some(format!("{} {}\n{}{}", try_opt!(lhs.rewrite(context, budget, offset)), operator_str, - make_indent(offset), + offset.to_string(context.config), rhs_result)) } @@ -1245,7 +1261,7 @@ fn rewrite_unary_op(context: &RewriteContext, op: &ast::UnOp, expr: &ast::Expr, width: usize, - offset: usize) + offset: Indent) -> Option { // For some reason, an UnOp is not spanned like BinOp! let operator_str = match *op { @@ -1265,7 +1281,7 @@ fn rewrite_assignment(context: &RewriteContext, rhs: &ast::Expr, op: Option<&ast::BinOp>, width: usize, - offset: usize) + offset: Indent) -> Option { let operator_str = match op { Some(op) => context.snippet(op.span), @@ -1285,7 +1301,7 @@ pub fn rewrite_assign_rhs>(context: &RewriteContext, lhs: S, ex: &ast::Expr, width: usize, - offset: usize) + offset: Indent) -> Option { let mut result = lhs.into(); @@ -1301,13 +1317,14 @@ pub fn rewrite_assign_rhs>(context: &RewriteContext, None => { // Expression did not fit on the same line as the identifier. Retry // on the next line. - let new_offset = offset + context.config.tab_spaces; - result.push_str(&format!("\n{}", make_indent(new_offset))); + let new_offset = offset.block_indent(context.config); + result.push_str(&format!("\n{}", new_offset.to_string(context.config))); // FIXME: we probably should related max_width to width instead of config.max_width // where is the 1 coming from anyway? - let max_width = try_opt!(context.config.max_width.checked_sub(new_offset + 1)); - let overflow_context = context.overflow_context(context.config.tab_spaces); + let max_width = try_opt!(context.config.max_width.checked_sub(new_offset.width() + 1)); + let rhs_indent = Indent::new(context.config.tab_spaces, 0); + let overflow_context = context.overflow_context(rhs_indent); let rhs = ex.rewrite(&overflow_context, max_width, new_offset); result.push_str(&&try_opt!(rhs)); diff --git a/src/imports.rs b/src/imports.rs index 1d215d022d1..f5f81015efb 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use Indent; use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic}; use utils::span_after; use rewrite::{Rewrite, RewriteContext}; @@ -20,7 +21,7 @@ impl Rewrite for ast::ViewPath { // Returns an empty string when the ViewPath is empty (like foo::bar::{}) - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { match self.node { ast::ViewPath_::ViewPathList(_, ref path_list) if path_list.is_empty() => { Some(String::new()) @@ -68,7 +69,7 @@ fn rewrite_single_use_list(path_str: String, vpi: ast::PathListItem) -> String { // Pretty prints a multi-item import. // Assumes that path_list.len() > 0. pub fn rewrite_use_list(width: usize, - offset: usize, + offset: Indent, path: &ast::Path, path_list: &[ast::PathListItem], span: Span, @@ -105,6 +106,7 @@ pub fn rewrite_use_list(width: usize, // (loose 1 column (";")) v_width: remaining_width, ends_with_newline: false, + config: context.config, }; let mut items = { diff --git a/src/items.rs b/src/items.rs index a14310e94ef..a61925798d3 100644 --- a/src/items.rs +++ b/src/items.rs @@ -10,8 +10,8 @@ // Formatting top-level items - functions, structs, enums, traits, impls. -use utils::{format_mutability, format_visibility, make_indent, contains_skip, span_after, - end_typaram, wrap_str}; +use Indent; +use utils::{format_mutability, format_visibility, contains_skip, span_after, end_typaram, wrap_str}; use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, ListTactic}; use expr::rewrite_assign_rhs; use comment::FindUncommented; @@ -34,8 +34,7 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) { if let Some(ref ty) = local.ty { infix.push_str(": "); - // FIXME: silly width, indent - infix.push_str(&ty.rewrite(&self.get_context(), 1000, 0).unwrap()); + infix.push_str(&ty.rewrite(&self.get_context(), 1000, Indent::empty()).unwrap()); } if local.init.is_some() { @@ -52,12 +51,13 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) { let mut result = "let ".to_owned(); let pattern_offset = self.block_indent + result.len() + infix.len(); // 1 = ; - let pattern_width = match self.config.max_width.checked_sub(pattern_offset + 1) { + let pattern_width = self.config.max_width.checked_sub(pattern_offset.width() + 1); + let pattern_width = match pattern_width { Some(width) => width, None => return, }; - match local.pat.rewrite(&context, pattern_offset, pattern_width) { + match local.pat.rewrite(&context, pattern_width, pattern_offset) { Some(ref pat_string) => result.push_str(pat_string), None => return, } @@ -65,7 +65,8 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) { result.push_str(&infix); if let Some(ref ex) = local.init { - let max_width = match self.config.max_width.checked_sub(context.block_indent + 1) { + let max_width = self.config.max_width.checked_sub(context.block_indent.width() + 1); + let max_width = match max_width { Some(width) => width, None => return, }; @@ -88,7 +89,7 @@ pub fn visit_let(&mut self, local: &ast::Local, span: Span) { } pub fn rewrite_fn(&mut self, - indent: usize, + indent: Indent, ident: ast::Ident, fd: &ast::FnDecl, explicit_self: Option<&ast::ExplicitSelf>, @@ -124,7 +125,7 @@ pub fn rewrite_fn(&mut self, // this. if newline_brace { result.push('\n'); - result.push_str(&make_indent(indent)); + result.push_str(&indent.to_string(self.config)); } else { result.push(' '); } @@ -133,7 +134,7 @@ pub fn rewrite_fn(&mut self, } pub fn rewrite_required_fn(&mut self, - indent: usize, + indent: Indent, ident: ast::Ident, sig: &ast::MethodSig, span: Span) @@ -160,7 +161,7 @@ pub fn rewrite_required_fn(&mut self, } fn rewrite_fn_base(&mut self, - indent: usize, + indent: Indent, ident: ast::Ident, fd: &ast::FnDecl, explicit_self: Option<&ast::ExplicitSelf>, @@ -207,13 +208,15 @@ fn rewrite_fn_base(&mut self, result.push_str(&generics_str); let context = self.get_context(); - let ret_str = fd.output.rewrite(&context, self.config.max_width - indent, indent).unwrap(); + let ret_str = fd.output + .rewrite(&context, self.config.max_width - indent.width(), indent) + .unwrap(); // Args. let (one_line_budget, multi_line_budget, mut arg_indent) = self.compute_budgets_for_args(&result, indent, ret_str.len(), newline_brace); - debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {}", + debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}", one_line_budget, multi_line_budget, arg_indent); @@ -222,17 +225,17 @@ fn rewrite_fn_base(&mut self, if one_line_budget <= 0 { if self.config.fn_args_paren_newline { result.push('\n'); - result.push_str(&make_indent(arg_indent)); + result.push_str(&arg_indent.to_string(self.config)); arg_indent = arg_indent + 1; // extra space for `(` result.push('('); } else { result.push_str("(\n"); - result.push_str(&make_indent(arg_indent)); + result.push_str(&arg_indent.to_string(self.config)); } } else if self.config.fn_args_layout == StructLitStyle::Block { - arg_indent = indent + self.config.tab_spaces; + arg_indent = indent.block_indent(self.config); result.push_str("(\n"); - result.push_str(&make_indent(arg_indent)); + result.push_str(&arg_indent.to_string(self.config)); } else { result.push('('); } @@ -266,7 +269,7 @@ fn rewrite_fn_base(&mut self, // Unless we are formatting args like a block, in which case there // should always be room for the return type. if (result.contains("\n") || - result.len() + indent + ret_str.len() > self.config.max_width) && + result.len() + indent.width() + ret_str.len() > self.config.max_width) && self.config.fn_args_layout != StructLitStyle::Block { let indent = match self.config.fn_return_indent { ReturnIndent::WithWhereClause => indent + 4, @@ -277,7 +280,7 @@ fn rewrite_fn_base(&mut self, }; result.push('\n'); - result.push_str(&make_indent(indent)); + result.push_str(&indent.to_string(self.config)); } else { result.push(' '); } @@ -326,8 +329,8 @@ fn rewrite_args(&self, explicit_self: Option<&ast::ExplicitSelf>, one_line_budget: usize, multi_line_budget: usize, - indent: usize, - arg_indent: usize, + indent: Indent, + arg_indent: Indent, span: Span) -> Option { let context = self.get_context(); @@ -386,7 +389,7 @@ fn rewrite_args(&self, let indent = match self.config.fn_arg_indent { BlockIndentStyle::Inherit => indent, - BlockIndentStyle::Tabbed => indent + self.config.tab_spaces, + BlockIndentStyle::Tabbed => indent.block_indent(self.config), BlockIndentStyle::Visual => arg_indent, }; @@ -398,6 +401,7 @@ fn rewrite_args(&self, h_width: one_line_budget, v_width: multi_line_budget, ends_with_newline: false, + config: self.config, }; write_list(&arg_items, &fmt) @@ -405,16 +409,16 @@ fn rewrite_args(&self, fn compute_budgets_for_args(&self, result: &str, - indent: usize, + indent: Indent, ret_str_len: usize, newline_brace: bool) - -> (usize, usize, usize) { + -> (usize, usize, Indent) { 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() + ret_str_len + 3; + let mut used_space = indent.width() + result.len() + ret_str_len + 3; if !newline_brace { used_space += 2; } @@ -425,7 +429,7 @@ fn compute_budgets_for_args(&self, }; // 2 = `()` - let used_space = indent + result.len() + 2; + let used_space = indent.width() + result.len() + 2; let max_space = self.config.ideal_width + self.config.leeway; debug!("compute_budgets_for_args: used_space: {}, max_space: {}", used_space, @@ -439,8 +443,8 @@ fn compute_budgets_for_args(&self, // Didn't work. we must force vertical layout and put args on a newline. if let None = budgets { - let new_indent = indent + self.config.tab_spaces; - let used_space = new_indent + 2; // account for `(` and `)` + let new_indent = indent.block_indent(self.config); + let used_space = new_indent.width() + 2; // account for `(` and `)` let max_space = self.config.ideal_width + self.config.leeway; if used_space > max_space { // Whoops! bankrupt. @@ -475,13 +479,13 @@ pub fn visit_enum(&mut self, let generics_str = self.format_generics(generics, " {", self.block_indent, - self.block_indent + self.config.tab_spaces, + self.block_indent.block_indent(self.config), codemap::mk_sp(span.lo, body_start)) .unwrap(); self.buffer.push_str(&generics_str); self.last_pos = body_start; - self.block_indent += self.config.tab_spaces; + self.block_indent = self.block_indent.block_indent(self.config); for (i, f) in enum_def.variants.iter().enumerate() { let next_span_start: BytePos = if i == enum_def.variants.len() - 1 { span.hi @@ -491,7 +495,7 @@ pub fn visit_enum(&mut self, self.visit_variant(f, i == enum_def.variants.len() - 1, next_span_start); } - self.block_indent -= self.config.tab_spaces; + self.block_indent = self.block_indent.block_unindent(self.config); self.format_missing_with_indent(span.lo + BytePos(enum_snippet.rfind('}').unwrap() as u32)); self.buffer.push_str("}"); @@ -521,7 +525,9 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st |arg| { // FIXME silly width, indent arg.ty - .rewrite(&self.get_context(), 1000, 0) + .rewrite(&self.get_context(), + 1000, + Indent::empty()) .unwrap() }, span_after(field.span, "(", self.codemap), @@ -536,7 +542,7 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st } else { 0 }; - let budget = self.config.ideal_width - indent - comma_cost - 1; // 1 = ) + let budget = self.config.ideal_width - indent.width() - comma_cost - 1; // 1 = ) let fmt = ListFormatting { tactic: ListTactic::HorizontalVertical, @@ -546,6 +552,7 @@ fn visit_variant(&mut self, field: &ast::Variant, last_field: bool, next_span_st h_width: budget, v_width: budget, ends_with_newline: true, + config: self.config, }; let list_str = match write_list(&items.collect::>(), &fmt) { Some(list_str) => list_str, @@ -601,7 +608,7 @@ fn format_struct(&self, struct_def: &ast::StructDef, generics: Option<&ast::Generics>, span: Span, - offset: usize) + offset: Indent) -> Option { let mut result = String::with_capacity(1024); @@ -654,7 +661,7 @@ fn format_struct(&self, span.hi); // 2 terminators and a semicolon - let used_budget = offset + header_str.len() + generics_str.len() + 3; + let used_budget = offset.width() + header_str.len() + generics_str.len() + 3; // Conservative approximation let single_line_cost = (span.hi - struct_def.fields[0].span.lo).0; @@ -662,7 +669,7 @@ fn format_struct(&self, single_line_cost as usize + used_budget > self.config.max_width; let tactic = if break_line { - let indentation = make_indent(offset + self.config.tab_spaces); + let indentation = offset.block_indent(self.config).to_string(self.config); result.push('\n'); result.push_str(&indentation); @@ -672,15 +679,16 @@ fn format_struct(&self, }; // 1 = , - let budget = self.config.ideal_width - offset + self.config.tab_spaces - 1; + let budget = self.config.ideal_width - offset.width() + self.config.tab_spaces - 1; let fmt = ListFormatting { tactic: tactic, separator: ",", trailing_separator: self.config.struct_trailing_comma, - indent: offset + self.config.tab_spaces, + indent: offset.block_indent(self.config), h_width: self.config.max_width, v_width: budget, ends_with_newline: true, + config: self.config, }; let list_str = write_list(&items.collect::>(), &fmt).unwrap(); @@ -688,7 +696,7 @@ fn format_struct(&self, if break_line { result.push('\n'); - result.push_str(&make_indent(offset)); + result.push_str(&offset.to_string(self.config)); } result.push_str(terminator); @@ -727,8 +735,8 @@ fn format_header(&self, item_name: &str, ident: ast::Ident, vis: ast::Visibility fn format_generics(&self, generics: &ast::Generics, opener: &str, - offset: usize, - generics_offset: usize, + offset: Indent, + generics_offset: Indent, span: Span) -> Option { let mut result = try_opt!(self.rewrite_generics(generics, offset, generics_offset, span)); @@ -740,7 +748,7 @@ fn format_generics(&self, Density::Tall, span.hi)); result.push_str(&where_clause_str); - result.push_str(&make_indent(self.block_indent)); + result.push_str(&self.block_indent.to_string(self.config)); result.push('\n'); result.push_str(opener.trim()); } else { @@ -765,18 +773,18 @@ fn format_field(&self, field: &ast::StructField) -> String { ast::StructFieldKind::UnnamedField(vis) => format_visibility(vis), }; // FIXME silly width, indent - let typ = field.node.ty.rewrite(&self.get_context(), 1000, 0).unwrap(); + let typ = field.node.ty.rewrite(&self.get_context(), 1000, Indent::empty()).unwrap(); - let indent = self.block_indent + self.config.tab_spaces; + let indent = self.block_indent.block_indent(self.config); let mut attr_str = field.node .attrs .rewrite(&self.get_context(), - self.config.max_width - indent, + self.config.max_width - indent.width(), indent) .unwrap(); if !attr_str.is_empty() { attr_str.push('\n'); - attr_str.push_str(&make_indent(indent)); + attr_str.push_str(&indent.to_string(self.config)); } match name { @@ -787,8 +795,8 @@ fn format_field(&self, field: &ast::StructField) -> String { fn rewrite_generics(&self, generics: &ast::Generics, - offset: usize, - generics_offset: usize, + offset: Indent, + generics_offset: Indent, span: Span) -> Option { // FIXME: convert bounds to where clauses where they get too big or if @@ -801,12 +809,12 @@ fn rewrite_generics(&self, let offset = match self.config.generics_indent { BlockIndentStyle::Inherit => offset, - BlockIndentStyle::Tabbed => offset + self.config.tab_spaces, + BlockIndentStyle::Tabbed => offset.block_indent(self.config), // 1 = < BlockIndentStyle::Visual => generics_offset + 1, }; - let h_budget = self.config.max_width - generics_offset - 2; + let h_budget = self.config.max_width - generics_offset.width() - 2; // TODO: might need to insert a newline if the generics are really long. // Strings for the generics. @@ -841,7 +849,7 @@ fn rewrite_generics(&self, item.item = ty; } - let fmt = ListFormatting::for_fn(h_budget, offset); + let fmt = ListFormatting::for_fn(h_budget, offset, self.config); let list_str = try_opt!(write_list(&items, &fmt)); Some(format!("<{}>", list_str)) @@ -850,7 +858,7 @@ fn rewrite_generics(&self, fn rewrite_where_clause(&self, where_clause: &ast::WhereClause, config: &Config, - indent: usize, + indent: Indent, density: Density, span_end: BytePos) -> Option { @@ -859,22 +867,23 @@ fn rewrite_where_clause(&self, } let extra_indent = match self.config.where_indent { - BlockIndentStyle::Inherit => 0, - BlockIndentStyle::Tabbed | BlockIndentStyle::Visual => config.tab_spaces, + BlockIndentStyle::Inherit => Indent::empty(), + BlockIndentStyle::Tabbed | BlockIndentStyle::Visual => Indent::new(config.tab_spaces, + 0), }; let context = self.get_context(); let offset = match self.config.where_pred_indent { BlockIndentStyle::Inherit => indent + extra_indent, - BlockIndentStyle::Tabbed => indent + extra_indent + config.tab_spaces, + BlockIndentStyle::Tabbed => indent + extra_indent.block_indent(config), // 6 = "where ".len() BlockIndentStyle::Visual => indent + extra_indent + 6, }; // FIXME: if where_pred_indent != Visual, then the budgets below might // be out by a char or two. - let budget = self.config.ideal_width + self.config.leeway - offset; + let budget = self.config.ideal_width + self.config.leeway - offset.width(); let span_start = span_for_where_pred(&where_clause.predicates[0]).lo; let items = itemize_list(self.codemap, where_clause.predicates.iter(), @@ -896,13 +905,14 @@ fn rewrite_where_clause(&self, h_width: budget, v_width: budget, ends_with_newline: true, + config: self.config, }; let preds_str = try_opt!(write_list(&items.collect::>(), &fmt)); // 9 = " where ".len() + " {".len() if density == Density::Tall || preds_str.contains('\n') || - indent + 9 + preds_str.len() > self.config.max_width { - Some(format!("\n{}where {}", make_indent(indent + extra_indent), preds_str)) + indent.width() + 9 + preds_str.len() > self.config.max_width { + Some(format!("\n{}where {}", (indent + extra_indent).to_string(self.config), preds_str)) } else { Some(format!(" where {}", preds_str)) } @@ -910,7 +920,7 @@ fn rewrite_where_clause(&self, } impl Rewrite for ast::FunctionRetTy { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { match *self { ast::FunctionRetTy::DefaultReturn(_) => Some(String::new()), ast::FunctionRetTy::NoReturn(_) => { @@ -929,7 +939,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti } impl Rewrite for ast::Arg { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { if is_named_arg(self) { if let ast::Ty_::TyInfer = self.ty.node { wrap_str(pprust::pat_to_string(&self.pat), context.config.max_width, width, offset) diff --git a/src/lib.rs b/src/lib.rs index 7b6adeba2c8..30150537309 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,7 @@ use syntax::codemap::CodeMap; use syntax::diagnostics; +use std::ops::{Add, Sub}; use std::path::PathBuf; use std::collections::HashMap; use std::fmt; @@ -80,6 +81,83 @@ // When we get scoped annotations, we should have rustfmt::skip. const SKIP_ANNOTATION: &'static str = "rustfmt_skip"; +#[derive(Copy, Clone, Debug)] +pub struct Indent { + // Width of the block indent, in characters. Must be a multiple of + // Config::tab_spaces. + block_indent: usize, + // Alignment in characters. + alignment: usize, +} + +impl Indent { + pub fn new(block_indent: usize, alignment: usize) -> Indent { + Indent { block_indent: block_indent, alignment: alignment } + } + + pub fn empty() -> Indent { + Indent::new(0, 0) + } + + pub fn block_indent(mut self, config: &Config) -> Indent { + self.block_indent += config.tab_spaces; + self + } + + pub fn block_unindent(mut self, config: &Config) -> Indent { + self.block_indent -= config.tab_spaces; + self + } + + pub fn width(&self) -> usize { + self.block_indent + self.alignment + } + + pub fn to_string(&self, config: &Config) -> String { + let (num_tabs, num_spaces) = if config.hard_tabs { + (self.block_indent / config.tab_spaces, self.alignment) + } else { + (0, self.block_indent + self.alignment) + }; + let num_chars = num_tabs + num_spaces; + let mut indent = String::with_capacity(num_chars); + for _ in 0..num_tabs { + indent.push('\t') + } + for _ in 0..num_spaces { + indent.push(' ') + } + indent + } +} + +impl Add for Indent { + type Output = Indent; + + fn add(self, rhs: Indent) -> Indent { + Indent { + block_indent: self.block_indent + rhs.block_indent, + alignment: self.alignment + rhs.alignment, + } + } +} + +impl Sub for Indent { + type Output = Indent; + + fn sub(self, rhs: Indent) -> Indent { + Indent::new(self.block_indent - rhs.block_indent, self.alignment - rhs.alignment) + } +} + +impl Add for Indent { + type Output = Indent; + + fn add(self, rhs: usize) -> Indent { + Indent::new(self.block_indent, self.alignment + rhs) + } +} + #[derive(Copy, Clone)] pub enum WriteMode { // Backups the original file and overwrites the orignal. diff --git a/src/lists.rs b/src/lists.rs index 2b85c994581..e00e8b0ee37 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -13,8 +13,10 @@ use syntax::codemap::{self, CodeMap, BytePos}; -use utils::{round_up_to_power_of_two, make_indent, wrap_str}; +use Indent; +use utils::{round_up_to_power_of_two, wrap_str}; use comment::{FindUncommented, rewrite_comment, find_comment_end}; +use config::Config; #[derive(Eq, PartialEq, Debug, Copy, Clone)] pub enum ListTactic { @@ -44,7 +46,7 @@ pub struct ListFormatting<'a> { pub tactic: ListTactic, pub separator: &'a str, pub trailing_separator: SeparatorTactic, - pub indent: usize, + pub indent: Indent, // Available width if we layout horizontally. pub h_width: usize, // Available width if we layout vertically @@ -52,10 +54,11 @@ pub struct ListFormatting<'a> { // Non-expressions, e.g. items, will have a new line at the end of the list. // Important for comment styles. pub ends_with_newline: bool, + pub config: &'a Config, } impl<'a> ListFormatting<'a> { - pub fn for_fn(width: usize, offset: usize) -> ListFormatting<'a> { + pub fn for_fn(width: usize, offset: Indent, config: &'a Config) -> ListFormatting<'a> { ListFormatting { tactic: ListTactic::HorizontalVertical, separator: ",", @@ -64,6 +67,7 @@ pub fn for_fn(width: usize, offset: usize) -> ListFormatting<'a> { h_width: width, v_width: width, ends_with_newline: false, + config: config, } } } @@ -146,12 +150,12 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op let alloc_width = if tactic == ListTactic::Horizontal { total_width + total_sep_len } else { - total_width + items.len() * (formatting.indent + 1) + total_width + items.len() * (formatting.indent.width() + 1) }; let mut result = String::with_capacity(round_up_to_power_of_two(alloc_width)); let mut line_len = 0; - let indent_str = &make_indent(formatting.indent); + let indent_str = &formatting.indent.to_string(formatting.config); for (i, item) in items.iter().enumerate() { let first = i == 0; let last = i == items.len() - 1; @@ -196,7 +200,8 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op let block_mode = tactic != ListTactic::Vertical; // Width restriction is only relevant in vertical mode. let max_width = formatting.v_width; - result.push_str(&rewrite_comment(comment, block_mode, max_width, formatting.indent)); + result.push_str(&rewrite_comment(comment, block_mode, max_width, formatting.indent, + formatting.config)); if tactic == ListTactic::Vertical { result.push('\n'); @@ -206,14 +211,18 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op } } - let max_width = formatting.indent + formatting.v_width; + let max_width = formatting.indent.width() + formatting.v_width; let item_str = wrap_str(&item.item[..], max_width, formatting.v_width, formatting.indent); result.push_str(&&try_opt!(item_str)); // Post-comments if tactic != ListTactic::Vertical && item.post_comment.is_some() { let comment = item.post_comment.as_ref().unwrap(); - let formatted_comment = rewrite_comment(comment, true, formatting.v_width, 0); + let formatted_comment = rewrite_comment(comment, + true, + formatting.v_width, + Indent::empty(), + formatting.config); result.push(' '); result.push_str(&formatted_comment); @@ -226,14 +235,19 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op if tactic == ListTactic::Vertical && item.post_comment.is_some() { // 1 = space between item and comment. let width = formatting.v_width.checked_sub(item_width + 1).unwrap_or(1); - let offset = formatting.indent + item_width + 1; + let mut offset = formatting.indent; + offset.alignment += item_width + 1; let comment = item.post_comment.as_ref().unwrap(); // Use block-style only for the last item or multiline comments. let block_style = !formatting.ends_with_newline && last || comment.trim().contains('\n') || comment.trim().len() > width; - let formatted_comment = rewrite_comment(comment, block_style, width, offset); + let formatted_comment = rewrite_comment(comment, + block_style, + width, + offset, + formatting.config); result.push(' '); result.push_str(&formatted_comment); diff --git a/src/macros.rs b/src/macros.rs index dd742ad46c8..8c38326ee8a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -25,6 +25,7 @@ use syntax::parse::token::{Eof, Comma, Token}; use syntax::parse::{ParseSess, tts_to_parser}; +use Indent; use rewrite::RewriteContext; use expr::{rewrite_call, rewrite_array}; use comment::FindUncommented; @@ -46,7 +47,7 @@ enum MacroStyle { pub fn rewrite_macro(mac: &ast::Mac, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option { let ast::Mac_::MacInvocTT(ref path, ref tt_vec, _) = mac.node; let style = macro_style(mac, context); diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 535fc4c8ad9..db9599c1706 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use utils::make_indent; use visitor::FmtVisitor; use syntax::codemap::{self, BytePos}; @@ -21,14 +20,15 @@ pub fn format_missing(&mut self, end: BytePos) { } pub fn format_missing_with_indent(&mut self, end: BytePos) { + let config = self.config; self.format_missing_inner(end, |this, last_snippet, snippet| { this.buffer.push_str(last_snippet.trim_right()); if last_snippet == snippet { - // No new lines in the snippet. + // No new lines in the snippet. this.buffer.push_str("\n"); } - let indent = make_indent(this.block_indent); + let indent = this.block_indent.to_string(config); this.buffer.push_str(&indent); }) } diff --git a/src/rewrite.rs b/src/rewrite.rs index 49c09ac53d1..6517bbdf37b 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -12,6 +12,7 @@ use syntax::codemap::{CodeMap, Span}; +use Indent; use config::Config; pub trait Rewrite { @@ -22,7 +23,7 @@ pub trait Rewrite { /// `width` is the maximum number of characters on the last line /// (excluding offset). The width of other lines is not limited by /// `width`. - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option; + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option; } pub struct RewriteContext<'a> { @@ -30,12 +31,12 @@ pub struct RewriteContext<'a> { pub config: &'a Config, // Indentation due to nesting of blocks. - pub block_indent: usize, + pub block_indent: Indent, // *Extra* indentation due to overflowing to the next line, e.g., // let foo = // bar(); // The extra 4 spaces when formatting `bar()` is overflow_indent. - pub overflow_indent: usize, + pub overflow_indent: Indent, } impl<'a> RewriteContext<'a> { @@ -43,12 +44,12 @@ pub fn nested_context(&self) -> RewriteContext<'a> { RewriteContext { codemap: self.codemap, config: self.config, - block_indent: self.block_indent + self.config.tab_spaces, + block_indent: self.block_indent.block_indent(self.config), overflow_indent: self.overflow_indent, } } - pub fn overflow_context(&self, overflow: usize) -> RewriteContext<'a> { + pub fn overflow_context(&self, overflow: Indent) -> RewriteContext<'a> { RewriteContext { codemap: self.codemap, config: self.config, diff --git a/src/string.rs b/src/string.rs index 5e5af61faf5..4e40b8e9ede 100644 --- a/src/string.rs +++ b/src/string.rs @@ -13,7 +13,9 @@ use unicode_segmentation::UnicodeSegmentation; use regex::Regex; -use utils::{make_indent, round_up_to_power_of_two}; +use Indent; +use config::Config; +use utils::round_up_to_power_of_two; use MIN_STRING; @@ -23,8 +25,9 @@ pub struct StringFormat<'a> { pub line_start: &'a str, pub line_end: &'a str, pub width: usize, - pub offset: usize, + pub offset: Indent, pub trim_end: bool, + pub config: &'a Config, } // TODO: simplify this! @@ -36,7 +39,7 @@ pub fn rewrite_string<'a>(s: &str, fmt: &StringFormat<'a>) -> String { let graphemes = UnicodeSegmentation::graphemes(&*stripped_str, false).collect::>(); - let indent = make_indent(fmt.offset); + let indent = fmt.offset.to_string(fmt.config); let indent = &indent; let mut cur_start = 0; diff --git a/src/types.rs b/src/types.rs index e52909b0d5c..9ba50a59cd9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -12,12 +12,13 @@ use syntax::print::pprust; use syntax::codemap::{self, Span, BytePos, CodeMap}; +use Indent; use lists::{itemize_list, write_list, ListFormatting}; use rewrite::{Rewrite, RewriteContext}; use utils::{extra_offset, span_after}; impl Rewrite for ast::Path { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { rewrite_path(context, None, self, width, offset) } } @@ -27,7 +28,7 @@ pub fn rewrite_path(context: &RewriteContext, qself: Option<&ast::QSelf>, path: &ast::Path, width: usize, - offset: usize) + offset: Indent) -> Option { let skip_count = qself.map(|x| x.position).unwrap_or(0); @@ -80,7 +81,7 @@ fn rewrite_path_segments<'a, I>(mut buffer: String, span_hi: BytePos, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option where I: Iterator { @@ -128,7 +129,7 @@ fn get_span(&self) -> Span { impl<'a> Rewrite for SegmentParam<'a> { // FIXME: doesn't always use width, offset. - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { Some(match *self { SegmentParam::LifeTime(ref lt) => { pprust::lifetime_to_string(lt) @@ -186,7 +187,7 @@ fn rewrite_segment(segment: &ast::PathSegment, span_hi: BytePos, context: &RewriteContext, width: usize, - offset: usize) + offset: Indent) -> Option { let ident_len = segment.identifier.to_string().len(); let width = try_opt!(width.checked_sub(ident_len)); @@ -229,7 +230,7 @@ fn rewrite_segment(segment: &ast::PathSegment, list_lo, span_hi); - let fmt = ListFormatting::for_fn(list_width, offset + extra_offset); + let fmt = ListFormatting::for_fn(list_width, offset + extra_offset, context.config); let list_str = try_opt!(write_list(&items.collect::>(), &fmt)); // Update position of last bracket. @@ -257,7 +258,7 @@ fn rewrite_segment(segment: &ast::PathSegment, let budget = try_opt!(width.checked_sub(output.len() + 2)); // 1 for ( - let fmt = ListFormatting::for_fn(budget, offset + 1); + let fmt = ListFormatting::for_fn(budget, offset + 1, context.config); let list_str = try_opt!(write_list(&items.collect::>(), &fmt)); format!("({}){}", list_str, output) @@ -269,7 +270,7 @@ fn rewrite_segment(segment: &ast::PathSegment, } impl Rewrite for ast::WherePredicate { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { // TODO: dead spans? // TODO: don't assume we'll always fit on one line... Some(match *self { @@ -340,7 +341,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti } impl Rewrite for ast::LifetimeDef { - fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option { + fn rewrite(&self, _: &RewriteContext, _: usize, _: Indent) -> Option { if self.bounds.is_empty() { Some(pprust::lifetime_to_string(&self.lifetime)) } else { @@ -356,7 +357,7 @@ fn rewrite(&self, _: &RewriteContext, _: usize, _: usize) -> Option { } impl Rewrite for ast::TyParamBound { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { match *self { ast::TyParamBound::TraitTyParamBound(ref tref, ast::TraitBoundModifier::None) => { tref.rewrite(context, width, offset) @@ -372,7 +373,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti } impl Rewrite for ast::TyParamBounds { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { let strs: Vec<_> = self.iter() .map(|b| b.rewrite(context, width, offset).unwrap()) .collect(); @@ -382,7 +383,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti // FIXME: this assumes everything will fit on one line impl Rewrite for ast::TyParam { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { let mut result = String::with_capacity(128); result.push_str(&self.ident.to_string()); if !self.bounds.is_empty() { @@ -407,7 +408,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti // FIXME: this assumes everything will fit on one line impl Rewrite for ast::PolyTraitRef { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { if !self.bound_lifetimes.is_empty() { let lifetime_str = self.bound_lifetimes .iter() @@ -430,7 +431,7 @@ fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Opti impl Rewrite for ast::Ty { // FIXME doesn't always use width, offset - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { match self.node { ast::TyPath(None, ref p) => { p.rewrite(context, width, offset) diff --git a/src/utils.rs b/src/utils.rs index 42d1151ba38..171572ccfbf 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -13,6 +13,7 @@ use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItem_}; use syntax::codemap::{CodeMap, Span, BytePos}; +use Indent; use comment::FindUncommented; use rewrite::{Rewrite, RewriteContext}; @@ -20,10 +21,10 @@ // Computes the length of a string's last line, minus offset. #[inline] -pub fn extra_offset(text: &str, offset: usize) -> usize { +pub fn extra_offset(text: &str, offset: Indent) -> usize { match text.rfind('\n') { // 1 for newline character - Some(idx) => text.len() - idx - 1 - offset, + Some(idx) => text.len() - idx - 1 - offset.width(), None => text.len(), } } @@ -35,15 +36,6 @@ pub fn span_after(original: Span, needle: &str, codemap: &CodeMap) -> BytePos { original.lo + BytePos(snippet.find_uncommented(needle).unwrap() as u32 + 1) } -#[inline] -pub fn make_indent(width: usize) -> String { - let mut indent = String::with_capacity(width); - for _ in 0..width { - indent.push(' ') - } - indent -} - #[inline] pub fn format_visibility(vis: Visibility) -> &'static str { match vis { @@ -186,7 +178,7 @@ macro_rules! try_opt { // Wraps string-like values in an Option. Returns Some when the string adheres // to the Rewrite constraints defined for the Rewrite trait and else otherwise. -pub fn wrap_str>(s: S, max_width: usize, width: usize, offset: usize) -> Option { +pub fn wrap_str>(s: S, max_width: usize, width: usize, offset: Indent) -> Option { { let snippet = s.as_ref(); @@ -197,7 +189,7 @@ pub fn wrap_str>(s: S, max_width: usize, width: usize, offset: usi // The caller of this function has already placed `offset` // characters on the first line. - let first_line_max_len = try_opt!(max_width.checked_sub(offset)); + let first_line_max_len = try_opt!(max_width.checked_sub(offset.width())); if lines.next().unwrap().len() > first_line_max_len { return None; } @@ -211,7 +203,7 @@ pub fn wrap_str>(s: S, max_width: usize, width: usize, offset: usi // indentation. // A special check for the last line, since the caller may // place trailing characters on this line. - if snippet.lines().rev().next().unwrap().len() > offset + width { + if snippet.lines().rev().next().unwrap().len() > offset.width() + width { return None; } } @@ -221,7 +213,7 @@ pub fn wrap_str>(s: S, max_width: usize, width: usize, offset: usi } impl Rewrite for String { - fn rewrite(&self, context: &RewriteContext, width: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, width: usize, offset: Indent) -> Option { wrap_str(self, context.config.max_width, width, offset).map(ToOwned::to_owned) } } diff --git a/src/visitor.rs b/src/visitor.rs index 1e3ebf71cef..762eda2013a 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -14,6 +14,7 @@ use strings::string_buffer::StringBuffer; +use Indent; use utils; use config::Config; use rewrite::{Rewrite, RewriteContext}; @@ -25,7 +26,7 @@ pub struct FmtVisitor<'a> { pub buffer: StringBuffer, pub last_pos: BytePos, // TODO: RAII util for indenting - pub block_indent: usize, + pub block_indent: Indent, pub config: &'a Config, } @@ -38,8 +39,9 @@ fn visit_expr(&mut self, ex: &'v ast::Expr) { self.codemap.lookup_char_pos(ex.span.hi)); self.format_missing(ex.span.lo); - let offset = self.buffer.cur_offset(); - let rewrite = ex.rewrite(&self.get_context(), self.config.max_width - offset, offset); + let rewrite = ex.rewrite(&self.get_context(), + self.config.max_width - self.block_indent.width(), + self.block_indent); if let Some(new_str) = rewrite { self.buffer.push_str(&new_str); @@ -65,7 +67,8 @@ fn visit_stmt(&mut self, stmt: &'v ast::Stmt) { // 1 = trailing semicolon; let rewrite = ex.rewrite(&self.get_context(), - self.config.max_width - self.block_indent - suffix.len(), + self.config.max_width - self.block_indent.width() - + suffix.len(), self.block_indent); if let Some(new_str) = rewrite { @@ -96,7 +99,7 @@ fn visit_block(&mut self, b: &'v ast::Block) { }; self.last_pos = self.last_pos + brace_compensation; - self.block_indent += self.config.tab_spaces; + self.block_indent = self.block_indent.block_indent(self.config); self.buffer.push_str("{"); for stmt in &b.stmts { @@ -111,8 +114,8 @@ fn visit_block(&mut self, b: &'v ast::Block) { None => {} } - self.block_indent -= self.config.tab_spaces; - // TODO: we should compress any newlines here to just one. + self.block_indent = self.block_indent.block_unindent(self.config); + // TODO: we should compress any newlines here to just one self.format_missing_with_indent(b.span.hi - brace_compensation); self.buffer.push_str("}"); self.last_pos = b.span.hi; @@ -126,6 +129,7 @@ fn visit_fn(&mut self, b: &'v ast::Block, s: Span, _: ast::NodeId) { + let indent = self.block_indent; let rewrite = match fk { visit::FnKind::ItemFn(ident, @@ -191,9 +195,9 @@ fn visit_item(&mut self, item: &'v ast::Item) { } ast::Item_::ItemImpl(..) | ast::Item_::ItemTrait(..) => { - self.block_indent += self.config.tab_spaces; + self.block_indent = self.block_indent.block_indent(self.config); visit::walk_item(self, item); - self.block_indent -= self.config.tab_spaces; + self.block_indent = self.block_indent.block_unindent(self.config); } ast::Item_::ItemExternCrate(_) => { self.format_missing_with_indent(item.span.lo); @@ -256,7 +260,7 @@ fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { fn visit_mac(&mut self, mac: &'v ast::Mac) { // 1 = ; - let width = self.config.max_width - self.block_indent - 1; + let width = self.config.max_width - self.block_indent.width() - 1; let rewrite = rewrite_macro(mac, &self.get_context(), width, self.block_indent); if let Some(res) = rewrite { @@ -272,7 +276,7 @@ pub fn from_codemap<'b>(codemap: &'b CodeMap, config: &'b Config) -> FmtVisitor< codemap: codemap, buffer: StringBuffer::new(), last_pos: BytePos(0), - block_indent: 0, + block_indent: Indent { block_indent: 0, alignment: 0 }, config: config, } } @@ -302,7 +306,7 @@ pub fn visit_attrs(&mut self, attrs: &[ast::Attribute]) -> bool { true } else { let rewrite = attrs.rewrite(&self.get_context(), - self.config.max_width - self.block_indent, + self.config.max_width - self.block_indent.width(), self.block_indent) .unwrap(); self.buffer.push_str(&rewrite); @@ -323,32 +327,33 @@ fn format_mod(&mut self, m: &ast::Mod, s: Span, ident: ast::Ident) { if is_internal { debug!("FmtVisitor::format_mod: internal mod"); - self.block_indent += self.config.tab_spaces; + self.block_indent = self.block_indent.block_indent(self.config); visit::walk_mod(self, m); debug!("... last_pos after: {:?}", self.last_pos); - self.block_indent -= self.config.tab_spaces; + self.block_indent = self.block_indent.block_unindent(self.config); } } pub fn format_separate_mod(&mut self, m: &ast::Mod, filename: &str) { let filemap = self.codemap.get_filemap(filename); self.last_pos = filemap.start_pos; - self.block_indent = 0; + self.block_indent = Indent::empty(); visit::walk_mod(self, m); self.format_missing(filemap.end_pos); } fn format_import(&mut self, vis: ast::Visibility, vp: &ast::ViewPath, span: Span) { let vis = utils::format_visibility(vis); - let offset = self.block_indent + vis.len() + "use ".len(); + let mut offset = self.block_indent; + offset.alignment += vis.len() + "use ".len(); let context = RewriteContext { codemap: self.codemap, config: self.config, block_indent: self.block_indent, - overflow_indent: 0, + overflow_indent: Indent::empty(), }; // 1 = ";" - match vp.rewrite(&context, self.config.max_width - offset - 1, offset) { + match vp.rewrite(&context, self.config.max_width - offset.width() - 1, offset) { Some(ref s) if s.is_empty() => { // Format up to last newline let prev_span = codemap::mk_sp(self.last_pos, span.lo); @@ -377,18 +382,18 @@ pub fn get_context(&self) -> RewriteContext { codemap: self.codemap, config: self.config, block_indent: self.block_indent, - overflow_indent: 0, + overflow_indent: Indent::empty(), } } } impl<'a> Rewrite for [ast::Attribute] { - fn rewrite(&self, context: &RewriteContext, _: usize, offset: usize) -> Option { + fn rewrite(&self, context: &RewriteContext, _: usize, offset: Indent) -> Option { let mut result = String::new(); if self.is_empty() { return Some(result); } - let indent = utils::make_indent(offset); + let indent = offset.to_string(context.config); for (i, a) in self.iter().enumerate() { let a_str = context.snippet(a.span); @@ -403,8 +408,9 @@ fn rewrite(&self, context: &RewriteContext, _: usize, offset: usize) -> Option(arg: T) -> &SomeType + where T: Fn(// First arg + A, + // Second argument + B, C, D, /* pre comment */ E /* last comment */) -> &SomeType { + arg(a, b, c, d, e) +} + + loong_func().quux(move || { + if true { + 1 + } else { + 2 + } + }); + + fffffffffffffffffffffffffffffffffff(a, + { + SCRIPT_TASK_ROOT + .with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + a.b + .c + .d(); + + x().y(|| { + match cond() { + true => (), + false => (), + } + }); +} diff --git a/tests/target/hard-tabs.rs b/tests/target/hard-tabs.rs new file mode 100644 index 00000000000..d8df0c2936a --- /dev/null +++ b/tests/target/hard-tabs.rs @@ -0,0 +1,95 @@ +// rustfmt-hard_tabs: true + +fn main() { + let x = Bar; + + let y = Foo { a: x }; + + Foo { + a: foo(), // comment + // comment + b: bar(), + ..something + }; + + fn foo(a: i32, + a: i32, + a: i32, + a: i32, + a: i32, + a: i32, + a: i32, + a: i32, + a: i32, + a: i32, + a: i32) { + } + + let str = "AAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAAAAAAAAAAAAAAA\ + AAAAAAAAAAAAaAa"; + + if let (some_very_large, tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuple) = + 1 + 2 + 3 { + } + + if cond() { + something(); + } else if different_cond() { + something_else(); + } else { + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + } + + unsafe /* very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong + * comment */ { + } + + unsafe /* So this is a very long comment. + * Multi-line, too. + * Will it still format correctly? */ { + } + + let z = [xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yyyyyyyyyyyyyyyyyyyyyyyyyyy, + zzzzzzzzzzzzzzzzzz, + q]; + + fn generic(arg: T) -> &SomeType + where T: Fn(// First arg + A, + // Second argument + B, + C, + D, + // pre comment + E /* last comment */) -> &SomeType + { + arg(a, b, c, d, e) + } + + loong_func().quux(move || { + if true { + 1 + } else { + 2 + } + }); + + fffffffffffffffffffffffffffffffffff(a, + { + SCRIPT_TASK_ROOT.with(|root| { + *root.borrow_mut() = Some(&script_task); + }); + }); + a.b + .c + .d(); + + x().y(|| { + match cond() { + true => (), + false => (), + } + }); +}