diff --git a/src/chains.rs b/src/chains.rs index 2e2ce293fdb..75dac5b3227 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -105,7 +105,7 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - let parent_shape = if is_block_expr(context, &parent, "\n") { match context.config.chain_indent() { IndentStyle::Visual => shape.visual_indent(0), - IndentStyle::Block => shape.block(), + IndentStyle::Block => shape, } } else { shape @@ -133,13 +133,9 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - // brace. IndentStyle::Visual => (parent_shape, false), } - } else if parent_rewrite_contains_newline { - (chain_indent(context, parent_shape), false) } else { ( - shape - .block_indent(context.config.tab_spaces()) - .with_max_width(context.config), + chain_indent(context, shape.add_offset(parent_rewrite.len())), false, ) }; @@ -166,8 +162,9 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - .into_iter() .chain(iter::repeat(other_child_shape)); let subexpr_num = subexpr_list.len(); + let last_subexpr = &subexpr_list[suffix_try_num]; let subexpr_list = &subexpr_list[suffix_try_num..subexpr_num - prefix_try_num]; - let iter = subexpr_list.iter().rev().zip(child_shape_iter); + let iter = subexpr_list.iter().skip(1).rev().zip(child_shape_iter); let mut rewrites = try_opt!( iter.map(|(e, shape)| { rewrite_chain_subexpr(e, total_span, context, shape) @@ -175,85 +172,46 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - ); // Total of all items excluding the last. - let rewrites_len = rewrites.len(); - let almost_total = rewrites[0..(rewrites_len - 1)] - .iter() - .fold(0, |a, b| a + first_line_width(b)) + parent_rewrite.len(); - let one_line_len = - rewrites.iter().fold(0, |a, r| a + first_line_width(r)) + parent_rewrite.len(); - - let one_line_budget = min(shape.width, context.config.chain_one_line_max()); - let veto_single_line = if one_line_len > one_line_budget { - if rewrites.len() > 1 { - true - } else if rewrites.len() == 1 { - context.config.chain_split_single_child() || one_line_len > shape.width - } else { - false - } - } else if context.config.take_source_hints() && rewrites.len() > 1 { - // Look at the source code. Unless all chain elements start on the same - // line, we won't consider putting them on a single line either. - let last_span = context.snippet(mk_sp(subexpr_list[1].span.hi, total_span.hi)); - let first_span = context.snippet(subexpr_list[1].span); - let last_iter = last_span.chars().take_while(|c| c.is_whitespace()); - - first_span.chars().chain(last_iter).any(|c| c == '\n') + let extend_last_subexr = last_line_extendable(&parent_rewrite) && rewrites.is_empty(); + let almost_total = if extend_last_subexr { + last_line_width(&parent_rewrite) } else { - false + rewrites.iter().fold(0, |a, b| a + b.len()) + parent_rewrite.len() }; - - 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); - fits_single_line = init.iter().all(|s| !s.contains('\n')); - - if fits_single_line { - fits_single_line = match expr.node { - ref e @ ast::ExprKind::MethodCall(..) => { - if rewrite_method_call_with_overflow( - e, - &mut last[0], - almost_total, - total_span, - context, - shape, - ) { - // If the first line of the last method does not fit into a single line - // after the others, allow new lines. - almost_total + first_line_width(&last[0]) < context.config.max_width() - } else { - false + let one_line_budget = if rewrites.is_empty() && !context.config.chain_split_single_child() { + shape.width + } else { + min(shape.width, context.config.chain_one_line_max()) + }; + let all_in_one_line = !parent_rewrite_contains_newline && + rewrites.iter().all(|s| !s.contains('\n')) && + almost_total < one_line_budget; + let rewrite_last = || rewrite_chain_subexpr(last_subexpr, total_span, context, nested_shape); + let (last_subexpr_str, fits_single_line) = try_opt!(if all_in_one_line || extend_last_subexr { + parent_shape.offset_left(almost_total).map(|shape| { + if let Some(rw) = rewrite_chain_subexpr(last_subexpr, total_span, context, shape) { + let line_count = rw.lines().count(); + let fits_single_line = almost_total + first_line_width(&rw) <= one_line_budget; + if (line_count >= 5 && fits_single_line) || extend_last_subexr { + (Some(rw), true) + } else { + match rewrite_last() { + Some(ref new_rw) if !fits_single_line => (Some(new_rw.clone()), false), + Some(ref new_rw) if new_rw.lines().count() >= line_count => { + (Some(rw), fits_single_line) + } + new_rw @ Some(..) => (new_rw, false), + _ => (Some(rw), fits_single_line), } } - _ => !last[0].contains('\n'), + } else { + (rewrite_last(), false) } - } - } - - // Try overflowing the last element if we are using block indent and it goes multi line - // or it fits in a single line but goes over the max width. - if !fits_single_line && context.use_block_indent() { - let last_expr_index = rewrites.len() - 1; - let (init, last) = rewrites.split_at_mut(last_expr_index); - let almost_single_line = init.iter().all(|s| !s.contains('\n')); - if almost_single_line && last[0].contains('\n') { - let overflow_shape = Shape { - width: one_line_budget, - ..parent_shape - }; - fits_single_line = rewrite_last_child_with_overflow( - context, - &subexpr_list[0], - overflow_shape, - total_span, - almost_total, - one_line_budget, - &mut last[0], - ); - } - } + }) + } else { + Some((rewrite_last(), false)) + }); + rewrites.push(try_opt!(last_subexpr_str)); let connector = if fits_single_line && !parent_rewrite_contains_newline { // Yay, we can put everything on one line. @@ -266,14 +224,14 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - format!("\n{}", nested_shape.indent.to_string(context.config)) }; - let first_connector = choose_first_connector( - context, - &parent_rewrite, - &rewrites[0], - &connector, - &subexpr_list, - extend, - ); + let first_connector = if is_small_parent || fits_single_line || + last_line_extendable(&parent_rewrite) || + context.config.chain_indent() == IndentStyle::Visual + { + "" + } else { + connector.as_str() + }; let result = if is_small_parent && rewrites.len() > 1 { let second_connector = choose_first_connector( @@ -301,11 +259,7 @@ pub fn rewrite_chain(expr: &ast::Expr, context: &RewriteContext, shape: Shape) - ) }; let result = format!("{}{}", result, repeat_try(suffix_try_num)); - if context.config.chain_indent() == IndentStyle::Block { - Some(result) - } else { - wrap_str(result, context.config.max_width(), shape) - } + wrap_str(result, context.config.max_width(), shape) } fn is_extendable_parent(context: &RewriteContext, parent_str: &str) -> bool { @@ -323,26 +277,6 @@ fn chain_only_try(exprs: &[ast::Expr]) -> bool { // Try to rewrite and replace the last non-try child. Return `true` if // replacing succeeds. -fn rewrite_last_child_with_overflow( - context: &RewriteContext, - expr: &ast::Expr, - shape: Shape, - span: Span, - almost_total: usize, - one_line_budget: usize, - last_child: &mut String, -) -> bool { - if let Some(shape) = shape.shrink_left(almost_total) { - if let Some(ref mut rw) = rewrite_chain_subexpr(expr, span, context, shape) { - if almost_total + first_line_width(rw) <= one_line_budget && rw.lines().count() > 3 { - ::std::mem::swap(last_child, rw); - return true; - } - } - } - false -} - fn repeat_try(try_count: usize) -> String { iter::repeat("?").take(try_count).collect::() } @@ -420,46 +354,6 @@ fn chain_indent(context: &RewriteContext, shape: Shape) -> Shape { } } -fn rewrite_method_call_with_overflow( - expr_kind: &ast::ExprKind, - last: &mut String, - almost_total: usize, - total_span: Span, - context: &RewriteContext, - shape: Shape, -) -> bool { - if let &ast::ExprKind::MethodCall(ref segment, ref expressions) = expr_kind { - let shape = match shape.shrink_left(almost_total) { - Some(b) => b, - None => return false, - }; - let types = match segment.parameters { - Some(ref params) => match **params { - ast::PathParameters::AngleBracketed(ref data) => &data.types[..], - _ => &[], - }, - _ => &[], - }; - let mut last_rewrite = rewrite_method_call( - segment.identifier, - types, - expressions, - total_span, - context, - shape, - ); - - if let Some(ref mut s) = last_rewrite { - ::std::mem::swap(s, last); - true - } else { - false - } - } else { - unreachable!(); - } -} - // Returns the expression's subexpression, if it exists. When the subexpr // is a try! macro, we'll convert it to shorthand when the option is set. fn pop_expr_chain(expr: &ast::Expr, context: &RewriteContext) -> Option {