From b35906dbce47552c2ba6350f31c654bc77603fbd Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 21 Feb 2017 14:43:43 +1300 Subject: [PATCH] WIP --- src/chains.rs | 72 ++++----- src/expr.rs | 411 ++++++++++++++++++++++-------------------------- src/items.rs | 55 +++---- src/lib.rs | 96 ++++++++++- src/macros.rs | 5 +- src/patterns.rs | 10 +- src/rewrite.rs | 13 +- src/string.rs | 5 +- src/types.rs | 40 ++--- src/utils.rs | 6 +- src/visitor.rs | 5 +- 11 files changed, 382 insertions(+), 336 deletions(-) diff --git a/src/chains.rs b/src/chains.rs index 38f6fad1166d..f3cd2ab8f013 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -93,6 +93,7 @@ use syntax::{ast, ptr}; use syntax::codemap::{mk_sp, Span}; pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -> Option { + debug!("rewrite_chain {:?}", shape); let total_span = expr.span; let (parent, subexpr_list) = make_subexpr_list(expr, context); @@ -103,42 +104,45 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - } // Parent is the first item in the chain, e.g., `foo` in `foo.bar.baz()`. - let parent_block_indent = chain_base_indent(context, shape.indent); - let parent_context = &RewriteContext { block_indent: parent_block_indent, ..*context }; - let parent_rewrite = try_opt!(parent.rewrite(parent_context, shape)); + let mut parent_shape = shape; + if is_block_expr(&parent, "\n") { + parent_shape = chain_base_indent(context, shape); + } + let parent_rewrite = try_opt!(parent.rewrite(context, parent_shape)); // Decide how to layout the rest of the chain. `extend` is true if we can // put the first non-parent item on the same line as the parent. - let (indent, extend) = if !parent_rewrite.contains('\n') && is_continuable(&parent) || - parent_rewrite.len() <= context.config.tab_spaces { - - let indent = if let ast::ExprKind::Try(..) = subexpr_list.last().unwrap().node { - parent_block_indent.block_indent(context.config) + let (nested_shape, extend) = if !parent_rewrite.contains('\n') && is_continuable(&parent) { + let nested_shape = if let ast::ExprKind::Try(..) = subexpr_list.last().unwrap().node { + parent_shape.block_indent(context.config.tab_spaces) } else { - chain_indent(context, shape.indent + Indent::new(0, parent_rewrite.len())) + chain_indent(context, shape.add_offset(parent_rewrite.len())) }; - (indent, true) + (nested_shape, true) } else if is_block_expr(&parent, &parent_rewrite) { // The parent is a block, so align the rest of the chain with the closing // brace. - (parent_block_indent, false) + (parent_shape, false) } else if parent_rewrite.contains('\n') { - (chain_indent(context, parent_block_indent.block_indent(context.config)), false) + (chain_indent(context, parent_shape.block_indent(context.config.tab_spaces)), false) } else { - (chain_indent_newline(context, shape.indent + Indent::new(0, parent_rewrite.len())), false) + (chain_indent_newline(context, shape.add_offset(parent_rewrite.len())), false) }; - let max_width = try_opt!((shape.width + shape.indent.width()).checked_sub(indent.width())); + let max_width = try_opt!((shape.width + shape.indent.width() + shape.offset).checked_sub(nested_shape.indent.width() + nested_shape.offset)); + // The alignement in the shape is only used if we start the item on a new + // line, so we don't need to preserve the offset. + let child_shape = Shape { width: max_width, ..nested_shape }; + debug!("child_shape {:?}", child_shape); let mut rewrites = try_opt!(subexpr_list.iter() .rev() - .map(|e| rewrite_chain_subexpr(e, total_span, context, Shape::legacy(max_width, indent))) + .map(|e| rewrite_chain_subexpr(e, total_span, context, child_shape)) .collect::>>()); // Total of all items excluding the last. let almost_total = rewrites[..rewrites.len() - 1] .iter() .fold(0, |a, b| a + first_line_width(b)) + parent_rewrite.len(); - let total_width = almost_total + first_line_width(rewrites.last().unwrap()); let veto_single_line = if context.config.take_source_hints && subexpr_list.len() > 1 { // Look at the source code. Unless all chain elements start on the same @@ -152,7 +156,7 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - false }; - let mut fits_single_line = !veto_single_line && total_width <= shape.width; + let mut fits_single_line = !veto_single_line && almost_total <= shape.width; if fits_single_line { let len = rewrites.len(); let (init, last) = rewrites.split_at_mut(len - 1); @@ -178,7 +182,7 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - String::new() } else { // Use new lines. - format!("\n{}", indent.to_string(context.config)) + format!("\n{}", nested_shape.indent.to_string(context.config)) }; let first_connector = if extend || subexpr_list.is_empty() { @@ -211,7 +215,7 @@ pub fn rewrite_try(expr: &ast::Expr, context: &RewriteContext, shape: Shape) -> Option { - let sub_expr = try_opt!(expr.rewrite(context, shape.sub_width(try_count))); + let sub_expr = try_opt!(expr.rewrite(context, try_opt!(shape.sub_width(try_count)))); Some(format!("{}{}", sub_expr, iter::repeat("?").take(try_count).collect::())) @@ -268,29 +272,29 @@ fn make_subexpr_list(expr: &ast::Expr, context: &RewriteContext) -> (ast::Expr, (parent, subexpr_list) } -fn chain_base_indent(context: &RewriteContext, offset: Indent) -> Indent { +fn chain_base_indent(context: &RewriteContext, shape: Shape) -> Shape { match context.config.chain_base_indent { - BlockIndentStyle::Visual => offset, - BlockIndentStyle::Inherit => context.block_indent, - BlockIndentStyle::Tabbed => context.block_indent.block_indent(context.config), + BlockIndentStyle::Visual => shape, + BlockIndentStyle::Inherit => shape.block_indent(0), + BlockIndentStyle::Tabbed => shape.block_indent(context.config.tab_spaces), } } -fn chain_indent(context: &RewriteContext, offset: Indent) -> Indent { +fn chain_indent(context: &RewriteContext, shape: Shape) -> Shape { match context.config.chain_indent { - BlockIndentStyle::Visual => offset, - BlockIndentStyle::Inherit => context.block_indent, - BlockIndentStyle::Tabbed => context.block_indent.block_indent(context.config), + BlockIndentStyle::Visual => shape, + BlockIndentStyle::Inherit => shape.block_indent(0), + BlockIndentStyle::Tabbed => shape.block_indent(context.config.tab_spaces), } } // Ignores visual indenting because this function should be called where it is // not possible to use visual indentation because we are starting on a newline. -fn chain_indent_newline(context: &RewriteContext, _offset: Indent) -> Indent { +fn chain_indent_newline(context: &RewriteContext, shape: Shape) -> Shape { match context.config.chain_indent { - BlockIndentStyle::Inherit => context.block_indent, + BlockIndentStyle::Inherit => shape.block_indent(0), BlockIndentStyle::Visual | BlockIndentStyle::Tabbed => { - context.block_indent.block_indent(context.config) + shape.block_indent(context.config.tab_spaces) } } } @@ -303,7 +307,7 @@ fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind, shape: Shape) -> bool { if let &ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) = expr_kind { - let budget = match shape.width.checked_sub(almost_total) { + let shape = match shape.shrink_left(almost_total) { Some(b) => b, None => return false, }; @@ -312,8 +316,7 @@ fn rewrite_method_call_with_overflow(expr_kind: &ast::ExprKind, expressions, total_span, context, - Shape::legacy(budget, - shape.indent + almost_total)); + shape); if let Some(ref mut s) = last_rewrite { ::std::mem::swap(s, last); @@ -362,8 +365,7 @@ fn rewrite_chain_subexpr(expr: &ast::Expr, -> Option { match expr.node { ast::ExprKind::MethodCall(ref method_name, ref types, ref expressions) => { - let inner = &RewriteContext { block_indent: shape.indent, ..*context }; - rewrite_method_call(method_name.node, types, expressions, span, inner, shape) + rewrite_method_call(method_name.node, types, expressions, span, context, shape) } ast::ExprKind::Field(_, ref field) => { let s = format!(".{}", field.node); diff --git a/src/expr.rs b/src/expr.rs index 2498ee653afd..4f069ba78043 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -253,21 +253,20 @@ pub fn rewrite_pair(lhs: &LHS, where LHS: Rewrite, RHS: Rewrite { - let lhs_budget = try_opt!(shape.width.checked_sub(prefix.len() + infix.len())); - let rhs_budget = try_opt!(shape.width.checked_sub(suffix.len())); - // Get "full width" rhs and see if it fits on the current line. This // usually works fairly well since it tends to place operands of // operations with high precendence close together. // Note that this is non-conservative, but its just to see if it's even // worth trying to put everything on one line. - let rhs_result = rhs.rewrite(context, Shape::legacy(rhs_budget, shape.indent)); + let rhs_shape = try_opt!(shape.sub_width(suffix.len())); + let rhs_result = rhs.rewrite(context, rhs_shape); if let Some(rhs_result) = rhs_result { // This is needed in case of line break not caused by a // shortage of space, but by end-of-line comments, for example. if !rhs_result.contains('\n') { - let lhs_result = lhs.rewrite(context, Shape::legacy(lhs_budget, shape.indent)); + let lhs_shape = try_opt!(shape.sub_width(prefix.len() + infix.len())); + let lhs_result = lhs.rewrite(context, lhs_shape); if let Some(lhs_result) = lhs_result { let mut result = format!("{}{}{}", prefix, lhs_result, infix); @@ -281,14 +280,15 @@ pub fn rewrite_pair(lhs: &LHS, } // Try rewriting the rhs into the remaining space. - let rhs_budget = try_opt!(remaining_width.checked_sub(suffix.len())); - if let Some(rhs_result) = rhs.rewrite(context, - Shape::legacy(rhs_budget, - shape.indent + result.len())) { - if rhs_result.len() <= remaining_width { - result.push_str(&rhs_result); - result.push_str(suffix); - return Some(result); + let rhs_shape = shape.shrink_left(last_line_width(&result) + suffix.len()); + if let Some(rhs_shape) = rhs_shape { + if let Some(rhs_result) = rhs.rewrite(context, rhs_shape) { + // FIXME this should always hold. + if rhs_result.len() <= remaining_width { + result.push_str(&rhs_result); + result.push_str(suffix); + return Some(result); + } } } } @@ -301,17 +301,16 @@ pub fn rewrite_pair(lhs: &LHS, let infix = infix.trim_right(); let lhs_budget = try_opt!(context.config .max_width - .checked_sub(shape.indent.width() + prefix.len() + infix.len())); - let rhs_budget = try_opt!(rhs_budget.checked_sub(prefix.len())); - let rhs_offset = shape.indent + prefix.len(); + .checked_sub(shape.used_width() + prefix.len() + infix.len())); + let rhs_shape = try_opt!(shape.sub_width(suffix.len() + prefix.len())).visual_indent(prefix.len()); - let rhs_result = try_opt!(rhs.rewrite(context, Shape::legacy(rhs_budget, rhs_offset))); - let lhs_result = try_opt!(lhs.rewrite(context, Shape::legacy(lhs_budget, shape.indent))); + let rhs_result = try_opt!(rhs.rewrite(context, rhs_shape)); + let lhs_result = try_opt!(lhs.rewrite(context, Shape { width: lhs_budget, ..shape })); Some(format!("{}{}{}\n{}{}{}", prefix, lhs_result, infix, - rhs_offset.to_string(context.config), + rhs_shape.indent.to_string(context.config), rhs_result, suffix)) } @@ -328,16 +327,14 @@ pub fn rewrite_array<'a, I>(expr_iter: I, } else { 1 // "[" }; - let offset = shape.indent + bracket_size; - let inner_context = &RewriteContext { block_indent: offset, ..*context }; - let max_item_width = try_opt!(shape.width.checked_sub(bracket_size * 2)); + let nested_shape = try_opt!(shape.visual_indent(bracket_size).sub_width(bracket_size * 2)); let items = itemize_list(context.codemap, expr_iter, "]", |item| item.span.lo, |item| item.span.hi, - |item| item.rewrite(inner_context, Shape::legacy(max_item_width, offset)), + |item| item.rewrite(context, nested_shape), span.lo, span.hi) .collect::>(); @@ -347,7 +344,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I, .fold(Some(false), |acc, x| acc.and_then(|y| x.map(|x| x || y)))); let tactic = if has_long_item || items.iter().any(ListItem::is_multiline) { - definitive_tactic(&items, ListTactic::HorizontalVertical, max_item_width) + definitive_tactic(&items, ListTactic::HorizontalVertical, nested_shape.width) } else { DefinitiveListTactic::Mixed }; @@ -356,7 +353,7 @@ pub fn rewrite_array<'a, I>(expr_iter: I, tactic: tactic, separator: ",", trailing_separator: SeparatorTactic::Never, - shape: Shape::legacy(max_item_width, offset), + shape: nested_shape, ends_with_newline: false, config: context.config, }; @@ -390,17 +387,14 @@ fn rewrite_closure(capture: ast::CaptureBy, } else { "" }; - let offset = shape.indent + mover.len(); - // 4 = "|| {".len(), which is overconservative when the closure consists of // a single expression. - let budget = try_opt!(shape.width.checked_sub(4 + mover.len())); - // 1 = | - let argument_offset = offset + 1; - let ret_str = try_opt!(fn_decl.output.rewrite(context, Shape::legacy(budget, argument_offset))); + let nested_shape = try_opt!(try_opt!(shape.shrink_left(mover.len())).sub_width(4)); - // 1 = space between arguments and return type. - let horizontal_budget = budget.checked_sub(ret_str.len() + 1).unwrap_or(0); + // 1 = | + let argument_offset = nested_shape.indent + 1; + let arg_shape = try_opt!(nested_shape.shrink_left(1)).visual_indent(0); + let ret_str = try_opt!(fn_decl.output.rewrite(context, arg_shape)); let arg_items = itemize_list(context.codemap, @@ -408,21 +402,23 @@ fn rewrite_closure(capture: ast::CaptureBy, "|", |arg| span_lo_for_arg(arg), |arg| span_hi_for_arg(arg), - |arg| arg.rewrite(context, Shape::legacy(budget, argument_offset)), + |arg| arg.rewrite(context, arg_shape), context.codemap.span_after(span, "|"), body.span.lo); let item_vec = arg_items.collect::>(); + // 1 = space between arguments and return type. + let horizontal_budget = nested_shape.width.checked_sub(ret_str.len() + 1).unwrap_or(0); let tactic = definitive_tactic(&item_vec, ListTactic::HorizontalVertical, horizontal_budget); - let budget = match tactic { - DefinitiveListTactic::Horizontal => horizontal_budget, - _ => budget, + let arg_shape = match tactic { + DefinitiveListTactic::Horizontal => try_opt!(arg_shape.sub_width(ret_str.len() + 1)), + _ => arg_shape, }; let fmt = ListFormatting { tactic: tactic, separator: ",", trailing_separator: SeparatorTactic::Never, - shape: Shape::legacy(budget, argument_offset), + shape: arg_shape, ends_with_newline: false, config: context.config, }; @@ -440,12 +436,11 @@ fn rewrite_closure(capture: ast::CaptureBy, } // 1 = space between `|...|` and body. - let extra_offset = extra_offset(&prefix, offset) + 1; - let budget = try_opt!(shape.width.checked_sub(extra_offset)); - let total_offset = offset + extra_offset; + let extra_offset = extra_offset(&prefix, shape) + 1; + let body_shape = try_opt!(shape.sub_width(extra_offset)).add_offset(extra_offset); if let ast::ExprKind::Block(ref block) = body.node { - // The body of the closure is a block. + // The body of the closure is an empty block. if block.stmts.is_empty() && !block_contains_comment(block, context.codemap) { return Some(format!("{} {{}}", prefix)); } @@ -461,7 +456,7 @@ fn rewrite_closure(capture: ast::CaptureBy, if let Some(rw) = rewrite_closure_expr(expr, &prefix, context, - Shape::legacy(budget, total_offset)) { + body_shape) { return Some(rw); } } @@ -472,8 +467,7 @@ fn rewrite_closure(capture: ast::CaptureBy, let stmt = &block.stmts[0]; // 4 = braces and spaces. let mut rewrite = stmt.rewrite(context, - Shape::legacy(try_opt!(budget.checked_sub(4)), - total_offset)); + try_opt!(body_shape.sub_width(4))); // Checks if rewrite succeeded and fits on a single line. rewrite = and_one_line(rewrite); @@ -484,13 +478,14 @@ fn rewrite_closure(capture: ast::CaptureBy, } // Either we require a block, or tried without and failed. - return rewrite_closure_block(&block, prefix, context, budget); + let body_shape = shape.block(); + return rewrite_closure_block(&block, prefix, context, body_shape); } if let Some(rw) = rewrite_closure_expr(body, &prefix, context, - Shape::legacy(budget, total_offset)) { + body_shape) { return Some(rw); } @@ -506,7 +501,7 @@ fn rewrite_closure(capture: ast::CaptureBy, rules: ast::BlockCheckMode::Default, span: body.span, }; - return rewrite_closure_block(&block, prefix, context, budget); + return rewrite_closure_block(&block, prefix, context, body_shape.block()); fn rewrite_closure_expr(expr: &ast::Expr, prefix: &str, @@ -523,11 +518,11 @@ fn rewrite_closure(capture: ast::CaptureBy, fn rewrite_closure_block(block: &ast::Block, prefix: String, context: &RewriteContext, - budget: usize) + shape: Shape) -> Option { // Start with visual indent, then fall back to block indent if the // closure is large. - let rewrite = try_opt!(block.rewrite(&context, Shape::legacy(budget, Indent::empty()))); + let rewrite = try_opt!(block.rewrite(&context, shape)); let block_threshold = context.config.closure_block_indent_threshold; if block_threshold < 0 || rewrite.matches('\n').count() <= block_threshold as usize { @@ -536,9 +531,8 @@ fn rewrite_closure(capture: ast::CaptureBy, // The body of the closure is big enough to be block indented, that // means we must re-format. - let mut context = context.clone(); - context.block_indent.alignment = 0; - let rewrite = try_opt!(block.rewrite(&context, Shape::legacy(budget, Indent::empty()))); + let block_shape = shape.block(); + let rewrite = try_opt!(block.rewrite(&context, block_shape)); Some(format!("{} {}", prefix, rewrite)) } } @@ -581,7 +575,7 @@ impl Rewrite for ast::Block { } let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config); - visitor.block_indent = context.block_indent; + visitor.block_indent = shape.indent; let prefix = match self.rules { ast::BlockCheckMode::Unsafe(..) => { @@ -648,9 +642,7 @@ impl Rewrite for ast::Stmt { _ => unreachable!(), }, context, - Shape::legacy(context.config.max_width - shape.indent.width() - - suffix.len(), - shape.indent)) + try_opt!(shape.sub_width(suffix.len()))) .map(|s| s + suffix) } ast::StmtKind::Mac(..) | @@ -815,32 +807,32 @@ impl<'a> ControlFlow<'a> { impl<'a> Rewrite for ControlFlow<'a> { fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option { debug!("ControlFlow::rewrite {:?} {:?}", self, shape); - let (budget, indent) = if self.nested_if { + let constr_shape = if self.nested_if { // We are part of an if-elseif-else chain. Our constraints are tightened. // 7 = "} else " .len() - (try_opt!(shape.width.checked_sub(7)), shape.indent + 7) + try_opt!(shape.shrink_left(7)) } else { - (shape.width, shape.indent) + shape }; let label_string = rewrite_label(self.label); // 1 = space after keyword. - let inner_offset = indent + self.keyword.len() + label_string.len() + 1; - let mut inner_width = - try_opt!(budget.checked_sub(self.keyword.len() + label_string.len() + 1)); - if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine { - // 2 = " {".len() - inner_width = try_opt!(inner_width.checked_sub(2)); - } + let add_offset = self.keyword.len() + label_string.len() + 1; let pat_expr_string = match self.cond { Some(cond) => { + let mut cond_shape = try_opt!(constr_shape.shrink_left(add_offset)); + if context.config.control_brace_style != ControlBraceStyle::AlwaysNextLine { + // 2 = " {".len() + cond_shape = try_opt!(cond_shape.sub_width(2)); + } + try_opt!(rewrite_pat_expr(context, self.pat, cond, self.matcher, self.connector, - Shape::legacy(inner_width, inner_offset))) + cond_shape)) } None => String::new(), }; @@ -855,21 +847,24 @@ impl<'a> Rewrite for ControlFlow<'a> { } } + let used_width = if pat_expr_string.contains('\n') { + last_line_width(&pat_expr_string) + } else { + // 2 = spaces after keyword and condition. + label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 + }; + let block_width = try_opt!(shape.width.checked_sub(used_width)); // This is used only for the empty block case: `{}`. So, we use 1 if we know // we should avoid the single line case. - // 2 = spaces after keyword and condition. - let block_width = try_opt!(shape.width - .checked_sub(label_string.len() + self.keyword.len() + - extra_offset(&pat_expr_string, inner_offset) + - 2)); let block_width = if self.else_block.is_some() || self.nested_if { min(1, block_width) } else { block_width }; - let block_str = try_opt!(self.block - .rewrite(context, Shape::legacy(block_width, shape.indent))); + // TODO this .block() - not what we want if we are actually visually indented + let block_shape = Shape { width: block_width, ..shape }; + let block_str = try_opt!(self.block.rewrite(context, block_shape)); let cond_span = if let Some(cond) = self.cond { cond.span @@ -891,7 +886,7 @@ impl<'a> Rewrite for ControlFlow<'a> { let after_cond_comment = extract_comment(mk_sp(cond_span.hi, self.block.span.lo), context, shape); - let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); + let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config); let block_sep = if self.cond.is_none() && between_kwd_cond_comment.is_some() { "" } else if context.config.control_brace_style == @@ -913,14 +908,8 @@ impl<'a> Rewrite for ControlFlow<'a> { if let Some(else_block) = self.else_block { // Since this is an else block, we should not indent for the assignment preceding - // the original if, so set shape.indent.alignment to 0. - let shape = Shape { - width: shape.width, - indent: Indent { - block_indent: shape.indent.block_indent, - alignment: 0, - }, - }; + // the original if, so set shape.offset to 0. + let shape = Shape { offset: 0, ..shape }; let mut last_in_chain = false; let rewrite = match else_block.node { // If the else expression is another if-else expression, prevent it @@ -935,7 +924,7 @@ impl<'a> Rewrite for ControlFlow<'a> { false, true, mk_sp(else_block.span.lo, self.span.hi)) - .rewrite(context, shape) + .rewrite(context, shape.visual_indent(0)) } ast::ExprKind::If(ref cond, ref if_block, ref next_else_block) => { ControlFlow::new_if(cond, @@ -945,13 +934,14 @@ impl<'a> Rewrite for ControlFlow<'a> { false, true, mk_sp(else_block.span.lo, self.span.hi)) - .rewrite(context, shape) + .rewrite(context, shape.visual_indent(0)) } _ => { last_in_chain = true; // When rewriting a block, the width is only used for single line // blocks, passing 1 lets us avoid that. - else_block.rewrite(context, Shape::legacy(min(1, shape.width), shape.indent)) + let else_shape = Shape { width: min(1, shape.width), ..shape }; + else_block.rewrite(context, else_shape) } }; @@ -1097,16 +1087,15 @@ fn rewrite_match(context: &RewriteContext, // `match `cond` {` let cond_budget = try_opt!(shape.width.checked_sub(8)); let cond_str = try_opt!(cond.rewrite(context, Shape::legacy(cond_budget, shape.indent + 6))); - let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); + let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config); let block_sep = match context.config.control_brace_style { ControlBraceStyle::AlwaysSameLine => " ", _ => alt_block_sep.as_str(), }; let mut result = format!("match {}{}{{", cond_str, block_sep); - let nested_context = context.nested_context(); - let arm_indent = nested_context.block_indent; - let arm_indent_str = arm_indent.to_string(context.config); + let arm_shape = shape.block_indent(context.config.tab_spaces); + let arm_indent_str = arm_shape.indent.to_string(context.config); let open_brace_pos = context.codemap .span_after(mk_sp(cond.span.hi, arm_start_pos(&arms[0])), "{"); @@ -1120,15 +1109,14 @@ fn rewrite_match(context: &RewriteContext, }; let comment = try_opt!(rewrite_match_arm_comment(context, &missed_str, - Shape::legacy(shape.width, arm_indent), + arm_shape, &arm_indent_str)); result.push_str(&comment); result.push('\n'); result.push_str(&arm_indent_str); - let arm_str = arm.rewrite(&nested_context, - Shape::legacy(context.config.max_width - arm_indent.width(), - arm_indent)); + let arm_str = arm.rewrite(&context, + Shape { width: context.config.max_width - arm_shape.indent.width(), .. arm_shape }); if let Some(ref arm_str) = arm_str { result.push_str(arm_str); } else { @@ -1143,11 +1131,11 @@ fn rewrite_match(context: &RewriteContext, let last_comment = context.snippet(last_span); let comment = try_opt!(rewrite_match_arm_comment(context, &last_comment, - Shape::legacy(shape.width, arm_indent), + arm_shape, &arm_indent_str)); result.push_str(&comment); result.push('\n'); - result.push_str(&context.block_indent.to_string(context.config)); + result.push_str(&shape.indent.to_string(context.config)); result.push('}'); Some(result) } @@ -1197,7 +1185,7 @@ impl Rewrite for ast::Arm { // We only use this visitor for the attributes, should we use it for // more? let mut attr_visitor = FmtVisitor::from_codemap(context.parse_session, context.config); - attr_visitor.block_indent = context.block_indent; + attr_visitor.block_indent = shape.indent.block_only(); attr_visitor.last_pos = attrs[0].span.lo; if attr_visitor.visit_attrs(attrs) { // Attributes included a skip instruction. @@ -1211,9 +1199,10 @@ impl Rewrite for ast::Arm { // Patterns // 5 = ` => {` - let pat_budget = try_opt!(shape.width.checked_sub(5)); + let pat_shape = try_opt!(shape.sub_width(5)); + let pat_strs = try_opt!(pats.iter() - .map(|p| p.rewrite(context, Shape::legacy(pat_budget, shape.indent))) + .map(|p| p.rewrite(context, pat_shape)) .collect::>>()); let all_simple = pat_strs.iter().all(|p| pat_is_simple(p)); @@ -1226,29 +1215,26 @@ impl Rewrite for ast::Arm { }, separator: " |", trailing_separator: SeparatorTactic::Never, - shape: Shape::legacy(pat_budget, shape.indent), + shape: pat_shape, ends_with_newline: false, config: context.config, }; let pats_str = try_opt!(write_list(items, &fmt)); - let budget = if pats_str.contains('\n') { - context.config.max_width - shape.indent.width() + let guard_shape = if pats_str.contains('\n') { + Shape { width: context.config.max_width - shape.indent.width(), ..shape } } else { - shape.width + shape }; let guard_str = try_opt!(rewrite_guard(context, guard, - Shape::legacy(budget, shape.indent), + guard_shape, trimmed_last_line_width(&pats_str))); let pats_str = format!("{}{}", pats_str, guard_str); // Where the next text can start. - let mut line_start = last_line_width(&pats_str); - if !pats_str.contains('\n') { - line_start += shape.indent.width(); - } + let mut line_start = trimmed_last_line_width(&pats_str); let body = match body.node { ast::ExprKind::Block(ref block) if !is_unsafe_block(block) && @@ -1264,16 +1250,17 @@ impl Rewrite for ast::Arm { }; let comma = arm_comma(&context.config, self, body); - let alt_block_sep = String::from("\n") + &context.block_indent.to_string(context.config); + let alt_block_sep = String::from("\n") + &shape.indent.block_only().to_string(context.config); // Let's try and get the arm body on the same line as the condition. // 4 = ` => `.len() - if context.config.max_width > line_start + comma.len() + 4 { - let budget = context.config.max_width - line_start - comma.len() - 4; - let offset = Indent::new(shape.indent.block_indent, - line_start + 4 - shape.indent.block_indent); - let rewrite = nop_block_collapse(body.rewrite(context, Shape::legacy(budget, offset)), - budget); + if shape.width > line_start + comma.len() + 4 { + let arm_shape = shape.shrink_left(line_start + 4).unwrap().sub_width(comma.len()).unwrap().block(); + // TODO + // let offset = Indent::new(shape.indent.block_indent, + // line_start + 4 - shape.indent.block_indent); + let rewrite = nop_block_collapse(body.rewrite(context, arm_shape), + arm_shape.width); let is_block = if let ast::ExprKind::Block(..) = body.node { true } else { @@ -1301,13 +1288,12 @@ impl Rewrite for ast::Arm { // FIXME: we're doing a second rewrite of the expr; This may not be // necessary. - let body_budget = try_opt!(shape.width.checked_sub(context.config.tab_spaces)); - let indent = context.block_indent.block_indent(context.config); - let inner_context = &RewriteContext { block_indent: indent, ..*context }; - let next_line_body = try_opt!(nop_block_collapse(body.rewrite(inner_context, - Shape::legacy(body_budget, - indent)), - body_budget)); + // TODO + // let body_budget = try_opt!(shape.width.checked_sub(context.config.tab_spaces)); + // let indent = shape.indent.block_only().block_indent(context.config); + let body_shape = try_opt!(shape.sub_width(context.config.tab_spaces)).block_indent(context.config.tab_spaces); + let next_line_body = try_opt!(nop_block_collapse(body.rewrite(context, body_shape), + body_shape.width)); let indent_str = shape.indent.block_indent(context.config).to_string(context.config); let (body_prefix, body_suffix) = if context.config.wrap_match_arms { if context.config.match_block_trailing_comma { @@ -1394,6 +1380,7 @@ fn rewrite_pat_expr(context: &RewriteContext, connector: &str, shape: Shape) -> Option { + debug!("rewrite_pat_expr {:?}", shape); let mut result = match pat { Some(pat) => { let matcher = if matcher.is_empty() { @@ -1401,26 +1388,22 @@ fn rewrite_pat_expr(context: &RewriteContext, } else { format!("{} ", matcher) }; - let pat_budget = try_opt!(shape.width.checked_sub(connector.len() + matcher.len())); - let pat_offset = shape.indent + matcher.len(); - let pat_string = try_opt!(pat.rewrite(context, Shape::legacy(pat_budget, pat_offset))); + let pat_shape = try_opt!(try_opt!(shape.shrink_left(matcher.len())).sub_width(connector.len())); + let pat_string = try_opt!(pat.rewrite(context, pat_shape)); format!("{}{}{}", matcher, pat_string, connector) } None => String::new(), }; // Consider only the last line of the pat string. - let extra_offset = extra_offset(&result, shape.indent); + let extra_offset = extra_offset(&result, shape); - // The expression may (partionally) fit on the current line. + // The expression may (partially) fit on the current line. if shape.width > extra_offset + 1 { let spacer = if pat.is_some() { " " } else { "" }; - let expr_rewrite = expr.rewrite(context, - Shape::legacy(try_opt!(shape.width - .checked_sub(extra_offset + - spacer.len())), - shape.indent + extra_offset + spacer.len())); + let expr_shape = try_opt!(shape.sub_width(extra_offset + spacer.len())).add_offset(extra_offset + spacer.len()); + let expr_rewrite = expr.rewrite(context, expr_shape); if let Some(expr_string) = expr_rewrite { let pat_simple = pat.and_then(|p| { @@ -1437,17 +1420,17 @@ fn rewrite_pat_expr(context: &RewriteContext, } } - let nested = context.nested_context(); + let nested_indent = shape.indent.block_only().block_indent(context.config); // The expression won't fit on the current line, jump to next. result.push('\n'); - result.push_str(&nested.block_indent.to_string(context.config)); + result.push_str(&nested_indent.to_string(context.config)); - let expr_rewrite = expr.rewrite(&nested, + let expr_rewrite = expr.rewrite(&context, Shape::legacy(try_opt!(context.config .max_width - .checked_sub(nested.block_indent.width())), - nested.block_indent)); + .checked_sub(nested_indent.width())), + nested_indent)); result.push_str(&try_opt!(expr_rewrite)); Some(result) @@ -1533,7 +1516,7 @@ fn rewrite_call_inner(context: &RewriteContext, let callee = callee.borrow(); // FIXME using byte lens instead of char lens (and probably all over the // place too) - let callee_str = match callee.rewrite(context, Shape::legacy(max_callee_width, shape.indent)) { + let callee_str = match callee.rewrite(context, Shape { width: max_callee_width, ..shape }) { Some(string) => { if !string.contains('\n') && string.len() > max_callee_width { panic!("{:?} {}", string, max_callee_width); @@ -1547,20 +1530,15 @@ fn rewrite_call_inner(context: &RewriteContext, let span_lo = context.codemap.span_after(span, "("); let span = mk_sp(span_lo, span.hi); - let extra_offset = extra_offset(&callee_str, shape.indent); + let used_width = extra_offset(&callee_str, shape); // 2 is for parens. - let remaining_width = match shape.width.checked_sub(extra_offset + 2) { - Some(str) => str, + let remaining_width = match shape.width.checked_sub(used_width + 2) { + Some(s) => s, None => return Err(Ordering::Greater), }; - let offset = shape.indent + extra_offset + 1; + // 1 = ( + let nested_shape = shape.visual_indent(used_width + 1); let arg_count = args.len(); - let block_indent = if arg_count == 1 { - context.block_indent - } else { - offset - }; - let inner_context = &RewriteContext { block_indent: block_indent, ..*context }; let items = itemize_list(context.codemap, @@ -1568,7 +1546,7 @@ fn rewrite_call_inner(context: &RewriteContext, ")", |item| item.span.lo, |item| item.span.hi, - |item| item.rewrite(inner_context, Shape::legacy(remaining_width, offset)), + |item| item.rewrite(context, Shape { width: remaining_width, ..nested_shape }), span.lo, span.hi); let mut item_vec: Vec<_> = items.collect(); @@ -1588,9 +1566,12 @@ fn rewrite_call_inner(context: &RewriteContext, // Replace the last item with its first line to see if it fits with // first arguments. if overflow_last { - let inner_context = &RewriteContext { block_indent: context.block_indent, ..*context }; - let rewrite = - args.last().unwrap().rewrite(inner_context, Shape::legacy(remaining_width, offset)); + let nested_shape = Shape { + width: remaining_width, + indent: nested_shape.indent.block_only(), + ..nested_shape + }; + let rewrite = args.last().unwrap().rewrite(context, nested_shape); if let Some(rewrite) = rewrite { let rewrite_first_line = Some(rewrite[..first_line_width(&rewrite)].to_owned()); @@ -1622,7 +1603,7 @@ fn rewrite_call_inner(context: &RewriteContext, tactic: tactic, separator: ",", trailing_separator: SeparatorTactic::Never, - shape: Shape::legacy(shape.width, offset), + shape: nested_shape, ends_with_newline: false, config: context.config, }; @@ -1643,9 +1624,8 @@ fn rewrite_paren(context: &RewriteContext, subexpr: &ast::Expr, shape: Shape) -> debug!("rewrite_paren, shape: {:?}", shape); // 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, - Shape::legacy(try_opt!(shape.width.checked_sub(2)), - shape.indent + 1)); + let sub_shape = try_opt!(shape.sub_width(2)).visual_indent(1); + let subexpr_str = subexpr.rewrite(context, sub_shape); debug!("rewrite_paren, subexpr_str: `{:?}`", subexpr_str); subexpr_str.map(|s| if context.config.spaces_within_parens && s.len() > 0 { @@ -1699,24 +1679,20 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, } // 2 = " {".len() - let path_budget = try_opt!(shape.width.checked_sub(2)); + let path_shape = try_opt!(shape.sub_width(2)); let path_str = try_opt!(rewrite_path(context, PathContext::Expr, None, path, - Shape::legacy(path_budget, shape.indent))); + path_shape)); // Foo { a: Foo } - indent is +3, width is -5. - let h_budget = shape.width.checked_sub(path_str.len() + 5).unwrap_or(0); - // The 1 taken from the v_budget is for the comma. - let (indent, v_budget) = match context.config.struct_lit_style { - StructLitStyle::Visual => (shape.indent + path_str.len() + 3, h_budget), + let h_shape = shape.sub_width(path_str.len() + 5); + let v_shape = match context.config.struct_lit_style { + StructLitStyle::Visual => try_opt!(try_opt!(shape.shrink_left(path_str.len() + 3)).sub_width(2)), 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.block_indent(context.config); - let v_budget = context.config.max_width.checked_sub(indent.width()).unwrap_or(0); - (indent, v_budget) + let shape = shape.block_indent(context.config.tab_spaces); + Shape { width: try_opt!(context.config.max_width.checked_sub(shape.indent.width())), ..shape } } }; @@ -1724,8 +1700,6 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, .map(StructLitField::Regular) .chain(base.into_iter().map(StructLitField::Base)); - let inner_context = &RewriteContext { block_indent: indent, ..*context }; - let items = itemize_list(context.codemap, field_iter, "}", @@ -1747,14 +1721,15 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, |item| { match *item { StructLitField::Regular(field) => { - rewrite_field(inner_context, + // The 1 taken from the v_budget is for the comma. + rewrite_field(context, field, - Shape::legacy(v_budget.checked_sub(1).unwrap_or(0), indent)) + try_opt!(v_shape.sub_width(1))) } StructLitField::Base(expr) => { // 2 = .. - expr.rewrite(inner_context, - Shape::legacy(try_opt!(v_budget.checked_sub(2)), indent + 2)) + expr.rewrite(context, + try_opt!(v_shape.shrink_left(2))) .map(|s| format!("..{}", s)) } } @@ -1763,7 +1738,7 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, span.hi); let item_vec = items.collect::>(); - let tactic = { + let tactic = if let Some(h_shape) = h_shape { let mut prelim_tactic = match (context.config.struct_lit_style, fields.len()) { (StructLitStyle::Visual, 1) => ListTactic::HorizontalVertical, _ => context.config.struct_lit_multiline_style.to_list_tactic(), @@ -1773,12 +1748,14 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, prelim_tactic = ListTactic::LimitedHorizontalVertical(context.config.struct_lit_width); } - definitive_tactic(&item_vec, prelim_tactic, h_budget) + definitive_tactic(&item_vec, prelim_tactic, h_shape.width) + } else { + DefinitiveListTactic::Vertical }; - let budget = match tactic { - DefinitiveListTactic::Horizontal => h_budget, - _ => v_budget, + let nested_shape = match tactic { + DefinitiveListTactic::Horizontal => h_shape.unwrap(), + _ => v_shape, }; let ends_with_newline = context.config.struct_lit_style != StructLitStyle::Visual && @@ -1792,36 +1769,33 @@ fn rewrite_struct_lit<'a>(context: &RewriteContext, } else { context.config.struct_lit_trailing_comma }, - shape: Shape::legacy(budget, indent), + shape: nested_shape, ends_with_newline: ends_with_newline, config: context.config, }; let fields_str = try_opt!(write_list(&item_vec, &fmt)); + // Empty struct. if fields_str.is_empty() { return Some(format!("{} {{}}", path_str)); } - let format_on_newline = || { - 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)) - }; - - match (context.config.struct_lit_style, context.config.struct_lit_multiline_style) { - (StructLitStyle::Block, _) if fields_str.contains('\n') || fields_str.len() > h_budget => { - format_on_newline() - } - (StructLitStyle::Block, MultilineStyle::ForceMulti) => format_on_newline(), - _ => Some(format!("{} {{ {} }}", path_str, fields_str)), + // One liner or visual indent. + if context.config.struct_lit_style == StructLitStyle::Visual || + (context.config.struct_lit_multiline_style != MultilineStyle::ForceMulti && + !fields_str.contains('\n') && + fields_str.len() <= h_shape.map(|s| s.width).unwrap_or(0)) { + return Some(format!("{} {{ {} }}", path_str, fields_str)); } + // Multiple lines. + let inner_indent = v_shape.indent.to_string(context.config); + let outer_indent = shape.indent.to_string(context.config); + Some(format!("{} {{\n{}{}\n{}}}", + path_str, + inner_indent, + fields_str, + outer_indent)) // FIXME if context.config.struct_lit_style == Visual, but we run out // of space, we should fall back to BlockIndent. } @@ -1842,9 +1816,9 @@ fn rewrite_field(context: &RewriteContext, field: &ast::Field, shape: Shape) -> } else { let separator = type_annotation_separator(context.config); let overhead = name.len() + separator.len(); - let expr = field.expr.rewrite(context, - Shape::legacy(try_opt!(shape.width.checked_sub(overhead)), - shape.indent + overhead)); + let mut expr_shape = try_opt!(shape.sub_width(overhead)); + expr_shape.offset += overhead; + let expr = field.expr.rewrite(context, expr_shape); match expr { Some(e) => Some(format!("{}{}{}", name, separator, e)), @@ -1871,16 +1845,14 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext, ::Item: Deref, ::Target: Rewrite + Spanned + 'a { - let indent = shape.indent + 1; - let aligned = RewriteContext { block_indent: indent, ..context.clone() }; - + debug!("rewrite_tuple {:?}", shape); // In case of length 1, need a trailing comma if items.len() == 1 { // 3 = "(" + ",)" - let budget = try_opt!(shape.width.checked_sub(3)); + let nested_shape = try_opt!(shape.sub_width(3)).visual_indent(1); return items.next() .unwrap() - .rewrite(&aligned, Shape::legacy(budget, indent)) + .rewrite(context, nested_shape) .map(|s| if context.config.spaces_within_parens { format!("( {}, )", s) } else { @@ -1889,16 +1861,16 @@ pub fn rewrite_tuple<'a, I>(context: &RewriteContext, } let list_lo = context.codemap.span_after(span, "("); - let budget = try_opt!(shape.width.checked_sub(2)); + let nested_shape = try_opt!(shape.sub_width(2)).visual_indent(1); let items = itemize_list(context.codemap, items, ")", |item| item.span().lo, |item| item.span().hi, - |item| item.rewrite(&aligned, Shape::legacy(budget, indent)), + |item| item.rewrite(context, nested_shape), list_lo, span.hi - BytePos(1)); - let list_str = try_opt!(format_item_list(items, Shape::legacy(budget, indent), context.config)); + let list_str = try_opt!(format_item_list(items, nested_shape, context.config)); if context.config.spaces_within_parens && list_str.len() > 0 { Some(format!("( {} )", list_str)) @@ -1912,10 +1884,8 @@ pub fn rewrite_unary_prefix(context: &RewriteContext, rewrite: &R, shape: Shape) -> Option { - rewrite.rewrite(context, - Shape::legacy(try_opt!(shape.width.checked_sub(prefix.len())), - shape.indent + prefix.len())) - .map(|r| format!("{}{}", prefix, r)) + let shape = try_opt!(shape.shrink_left(prefix.len())).visual_indent(0); + rewrite.rewrite(context, shape).map(|r| format!("{}{}", prefix, r)) } // FIXME: this is probably not correct for multi-line Rewrites. we should @@ -1925,9 +1895,7 @@ pub fn rewrite_unary_suffix(context: &RewriteContext, rewrite: &R, shape: Shape) -> Option { - rewrite.rewrite(context, - Shape::legacy(try_opt!(shape.width.checked_sub(suffix.len())), - shape.indent)) + rewrite.rewrite(context, try_opt!(shape.sub_width(suffix.len()))) .map(|mut r| { r.push_str(suffix); r @@ -1960,9 +1928,9 @@ fn rewrite_assignment(context: &RewriteContext, }; // 1 = space between lhs and operator. - let max_width = try_opt!(shape.width.checked_sub(operator_str.len() + 1)); + let lhs_shape = try_opt!(shape.sub_width(operator_str.len() + 1)); let lhs_str = format!("{} {}", - try_opt!(lhs.rewrite(context, Shape::legacy(max_width, shape.indent))), + try_opt!(lhs.rewrite(context, lhs_shape)), operator_str); rewrite_assign_rhs(context, lhs_str, rhs, shape) @@ -1985,7 +1953,9 @@ pub fn rewrite_assign_rhs>(context: &RewriteContext, // 1 = space between operator and rhs. let max_width = try_opt!(shape.width.checked_sub(last_line_width + 1)); let rhs = ex.rewrite(context, - Shape::legacy(max_width, shape.indent + last_line_width + 1)); + Shape::offset(max_width, + shape.indent.block_only(), + shape.indent.alignment + last_line_width + 1)); fn count_line_breaks(src: &str) -> usize { src.chars().filter(|&x| x == '\n').count() @@ -2003,8 +1973,7 @@ pub fn rewrite_assign_rhs>(context: &RewriteContext, let new_offset = shape.indent.block_indent(context.config); let max_width = try_opt!((shape.width + shape.indent.width()) .checked_sub(new_offset.width())); - let inner_context = context.nested_context(); - let new_rhs = ex.rewrite(&inner_context, Shape::legacy(max_width, new_offset)); + let new_rhs = ex.rewrite(context, Shape::legacy(max_width, new_offset)); // FIXME: DRY! match (rhs, new_rhs) { diff --git a/src/items.rs b/src/items.rs index 3c5bf3ce6cee..1a27b5bfbc95 100644 --- a/src/items.rs +++ b/src/items.rs @@ -70,12 +70,12 @@ impl Rewrite for ast::Local { if let Some(ref ex) = self.init { // 1 = trailing semicolon; - let budget = try_opt!(shape.width.checked_sub(context.block_indent.width() + 1)); + let budget = try_opt!(shape.width.checked_sub(shape.indent.block_only().width() + 1)); result = try_opt!(rewrite_assign_rhs(&context, result, ex, - Shape::legacy(budget, context.block_indent))); + Shape::legacy(budget, shape.indent.block_only()))); } result.push(';'); @@ -520,7 +520,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) - context.config, context.config.item_brace_style, Shape::legacy(where_budget, - context.block_indent), + offset.block_only()), context.config.where_density, "{", true, @@ -539,7 +539,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) - if !where_clause_str.is_empty() && !where_clause_str.contains('\n') { result.push('\n'); - let width = context.block_indent.width() + context.config.tab_spaces - 1; + let width = offset.block_indent + context.config.tab_spaces - 1; let where_indent = Indent::new(0, width); result.push_str(&where_indent.to_string(context.config)); } @@ -568,7 +568,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) - if !items.is_empty() || contains_comment(&snippet[open_pos..]) { let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config); - visitor.block_indent = context.block_indent.block_indent(context.config); + visitor.block_indent = offset.block_only().block_indent(context.config); visitor.last_pos = item.span.lo + BytePos(open_pos as u32); for item in items { @@ -578,7 +578,7 @@ pub fn format_impl(context: &RewriteContext, item: &ast::Item, offset: Indent) - visitor.format_missing(item.span.hi - BytePos(1)); let inner_indent_str = visitor.block_indent.to_string(context.config); - let outer_indent_str = context.block_indent.to_string(context.config); + let outer_indent_str = offset.block_only().to_string(context.config); result.push('\n'); result.push_str(&inner_indent_str); @@ -654,7 +654,7 @@ fn format_impl_ref_and_type(context: &RewriteContext, result.push('\n'); // Add indentation of one additional tab. - let width = context.block_indent.width() + context.config.tab_spaces; + let width = offset.block_indent + context.config.tab_spaces; let for_indent = Indent::new(0, width); result.push_str(&for_indent.to_string(context.config)); @@ -762,7 +762,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) if offset.width() + last_line_width(&result) + trait_bound_str.len() > context.config.ideal_width { result.push('\n'); - let trait_indent = context.block_indent.block_indent(context.config); + let trait_indent = offset.block_only().block_indent(context.config); result.push_str(&trait_indent.to_string(context.config)); } result.push_str(&trait_bound_str); @@ -789,7 +789,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) context.config, context.config.item_brace_style, Shape::legacy(where_budget, - context.block_indent), + offset.block_only()), where_density, "{", has_body, @@ -800,7 +800,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) last_line_width(&result) + where_clause_str.len() + offset.width() > context.config.ideal_width { result.push('\n'); - let width = context.block_indent.width() + context.config.tab_spaces - 1; + let width = offset.block_indent + context.config.tab_spaces - 1; let where_indent = Indent::new(0, width); result.push_str(&where_indent.to_string(context.config)); } @@ -829,7 +829,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) if !trait_items.is_empty() || contains_comment(&snippet[open_pos..]) { let mut visitor = FmtVisitor::from_codemap(context.parse_session, context.config); - visitor.block_indent = context.block_indent.block_indent(context.config); + visitor.block_indent = offset.block_only().block_indent(context.config); visitor.last_pos = item.span.lo + BytePos(open_pos as u32); for item in trait_items { @@ -839,7 +839,7 @@ pub fn format_trait(context: &RewriteContext, item: &ast::Item, offset: Indent) visitor.format_missing(item.span.hi - BytePos(1)); let inner_indent_str = visitor.block_indent.to_string(context.config); - let outer_indent_str = context.block_indent.to_string(context.config); + let outer_indent_str = offset.block_only().to_string(context.config); result.push('\n'); result.push_str(&inner_indent_str); @@ -892,7 +892,7 @@ fn format_struct_struct(context: &RewriteContext, } None => { if context.config.item_brace_style == BraceStyle::AlwaysNextLine && !fields.is_empty() { - format!("\n{}{{", context.block_indent.to_string(context.config)) + format!("\n{}{{", offset.block_only().to_string(context.config)) } else { " {".to_owned() } @@ -1004,7 +1004,7 @@ fn format_tuple_struct(context: &RewriteContext, &generics.where_clause, context.config, context.config.item_brace_style, - Shape::legacy(where_budget, context.block_indent), + Shape::legacy(where_budget, offset.block_only()), Density::Compressed, ";", false, @@ -1014,7 +1014,7 @@ fn format_tuple_struct(context: &RewriteContext, }; result.push('('); - let item_indent = context.block_indent + result.len(); + let item_indent = offset.block_only() + result.len(); // 2 = ");" let item_budget = try_opt!(context.config.max_width.checked_sub(item_indent.width() + 2)); @@ -1052,12 +1052,12 @@ fn format_tuple_struct(context: &RewriteContext, if !where_clause_str.is_empty() && !where_clause_str.contains('\n') && (result.contains('\n') || - context.block_indent.width() + result.len() + where_clause_str.len() + 1 > + offset.block_indent + result.len() + where_clause_str.len() + 1 > context.config.max_width) { // We need to put the where clause on a new line, but we didn'to_string // know that earlier, so the where clause will not be indented properly. result.push('\n'); - result.push_str(&(context.block_indent + (context.config.tab_spaces - 1)) + result.push_str(&(offset.block_only() + (context.config.tab_spaces - 1)) .to_string(context.config)); } result.push_str(&where_clause_str); @@ -1192,6 +1192,7 @@ pub fn rewrite_static(prefix: &str, ty: &ast::Ty, mutability: ast::Mutability, expr_opt: Option<&ptr::P>, + offset: Indent, context: &RewriteContext) -> Option { let type_annotation_spacing = type_annotation_spacing(context.config); @@ -1205,19 +1206,19 @@ pub fn rewrite_static(prefix: &str, // 2 = " =".len() let ty_str = try_opt!(ty.rewrite(context, Shape::legacy(context.config.max_width - - context.block_indent.width() - + offset.block_indent - prefix.len() - 2, - context.block_indent))); + offset.block_only()))); if let Some(expr) = expr_opt { let lhs = format!("{}{} =", prefix, ty_str); // 1 = ; - let remaining_width = context.config.max_width - context.block_indent.width() - 1; + let remaining_width = context.config.max_width - offset.block_indent - 1; rewrite_assign_rhs(context, lhs, expr, - Shape::legacy(remaining_width, context.block_indent)) + Shape::legacy(remaining_width, offset.block_only())) .map(|s| s + ";") } else { let lhs = format!("{}{};", prefix, ty_str); @@ -1253,10 +1254,10 @@ pub fn rewrite_associated_type(ident: ast::Ident, if let Some(ty) = ty_opt { let ty_str = try_opt!(ty.rewrite(context, Shape::legacy(context.config.max_width - - context.block_indent.width() - + indent.block_indent - prefix.len() - 2, - context.block_indent))); + indent.block_only()))); Some(format!("{} = {};", prefix, ty_str)) } else { Some(format!("{}{};", prefix, type_bounds_str)) @@ -1473,7 +1474,7 @@ fn rewrite_fn_base(context: &RewriteContext, multi_line_budget = context.config.max_width - arg_indent.width(); } - debug!("rewrite_fn: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}", + debug!("rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, arg_indent: {:?}", one_line_budget, multi_line_budget, arg_indent); @@ -2020,7 +2021,7 @@ fn format_generics(context: &RewriteContext, context.config, brace_style, Shape::legacy(budget, - context.block_indent), + offset.block_only()), Density::Tall, terminator, true, @@ -2029,7 +2030,7 @@ fn format_generics(context: &RewriteContext, if !force_same_line_brace && (brace_style == BraceStyle::SameLineWhere || brace_style == BraceStyle::AlwaysNextLine) { result.push('\n'); - result.push_str(&context.block_indent.to_string(context.config)); + result.push_str(&offset.block_only().to_string(context.config)); } else { result.push(' '); } @@ -2037,7 +2038,7 @@ fn format_generics(context: &RewriteContext, } else { if !force_same_line_brace && brace_style == BraceStyle::AlwaysNextLine { result.push('\n'); - result.push_str(&context.block_indent.to_string(context.config)); + result.push_str(&offset.block_only().to_string(context.config)); } else { result.push(' '); } diff --git a/src/lib.rs b/src/lib.rs index abe9a6e8554e..43054698b2d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,6 +133,13 @@ impl Indent { Indent::new(0, 0) } + pub fn block_only(&self) -> Indent { + Indent { + block_indent: self.block_indent, + alignment: 0, + } + } + pub fn block_indent(mut self, config: &Config) -> Indent { self.block_indent += config.tab_spaces; self @@ -204,7 +211,11 @@ impl Sub for Indent { #[derive(Copy, Clone, Debug)] pub struct Shape { pub width: usize, + // The current indentation of code. pub indent: Indent, + // Indentation + any already emitted text on the first line of the current + // statement. + pub offset: usize, } impl Shape { @@ -212,6 +223,7 @@ impl Shape { Shape { width: config.max_width, indent: indent, + offset: indent.width(), } } @@ -234,14 +246,92 @@ impl Shape { Shape { width: width, indent: indent, + offset: indent.alignment, } } - pub fn sub_width(self, width: usize) -> Shape { + pub fn offset(width: usize, indent: Indent, offset: usize) -> Shape { Shape { - width: self.width - width, - indent: self.indent, + width: width, + indent: indent, + offset: offset, } + } + + pub fn visual_indent(&self, extra_width: usize) -> Shape { + let alignment = self.offset + extra_width; + Shape { + width: self.width, + indent: Indent { + block_indent: self.indent.block_indent, + alignment: alignment, + }, + offset: alignment, + } + } + + pub fn block_indent(&self, extra_width: usize) -> Shape { + if self.indent.alignment == 0 { + Shape { + width: self.width, + indent: Indent { + block_indent: self.indent.block_indent + extra_width, + alignment: 0, + }, + offset: 0, + } + } else { + Shape { + width: self.width, + indent: Indent { + block_indent: self.indent.block_indent, + alignment: self.indent.alignment + extra_width, + }, + offset: self.indent.alignment + extra_width, + } + } + } + + pub fn add_offset(&self, extra_width: usize) -> Shape { + Shape { + width: self.width, + indent: Indent { + block_indent: self.indent.block_indent, + alignment: self.indent.alignment, + }, + offset: self.offset + extra_width, + } + } + + pub fn block(&self) -> Shape { + Shape { + width: self.width, + indent: Indent { + block_indent: self.indent.block_indent, + alignment: 0, + }, + offset: self.offset, + } + } + + pub fn sub_width(&self, width: usize) -> Option { + Some(Shape { + width: try_opt!(self.width.checked_sub(width)), + indent: self.indent, + offset: self.offset, + }) + } + + pub fn shrink_left(&self, width: usize) -> Option { + Some(Shape { + width: try_opt!(self.width.checked_sub(width)), + indent: self.indent + width, + offset: self.offset + width, + }) + } + + pub fn used_width(&self) -> usize { + self.indent.block_indent + self.offset } } diff --git a/src/macros.rs b/src/macros.rs index 3a184d242ef5..97c60adb6841 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -155,15 +155,14 @@ pub fn rewrite_macro(mac: &ast::Mac, MacroStyle::Brackets => { // Format macro invocation as array literal. let extra_offset = macro_name.len(); + let shape = try_opt!(shape.shrink_left(extra_offset)); let rewrite = try_opt!(rewrite_array(expr_vec.iter().map(|x| &**x), mk_sp(context.codemap .span_after(mac.span, original_style.opener()), mac.span.hi - BytePos(1)), context, - Shape::legacy(try_opt!(shape.width - .checked_sub(extra_offset)), - shape.indent + extra_offset))); + shape)); Some(format!("{}{}", macro_name, rewrite)) } diff --git a/src/patterns.rs b/src/patterns.rs index 7346dc08010c..73b54a666268 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -220,16 +220,16 @@ fn rewrite_tuple_pat(pats: &[ptr::P], let path_len = path_str.as_ref().map(|p| p.len()).unwrap_or(0); // 2 = "()".len(), 3 = "(,)".len() - let width = try_opt!(shape.width.checked_sub(path_len + if add_comma { 3 } else { 2 })); + let nested_shape = try_opt!(shape.sub_width(path_len + if add_comma { 3 } else { 2 })); // 1 = "(".len() - let offset = shape.indent + path_len + 1; + let nested_shape = nested_shape.visual_indent(path_len + 1); let mut items: Vec<_> = itemize_list(context.codemap, pat_vec.iter(), if add_comma { ",)" } else { ")" }, |item| item.span().lo, |item| item.span().hi, - |item| item.rewrite(context, Shape::legacy(width, offset)), + |item| item.rewrite(context, nested_shape), context.codemap.span_after(span, "("), span.hi - BytePos(1)) .collect(); @@ -242,10 +242,10 @@ fn rewrite_tuple_pat(pats: &[ptr::P], items[new_item_count - 1].item = Some("..".to_owned()); let da_iter = items.into_iter().take(new_item_count); - try_opt!(format_item_list(da_iter, Shape::legacy(width, offset), context.config)) + try_opt!(format_item_list(da_iter, nested_shape, context.config)) } else { try_opt!(format_item_list(items.into_iter(), - Shape::legacy(width, offset), + nested_shape, context.config)) }; diff --git a/src/rewrite.rs b/src/rewrite.rs index 3c69e2c3c451..bb75a6f4db79 100644 --- a/src/rewrite.rs +++ b/src/rewrite.rs @@ -13,7 +13,7 @@ use syntax::codemap::{CodeMap, Span}; use syntax::parse::ParseSess; -use {Indent, Shape}; +use Shape; use config::Config; pub trait Rewrite { @@ -26,20 +26,9 @@ pub struct RewriteContext<'a> { pub parse_session: &'a ParseSess, pub codemap: &'a CodeMap, pub config: &'a Config, - // Indentation due to nesting of blocks. - pub block_indent: Indent, } impl<'a> RewriteContext<'a> { - pub fn nested_context(&self) -> RewriteContext<'a> { - RewriteContext { - parse_session: self.parse_session, - codemap: self.codemap, - config: self.config, - block_indent: self.block_indent.block_indent(self.config), - } - } - pub fn snippet(&self, span: Span) -> String { self.codemap.span_to_snippet(span).unwrap() } diff --git a/src/string.rs b/src/string.rs index 014830a9155a..7fd6bb78e61c 100644 --- a/src/string.rs +++ b/src/string.rs @@ -36,7 +36,8 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option let stripped_str = re.replace_all(orig, "$1"); let graphemes = UnicodeSegmentation::graphemes(&*stripped_str, false).collect::>(); - let indent = fmt.shape.indent.to_string(fmt.config); + let shape = fmt.shape.visual_indent(0); + let indent = shape.indent.to_string(fmt.config); let punctuation = ":,;."; // `cur_start` is the position in `orig` of the start of the current line. @@ -49,7 +50,7 @@ pub fn rewrite_string<'a>(orig: &str, fmt: &StringFormat<'a>) -> Option let ender_length = fmt.line_end.len(); // If we cannot put at least a single character per line, the rewrite won't // succeed. - let max_chars = try_opt!(fmt.shape.width.checked_sub(fmt.opener.len() + ender_length + 1)) + 1; + let max_chars = try_opt!(shape.width.checked_sub(fmt.opener.len() + ender_length + 1)) + 1; // Snip a line at a time from `orig` until it is used up. Push the snippet // onto result. diff --git a/src/types.rs b/src/types.rs index cce487cb2ea8..d636f6e587a3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -66,9 +66,9 @@ pub fn rewrite_path(context: &RewriteContext, result.push_str("::"); } - let extra_offset = extra_offset(&result, shape.indent); + let extra_offset = extra_offset(&result, shape); // 3 = ">::".len() - let budget = try_opt!(shape.width.checked_sub(extra_offset + 3)); + let shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(3)); result = try_opt!(rewrite_path_segments(PathContext::Type, result, @@ -76,8 +76,7 @@ pub fn rewrite_path(context: &RewriteContext, span_lo, path.span.hi, context, - Shape::legacy(budget, - shape.indent + extra_offset))); + shape)); } if context.config.spaces_within_angle_brackets { @@ -88,15 +87,15 @@ pub fn rewrite_path(context: &RewriteContext, span_lo = qself.ty.span.hi + BytePos(1); } - let extra_offset = extra_offset(&result, shape.indent); - let budget = try_opt!(shape.width.checked_sub(extra_offset)); + let extra_offset = extra_offset(&result, shape); + let shape = try_opt!(shape.shrink_left(extra_offset)); rewrite_path_segments(path_context, result, path.segments.iter().skip(skip_count), span_lo, path.span.hi, context, - Shape::legacy(budget, shape.indent + extra_offset)) + shape) } fn rewrite_path_segments<'a, I>(path_context: PathContext, @@ -110,6 +109,7 @@ fn rewrite_path_segments<'a, I>(path_context: PathContext, where I: Iterator { let mut first = true; + let shape = shape.visual_indent(0); for segment in iter { // Indicates a global path, shouldn't be rendered. @@ -122,15 +122,14 @@ fn rewrite_path_segments<'a, I>(path_context: PathContext, buffer.push_str("::"); } - let extra_offset = extra_offset(&buffer, shape.indent); - let remaining_width = try_opt!(shape.width.checked_sub(extra_offset)); - let new_offset = shape.indent + extra_offset; + let extra_offset = extra_offset(&buffer, shape); + let new_shape = try_opt!(shape.shrink_left(extra_offset)); let segment_string = try_opt!(rewrite_segment(path_context, segment, &mut span_lo, span_hi, context, - Shape::legacy(remaining_width, new_offset))); + new_shape)); buffer.push_str(&segment_string); } @@ -190,8 +189,7 @@ fn rewrite_segment(path_context: PathContext, shape: Shape) -> Option { let ident_len = segment.identifier.to_string().len(); - let width = try_opt!(shape.width.checked_sub(ident_len)); - let offset = shape.indent + ident_len; + let shape = try_opt!(shape.shrink_left(ident_len)); let params = if let Some(ref params) = segment.parameters { match **params { @@ -216,24 +214,18 @@ fn rewrite_segment(path_context: PathContext, // 1 for < let extra_offset = 1 + separator.len(); // 1 for > - let list_width = try_opt!(width.checked_sub(extra_offset + 1)); + // TODO bad visual indent + let list_shape = try_opt!(try_opt!(shape.shrink_left(extra_offset)).sub_width(1)).visual_indent(0); let items = itemize_list(context.codemap, param_list.into_iter(), ">", |param| param.get_span().lo, |param| param.get_span().hi, - |seg| { - seg.rewrite(context, - Shape::legacy(list_width, - offset + extra_offset)) - }, + |seg| seg.rewrite(context, list_shape), list_lo, span_hi); - let list_str = try_opt!(format_item_list(items, - Shape::legacy(list_width, - offset + extra_offset), - context.config)); + let list_str = try_opt!(format_item_list(items, list_shape, context.config)); // Update position of last bracket. *span_lo = next_span_lo; @@ -254,7 +246,7 @@ fn rewrite_segment(path_context: PathContext, false, data.span, context, - Shape::legacy(width, offset))) + shape)) } _ => String::new(), } diff --git a/src/utils.rs b/src/utils.rs index c90175710251..da48831e672e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -18,16 +18,16 @@ use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, NestedMet use syntax::codemap::BytePos; use syntax::abi; -use {Indent, Shape}; +use Shape; use rewrite::{Rewrite, RewriteContext}; use SKIP_ANNOTATION; // Computes the length of a string's last line, minus offset. -pub fn extra_offset(text: &str, offset: Indent) -> usize { +pub fn extra_offset(text: &str, shape: Shape) -> usize { match text.rfind('\n') { // 1 for newline character - Some(idx) => text.len().checked_sub(idx + 1 + offset.width()).unwrap_or(0), + Some(idx) => text.len().checked_sub(idx + 1 + shape.used_width()).unwrap_or(0), None => text.len(), } } diff --git a/src/visitor.rs b/src/visitor.rs index fd0a1f1ed21e..e7fd3beda2f9 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -294,6 +294,7 @@ impl<'a> FmtVisitor<'a> { ty, mutability, Some(expr), + self.block_indent, &self.get_context()); self.push_rewrite(item.span, rewrite); } @@ -304,6 +305,7 @@ impl<'a> FmtVisitor<'a> { ty, ast::Mutability::Immutable, Some(expr), + self.block_indent, &self.get_context()); self.push_rewrite(item.span, rewrite); } @@ -353,6 +355,7 @@ impl<'a> FmtVisitor<'a> { ty, ast::Mutability::Immutable, expr_opt.as_ref(), + self.block_indent, &self.get_context()); self.push_rewrite(ti.span, rewrite); } @@ -403,6 +406,7 @@ impl<'a> FmtVisitor<'a> { ty, ast::Mutability::Immutable, Some(expr), + self.block_indent, &self.get_context()); self.push_rewrite(ii.span, rewrite); } @@ -560,7 +564,6 @@ impl<'a> FmtVisitor<'a> { parse_session: self.parse_session, codemap: self.codemap, config: self.config, - block_indent: self.block_indent, } } }