diff --git a/src/chains.rs b/src/chains.rs index 90adb67ad43..97a79794d4a 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -566,8 +566,13 @@ impl Rewrite for Chain { formatter.format_root(&self.parent, context, shape)?; if let Some(result) = formatter.pure_root() { - return wrap_str(result, context.config.max_width(), shape) - .max_width_error(shape.width, self.parent.span); + return wrap_str( + result, + context.config.max_width(), + context.config.tab_spaces(), + shape, + ) + .max_width_error(shape.width, self.parent.span); } let first = self.children.first().unwrap_or(&self.parent); @@ -582,7 +587,13 @@ impl Rewrite for Chain { formatter.format_last_child(context, shape, child_shape)?; let result = formatter.join_rewrites(context, child_shape)?; - wrap_str(result, context.config.max_width(), shape).max_width_error(shape.width, full_span) + wrap_str( + result, + context.config.max_width(), + context.config.tab_spaces(), + shape, + ) + .max_width_error(shape.width, full_span) } } @@ -718,7 +729,7 @@ impl<'a> ChainFormatterShared<'a> { ) -> Result<(), RewriteError> { let last = self.children.last().unknown_error()?; let extendable = may_extend && last_line_extendable(&self.rewrites[0]); - let prev_last_line_width = last_line_width(&self.rewrites[0]); + let prev_last_line_width = last_line_width(&self.rewrites[0], context.config.tab_spaces()); // Total of all items excluding the last. let almost_total = if extendable { @@ -958,7 +969,8 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { let mut root_rewrite = parent.rewrite_result(context, parent_shape)?; let multiline = root_rewrite.contains('\n'); self.offset = if multiline { - last_line_width(&root_rewrite).saturating_sub(shape.used_width()) + last_line_width(&root_rewrite, context.config.tab_spaces()) + .saturating_sub(shape.used_width()) } else { trimmed_last_line_width(&root_rewrite) }; @@ -973,7 +985,12 @@ impl<'a> ChainFormatter for ChainFormatterVisual<'a> { .visual_indent(self.offset) .sub_width(self.offset, item.span)?; let rewrite = item.rewrite_result(context, child_shape)?; - if filtered_str_fits(&rewrite, context.config.max_width(), shape) { + if filtered_str_fits( + &rewrite, + context.config.max_width(), + context.config.tab_spaces(), + shape, + ) { root_rewrite.push_str(&rewrite); } else { // We couldn't fit in at the visual indent, try the last diff --git a/src/closures.rs b/src/closures.rs index 19cd0d9792c..6d5b43082ef 100644 --- a/src/closures.rs +++ b/src/closures.rs @@ -351,7 +351,7 @@ fn rewrite_closure_fn_decl( prefix.push_str(&ret_str); } // 1 = space between `|...|` and body. - let extra_offset = last_line_width(&prefix) + 1; + let extra_offset = last_line_width(&prefix, context.config.tab_spaces()) + 1; Ok((prefix, extra_offset)) } diff --git a/src/comment.rs b/src/comment.rs index 241934a7d3d..c0709d6a324 100644 --- a/src/comment.rs +++ b/src/comment.rs @@ -174,8 +174,9 @@ pub(crate) fn combine_strs_with_missing_comments( } else { " " }; - let mut one_line_width = - last_line_width(prev_str) + first_line_width(next_str) + first_sep.len(); + let mut one_line_width = last_line_width(prev_str, context.config.tab_spaces()) + + first_line_width(next_str) + + first_sep.len(); let config = context.config; let indent = shape.indent; @@ -207,7 +208,9 @@ pub(crate) fn combine_strs_with_missing_comments( let first_sep = if prev_str.is_empty() || missing_comment.is_empty() { Cow::from("") } else { - let one_line_width = last_line_width(prev_str) + first_line_width(&missing_comment) + 1; + let one_line_width = last_line_width(prev_str, context.config.tab_spaces()) + + first_line_width(&missing_comment) + + 1; if prefer_same_line && one_line_width <= shape.width { Cow::from(" ") } else { @@ -871,7 +874,8 @@ impl<'a> CommentRewrite<'a> { self.fmt.shape = if self.is_prev_line_multi_line { // 1 = " " - let offset = 1 + last_line_width(&self.result) - self.line_start.len(); + let offset = 1 + last_line_width(&self.result, self.fmt.config.tab_spaces()) + - self.line_start.len(); Shape { width: self.max_width.saturating_sub(offset), indent: self.fmt_indent, diff --git a/src/expr.rs b/src/expr.rs index b79137c4442..505cccbe1ef 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -279,6 +279,7 @@ pub(crate) fn format_expr( wrap_str( context.snippet(expr.span).to_owned(), context.config.max_width(), + context.config.tab_spaces(), shape, ) .max_width_error(shape.width, expr.span) @@ -597,7 +598,10 @@ fn rewrite_single_line_block( shape: Shape, ) -> RewriteResult { if let Some(block_expr) = stmt::Stmt::from_simple_block(context, block, attrs) { - let expr_shape = shape.offset_left(last_line_width(prefix), block_expr.span())?; + let expr_shape = shape.offset_left( + last_line_width(prefix, context.config.tab_spaces()), + block_expr.span(), + )?; let expr_str = block_expr.rewrite_result(context, expr_shape)?; let label_str = rewrite_label(context, label); let result = format!("{prefix}{label_str}{{ {expr_str} }}"); @@ -1097,7 +1101,7 @@ impl<'a> ControlFlow<'a> { }; let used_width = if pat_expr_string.contains('\n') { - last_line_width(&pat_expr_string) + last_line_width(&pat_expr_string, context.config.tab_spaces()) } else { // 2 = spaces after keyword and condition. label_string.len() + self.keyword.len() + pat_expr_string.len() + 2 @@ -1342,6 +1346,7 @@ pub(crate) fn rewrite_literal( _ => wrap_str( context.snippet(span).to_owned(), context.config.max_width(), + context.config.tab_spaces(), shape, ) .max_width_error(shape.width, span), @@ -1360,8 +1365,13 @@ fn rewrite_string_lit(context: &RewriteContext<'_>, span: Span, shape: Shape) -> { return Ok(string_lit.to_owned()); } else { - return wrap_str(string_lit.to_owned(), context.config.max_width(), shape) - .max_width_error(shape.width, span); + return wrap_str( + string_lit.to_owned(), + context.config.max_width(), + context.config.tab_spaces(), + shape, + ) + .max_width_error(shape.width, span); } } @@ -1402,6 +1412,7 @@ fn rewrite_int_lit( token_lit.suffix.as_ref().map_or("", |s| s.as_str()) ), context.config.max_width(), + context.config.tab_spaces(), shape, ) .max_width_error(shape.width, span); @@ -1411,6 +1422,7 @@ fn rewrite_int_lit( wrap_str( context.snippet(span).to_owned(), context.config.max_width(), + context.config.tab_spaces(), shape, ) .max_width_error(shape.width, span) @@ -1429,6 +1441,7 @@ fn rewrite_float_lit( return wrap_str( context.snippet(span).to_owned(), context.config.max_width(), + context.config.tab_spaces(), shape, ) .max_width_error(shape.width, span); @@ -1477,6 +1490,7 @@ fn rewrite_float_lit( suffix.unwrap_or(""), ), context.config.max_width(), + context.config.tab_spaces(), shape, ) .max_width_error(shape.width, span) @@ -1698,7 +1712,7 @@ fn rewrite_index( ) -> RewriteResult { let expr_str = expr.rewrite_result(context, shape)?; - let offset = last_line_width(&expr_str) + 1; + let offset = last_line_width(&expr_str, context.config.tab_spaces()) + 1; let rhs_overhead = shape.rhs_overhead(context.config); let index_shape = if expr_str.contains('\n') { Shape::legacy(context.config.max_width(), shape.indent) @@ -2220,11 +2234,12 @@ pub(crate) fn rewrite_assign_rhs_expr( rhs_kind: &RhsAssignKind<'_>, rhs_tactics: RhsTactics, ) -> RewriteResult { - let last_line_width = last_line_width(lhs).saturating_sub(if lhs.contains('\n') { - shape.indent.width() - } else { - 0 - }); + let last_line_width = + last_line_width(lhs, context.config.tab_spaces()).saturating_sub(if lhs.contains('\n') { + shape.indent.width() + } else { + 0 + }); // 1 = space between operator and rhs. let orig_shape = shape.offset_left_opt(last_line_width + 1).unwrap_or(Shape { width: 0, @@ -2321,7 +2336,12 @@ fn choose_rhs( match (orig_rhs, new_rhs) { (Ok(ref orig_rhs), Ok(ref new_rhs)) - if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) => + if !filtered_str_fits( + &new_rhs, + context.config.max_width(), + context.config.tab_spaces(), + new_shape, + ) => { Ok(format!("{before_space_str}{orig_rhs}")) } diff --git a/src/items.rs b/src/items.rs index a2c0e8e0f50..60300222d68 100644 --- a/src/items.rs +++ b/src/items.rs @@ -107,7 +107,10 @@ impl Rewrite for ast::Local { } else { shape } - .offset_left(last_line_width(&result) + separator.len(), self.span())? + .offset_left( + last_line_width(&result, context.config.tab_spaces()) + separator.len(), + self.span(), + )? // 2 = ` =` .sub_width(2, self.span())?; @@ -437,7 +440,7 @@ impl<'a> FmtVisitor<'a> { // 2 = ` {` if self.config.brace_style() == BraceStyle::AlwaysNextLine || force_newline_brace - || last_line_width(&result) + 2 > self.shape().width + || last_line_width(&result, context.config.tab_spaces()) + 2 > self.shape().width { fn_brace_style = FnBraceStyle::NextLine } @@ -556,7 +559,7 @@ impl<'a> FmtVisitor<'a> { self.block_indent, // make a span that starts right after `enum Foo` mk_sp(ident.span.hi(), body_start), - last_line_width(&enum_header), + last_line_width(&enum_header, self.get_context().config.tab_spaces()), ) .unwrap(); self.push_str(&generics_str); @@ -810,10 +813,10 @@ pub(crate) fn format_impl( let where_budget = if result.contains('\n') { context.config.max_width() } else { - context.budget(last_line_width(&result)) + context.budget(last_line_width(&result, context.config.tab_spaces())) }; - let mut option = WhereClauseOption::snuggled(&ref_and_type); + let mut option = WhereClauseOption::snuggled(&ref_and_type, context.config.tab_spaces()); let snippet = context.snippet(item.span); let open_pos = snippet.find_uncommented("{").unknown_error()? + 1; if !contains_comment(&snippet[open_pos..]) @@ -848,7 +851,7 @@ pub(crate) fn format_impl( mk_sp(self_ty.span.hi(), hi), Shape::indented(offset, context.config), context, - last_line_width(&result), + last_line_width(&result, context.config.tab_spaces()), ) { Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); @@ -966,11 +969,17 @@ fn format_impl_ref_and_type( } let shape = if context.config.style_edition() >= StyleEdition::Edition2024 { - Shape::indented(offset + last_line_width(&result), context.config) + Shape::indented( + offset + last_line_width(&result, context.config.tab_spaces()), + context.config, + ) } else { generics_shape_from_config( context.config, - Shape::indented(offset + last_line_width(&result), context.config), + Shape::indented( + offset + last_line_width(&result, context.config.tab_spaces()), + context.config, + ), 0, item.span, )? @@ -985,7 +994,7 @@ fn format_impl_ref_and_type( ast::ImplPolarity::Negative(_) => "!", ast::ImplPolarity::Positive => "", }; - let result_len = last_line_width(&result); + let result_len = last_line_width(&result, context.config.tab_spaces()); result.push_str(&rewrite_trait_ref( context, &of_trait.trait_ref, @@ -1009,7 +1018,9 @@ fn format_impl_ref_and_type( } else { 0 }; - let used_space = last_line_width(&result) + trait_ref_overhead + curly_brace_overhead; + let used_space = last_line_width(&result, context.config.tab_spaces()) + + trait_ref_overhead + + curly_brace_overhead; // 1 = space before the type. let budget = context.budget(used_space + 1); if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) { @@ -1032,7 +1043,7 @@ fn format_impl_ref_and_type( if of_trait.is_some() { result.push_str("for "); } - let budget = context.budget(last_line_width(&result)); + let budget = context.budget(last_line_width(&result, context.config.tab_spaces())); let type_offset = match context.config.indent_style() { IndentStyle::Visual => new_line_offset + trait_ref_overhead, IndentStyle::Block => new_line_offset, @@ -1205,13 +1216,13 @@ pub(crate) fn format_trait( if !generics.where_clause.predicates.is_empty() { let where_on_new_line = context.config.indent_style() != IndentStyle::Block; - let where_budget = context.budget(last_line_width(&result)); + let where_budget = context.budget(last_line_width(&result, context.config.tab_spaces())); let pos_before_where = if bounds.is_empty() { generics.where_clause.span.lo() } else { bounds[bounds.len() - 1].span().hi() }; - let option = WhereClauseOption::snuggled(&generics_str); + let option = WhereClauseOption::snuggled(&generics_str, context.config.tab_spaces()); let where_clause_str = rewrite_where_clause( context, &generics.where_clause, @@ -1227,7 +1238,9 @@ pub(crate) fn format_trait( // If the where-clause cannot fit on the same line, // put the where-clause on a new line if !where_clause_str.contains('\n') - && last_line_width(&result) + where_clause_str.len() + offset.width() + && last_line_width(&result, context.config.tab_spaces()) + + where_clause_str.len() + + offset.width() > context.config.comment_width() { let width = offset.block_indent + context.config.tab_spaces() - 1; @@ -1250,7 +1263,7 @@ pub(crate) fn format_trait( mk_sp(comment_lo, comment_hi), Shape::indented(offset, context.config), context, - last_line_width(&result), + last_line_width(&result, context.config.tab_spaces()), ) { Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); @@ -1267,7 +1280,8 @@ pub(crate) fn format_trait( match context.config.brace_style() { _ if last_line_contains_single_line_comment(&result) - || last_line_width(&result) + 2 > context.budget(offset.width()) => + || last_line_width(&result, context.config.tab_spaces()) + 2 + > context.budget(offset.width()) => { result.push_str(&offset.to_string_with_newline(context.config)); } @@ -1409,7 +1423,7 @@ fn format_unit_struct( offset, // make a span that starts right after `struct Foo` mk_sp(p.ident.span.hi(), hi), - last_line_width(&header_str), + last_line_width(&header_str, context.config.tab_spaces()), )? } else { String::new() @@ -1452,7 +1466,7 @@ pub(crate) fn format_struct_struct( offset, // make a span that starts right after `struct Foo` mk_sp(header_hi, body_lo), - last_line_width(&result), + last_line_width(&result, context.config.tab_spaces()), )?, None => { // 3 = ` {}`, 2 = ` {`. @@ -1536,7 +1550,7 @@ fn format_empty_struct_or_tuple( closer: &str, ) { // 3 = " {}" or "();" - let used_width = last_line_used_width(result, offset.width()) + 3; + let used_width = last_line_used_width(result, offset.width(), context.config.tab_spaces()) + 3; if used_width > context.config.max_width() { result.push_str(&offset.to_string_with_newline(context.config)) } @@ -1599,12 +1613,13 @@ fn format_tuple_struct( let where_clause_str = match struct_parts.generics { Some(generics) => { - let budget = context.budget(last_line_width(&header_str)); + let budget = context.budget(last_line_width(&header_str, context.config.tab_spaces())); let shape = Shape::legacy(budget, offset); let generics_str = rewrite_generics(context, "", generics, shape).ok()?; result.push_str(&generics_str); - let where_budget = context.budget(last_line_width(&result)); + let where_budget = + context.budget(last_line_width(&result, context.config.tab_spaces())); let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); rewrite_where_clause( context, @@ -1776,8 +1791,8 @@ fn rewrite_ty( } } - let where_budget = context.budget(last_line_width(&result)); - let mut option = WhereClauseOption::snuggled(&result); + let where_budget = context.budget(last_line_width(&result, context.config.tab_spaces())); + let mut option = WhereClauseOption::snuggled(&result, context.config.tab_spaces()); if rhs.is_none() { option.suppress_comma(); } @@ -2320,7 +2335,7 @@ impl Rewrite for ast::Param { result.push_str(&before_comment); result.push_str(colon_spaces(context.config)); result.push_str(&after_comment); - let overhead = last_line_width(&result); + let overhead = last_line_width(&result, context.config.tab_spaces()); let max_width = shape .width .checked_sub(overhead) @@ -2348,7 +2363,7 @@ impl Rewrite for ast::Param { result.push_str(&before_comment); result.push_str(colon_spaces(context.config)); result.push_str(&after_comment); - let overhead = last_line_width(&result); + let overhead = last_line_width(&result, context.config.tab_spaces()); let max_width = shape .width .checked_sub(overhead) @@ -2478,7 +2493,7 @@ fn rewrite_fn_base( // 2 = `()` 2 }; - let used_width = last_line_used_width(&result, indent.width()); + let used_width = last_line_used_width(&result, indent.width(), context.config.tab_spaces()); let one_line_budget = context.budget(used_width + overhead); let shape = Shape { width: one_line_budget, @@ -2573,7 +2588,8 @@ fn rewrite_fn_base( result.push(')'); } else { result.push_str(¶m_str); - let used_width = last_line_used_width(&result, indent.width()) + first_line_width(&ret_str); + let used_width = last_line_used_width(&result, indent.width(), context.config.tab_spaces()) + + first_line_width(&ret_str); // Put the closing brace on the next line if it overflows the max width. // 1 = `)` let closing_paren_overflow_max_width = @@ -2671,11 +2687,12 @@ fn rewrite_fn_base( let ret_shape = Shape::indented(indent, context.config); ret_shape - .offset_left_opt(last_line_width(&result)) + .offset_left_opt(last_line_width(&result, context.config.tab_spaces())) .unwrap_or(ret_shape) }; - let exceeds_max_width = last_line_width(&result) + ret_str_len > context.config.max_width(); + let exceeds_max_width = last_line_width(&result, context.config.tab_spaces()) + ret_str_len + > context.config.max_width(); if multi_line_ret_str || ret_should_indent @@ -2752,7 +2769,7 @@ fn rewrite_fn_base( mk_sp(ret_span.lo(), span.hi()), shape, context, - last_line_width(&result), + last_line_width(&result, context.config.tab_spaces()), ) { Ok(ref missing_comment) if !missing_comment.is_empty() => { result.push_str(missing_comment); @@ -2801,10 +2818,10 @@ impl WhereClauseOption { } } - fn snuggled(current: &str) -> WhereClauseOption { + fn snuggled(current: &str, tab_spaces: usize) -> WhereClauseOption { WhereClauseOption { suppress_comma: false, - snuggle: if last_line_width(current) == 1 { + snuggle: if last_line_width(current, tab_spaces) == 1 { WhereClauseSpace::Space } else { WhereClauseSpace::Newline @@ -3360,8 +3377,12 @@ fn format_generics( span.lo() }; let (same_line_brace, missed_comments) = if !generics.where_clause.predicates.is_empty() { - let budget = context.budget(last_line_used_width(&result, offset.width())); - let mut option = WhereClauseOption::snuggled(&result); + let budget = context.budget(last_line_used_width( + &result, + offset.width(), + context.config.tab_spaces(), + )); + let mut option = WhereClauseOption::snuggled(&result, context.config.tab_spaces()); if brace_pos == BracePos::None { option.suppress_comma = true; } @@ -3417,7 +3438,7 @@ fn format_generics( if brace_pos == BracePos::None { return Some(result); } - let total_used_width = last_line_used_width(&result, used_width); + let total_used_width = last_line_used_width(&result, used_width, context.config.tab_spaces()); let remaining_budget = context.budget(total_used_width); // If the same line brace if forced, it indicates that we are rewriting an item with empty body, // and hence we take the closer into account as well for one line budget. diff --git a/src/lists.rs b/src/lists.rs index 9d811e5d9b5..f567a191e1f 100644 --- a/src/lists.rs +++ b/src/lists.rs @@ -425,7 +425,8 @@ where if tactic != DefinitiveListTactic::Horizontal && item.post_comment.is_some() { let comment = item.post_comment.as_ref().unwrap(); - let overhead = last_line_width(&result) + first_line_width(comment.trim()); + let overhead = last_line_width(&result, formatting.config.tab_spaces()) + + first_line_width(comment.trim()); let rewrite_post_comment = |item_max_width: &mut Option| { if item_max_width.is_none() && !last && !inner_item.contains('\n') { @@ -471,7 +472,7 @@ where let mut comment_alignment = post_comment_alignment(item_max_width, unicode_str_width(inner_item)); if first_line_width(&formatted_comment) - + last_line_width(&result) + + last_line_width(&result, formatting.config.tab_spaces()) + comment_alignment + 1 > formatting.config.max_width() diff --git a/src/macros.rs b/src/macros.rs index 2d56021069c..95cdebc5cb7 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1360,7 +1360,12 @@ impl MacroBranch { } }; - if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) { + if !filtered_str_fits( + &new_body_snippet.snippet, + config.max_width(), + context.config.tab_spaces(), + shape, + ) { return Err(RewriteError::ExceedsMaxWidth { configured_width: shape.width, span: self.span, diff --git a/src/missed_spans.rs b/src/missed_spans.rs index 2654d2464ee..8b124705ad2 100644 --- a/src/missed_spans.rs +++ b/src/missed_spans.rs @@ -266,7 +266,10 @@ impl<'a> FmtVisitor<'a> { self.block_indent } else { self.push_str(" "); - Indent::from_width(self.config, last_line_width(&self.buffer)) + Indent::from_width( + self.config, + last_line_width(&self.buffer, self.config.tab_spaces()), + ) }; let comment_width = ::std::cmp::min( diff --git a/src/overflow.rs b/src/overflow.rs index 4230c89b57b..9e9854278cf 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -385,7 +385,7 @@ impl<'a> Context<'a> { // 1 = "(" or ")" let one_line_shape = shape - .offset_left_opt(last_line_width(ident) + 1) + .offset_left_opt(last_line_width(ident, context.config.tab_spaces()) + 1) .and_then(|shape| shape.sub_width_opt(1)) .unwrap_or(Shape { width: 0, ..shape }); let nested_shape = shape_from_indent_style(context, shape, used_width + 2, used_width + 1); @@ -669,7 +669,10 @@ impl<'a> Context<'a> { fn wrap_items(&self, items_str: &str, shape: Shape, is_extendable: bool) -> String { let shape = Shape { - width: shape.width.saturating_sub(last_line_width(self.ident)), + width: shape.width.saturating_sub(last_line_width( + self.ident, + self.context.config.tab_spaces(), + )), ..shape }; diff --git a/src/pairs.rs b/src/pairs.rs index 48948b88b3b..b4e390f2061 100644 --- a/src/pairs.rs +++ b/src/pairs.rs @@ -86,7 +86,8 @@ fn rewrite_pairs_one_line( let prefix_len = result.len(); let last = list.list.last()?.0; - let cur_shape = base_shape.offset_left_opt(last_line_width(&result))?; + let cur_shape = + base_shape.offset_left_opt(last_line_width(&result, context.config.tab_spaces()))?; let last_rewrite = last.rewrite(context, cur_shape)?; result.push_str(&last_rewrite); @@ -102,7 +103,12 @@ fn rewrite_pairs_one_line( return None; } - wrap_str(result, context.config.max_width(), shape) + wrap_str( + result, + context.config.max_width(), + context.config.tab_spaces(), + shape, + ) } fn rewrite_pairs_multiline( @@ -132,7 +138,9 @@ fn rewrite_pairs_multiline( } else { shape.used_width() }; - if last_line_width(&result) + offset <= nested_shape.used_width() { + if last_line_width(&result, context.config.tab_spaces()) + offset + <= nested_shape.used_width() + { // We must snuggle the next line onto the previous line to avoid an orphan. if let Some(line_shape) = shape.offset_left_opt(s.len() + 2 + trimmed_last_line_width(&result)) @@ -193,7 +201,7 @@ where // Try to put both lhs and rhs on the same line. let rhs_orig_result = shape - .offset_left_opt(last_line_width(&lhs_result) + pp.infix.len()) + .offset_left_opt(last_line_width(&lhs_result, tab_spaces) + pp.infix.len()) .and_then(|s| s.sub_width_opt(pp.suffix.len())) .and_then(|rhs_shape| rhs.rewrite_result(context, rhs_shape).ok()); @@ -208,7 +216,7 @@ where .map(|first_line| first_line.ends_with('{')) .unwrap_or(false); if !rhs_result.contains('\n') || allow_same_line { - let one_line_width = last_line_width(&lhs_result) + let one_line_width = last_line_width(&lhs_result, tab_spaces) + pp.infix.len() + first_line_width(rhs_result) + pp.suffix.len(); diff --git a/src/string.rs b/src/string.rs index 59c445f904b..af90bb055c8 100644 --- a/src/string.rs +++ b/src/string.rs @@ -150,7 +150,12 @@ pub(crate) fn rewrite_string<'a>( } result.push_str(fmt.closer); - wrap_str(result, fmt.config.max_width(), fmt.shape) + wrap_str( + result, + fmt.config.max_width(), + fmt.config.tab_spaces(), + fmt.shape, + ) } /// Returns the index to the end of the URL if the split at index of the given string includes a diff --git a/src/types.rs b/src/types.rs index 94ed42c6ea5..a9f0bc5da7f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -421,7 +421,10 @@ where shape.block().indent.to_string_with_newline(context.config), ) }; - if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { + if output.is_empty() + || last_line_width(&args, context.config.tab_spaces()) + first_line_width(&output) + <= shape.width + { Ok(format!("{args}{output}")) } else { Ok(format!( @@ -499,7 +502,9 @@ impl Rewrite for ast::WherePredicate { let mut result = String::with_capacity(attrs_str.len() + pred_str.len() + 1); result.push_str(&attrs_str); let pred_start = self.span.lo(); - let line_len = last_line_width(&attrs_str) + 1 + first_line_width(&pred_str); + let line_len = last_line_width(&attrs_str, context.config.tab_spaces()) + + 1 + + first_line_width(&pred_str); if let Some(last_attr) = self.attrs.last().filter(|last_attr| { contains_comment(context.snippet(mk_sp(last_attr.span.hi(), pred_start))) }) { @@ -598,7 +603,7 @@ fn rewrite_bounded_lifetime( Ok(result) } else { let colon = type_bound_colon(context); - let overhead = last_line_width(&result) + colon.len(); + let overhead = last_line_width(&result, context.config.tab_spaces()) + colon.len(); let shape = shape.sub_width(overhead, span)?; let result = format!( "{}{}{}", @@ -915,7 +920,7 @@ impl Rewrite for ast::Ty { true, )?; } else { - let used_width = last_line_width(&result); + let used_width = last_line_width(&result, context.config.tab_spaces()); let budget = shape .width .checked_sub(used_width) diff --git a/src/utils.rs b/src/utils.rs index b676803379f..398418b8345 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -218,15 +218,17 @@ pub(crate) fn first_line_width(s: &str) -> usize { /// The width of the last line in s. #[inline] -pub(crate) fn last_line_width(s: &str) -> usize { - unicode_str_width(s.rsplitn(2, '\n').next().unwrap_or("")) +pub(crate) fn last_line_width(s: &str, tab_spaces: usize) -> usize { + let last_line = s.rsplitn(2, '\n').next().unwrap_or(""); + let (prefix_width, prefix_end) = get_prefix_space_width_and_end(last_line, tab_spaces); + prefix_width + unicode_str_width(&last_line[prefix_end..]) } /// The total used width of the last line. #[inline] -pub(crate) fn last_line_used_width(s: &str, offset: usize) -> usize { +pub(crate) fn last_line_used_width(s: &str, offset: usize, tab_spaces: usize) -> usize { if s.contains('\n') { - last_line_width(s) + last_line_width(s, tab_spaces) } else { offset + unicode_str_width(s) } @@ -400,15 +402,25 @@ macro_rules! skip_out_of_file_lines_range_visitor { // Wraps String in an Option. Returns Some when the string adheres to the // Rewrite constraints defined for the Rewrite trait and None otherwise. -pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option { - if filtered_str_fits(&s, max_width, shape) { +pub(crate) fn wrap_str( + s: String, + max_width: usize, + tab_spaces: usize, + shape: Shape, +) -> Option { + if filtered_str_fits(&s, max_width, tab_spaces, shape) { Some(s) } else { None } } -pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool { +pub(crate) fn filtered_str_fits( + snippet: &str, + max_width: usize, + tab_spaces: usize, + shape: Shape, +) -> bool { let snippet = &filter_normal_code(snippet); if !snippet.is_empty() { // First line must fits with `shape.width`. @@ -429,7 +441,7 @@ pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) - } // A special check for the last line, since the caller may // place trailing characters on this line. - if last_line_width(snippet) > shape.used_width() + shape.width { + if last_line_width(snippet, tab_spaces) > shape.used_width() + shape.width { return false; } } @@ -609,7 +621,8 @@ pub(crate) fn trim_left_preserve_layout( let prefix_space_width = if is_empty_line(&line) { None } else { - Some(get_prefix_space_width(config, &line)) + let (prefix_width, _) = get_prefix_space_width_and_end(&line, config.tab_spaces()); + Some(prefix_width) }; // just InString{Commented} in order to allow the start of a string to be indented @@ -685,16 +698,17 @@ pub(crate) fn is_empty_line(s: &str) -> bool { s.is_empty() || s.chars().all(char::is_whitespace) } -fn get_prefix_space_width(config: &Config, s: &str) -> usize { +fn get_prefix_space_width_and_end(s: &str, tab_spaces: usize) -> (usize, usize) { let mut width = 0; - for c in s.chars() { + + for (i, c) in s.char_indices() { match c { ' ' => width += 1, - '\t' => width += config.tab_spaces(), - _ => return width, + '\t' => width += tab_spaces, + _ => return (width, i), } } - width + (width, s.len()) } pub(crate) trait NodeIdExt { diff --git a/src/visitor.rs b/src/visitor.rs index 4072a1d8697..7237eb4c1fe 100644 --- a/src/visitor.rs +++ b/src/visitor.rs @@ -277,7 +277,8 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let align_to_right = if unindent_comment && contains_comment(comment_snippet) { let first_lines = comment_snippet.splitn(2, '/').next().unwrap_or(""); - last_line_width(first_lines) > last_line_width(comment_snippet) + last_line_width(first_lines, config.tab_spaces()) + > last_line_width(comment_snippet, config.tab_spaces()) } else { false }; @@ -330,7 +331,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { if comment_on_same_line { // 1 = a space before `//` - let offset_len = 1 + last_line_width(&self.buffer) + let offset_len = 1 + last_line_width(&self.buffer, config.tab_spaces()) .saturating_sub(self.block_indent.width()); match comment_shape .visual_indent(offset_len) diff --git a/tests/target/issue_6859_hard_tab_breaks.rs b/tests/target/issue_6859_hard_tab_breaks.rs new file mode 100644 index 00000000000..c83dc3637b8 --- /dev/null +++ b/tests/target/issue_6859_hard_tab_breaks.rs @@ -0,0 +1,11 @@ +// rustfmt-hard_tabs: true + +fn testing() { + let _ = some_long_name + | if some_other_long_name { + foo + } + | if some_other_name { + bar + }; +}