From 16615005421f2e948f2c9bc26721f3e704aa8f3d Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 21 Mar 2024 21:22:33 +0100 Subject: [PATCH 1/5] Fix indentation of CST struct's key_and_colon --- compiler/frontend/src/cst/kind.rs | 6 ++-- .../frontend/src/string_to_rcst/expression.rs | 10 +++--- .../frontend/src/string_to_rcst/struct_.rs | 34 +++++++++---------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/compiler/frontend/src/cst/kind.rs b/compiler/frontend/src/cst/kind.rs index 7087edebe..0960e649d 100644 --- a/compiler/frontend/src/cst/kind.rs +++ b/compiler/frontend/src/cst/kind.rs @@ -859,8 +859,10 @@ where builder.push_cst_kind("StructField", |builder| { builder.push_cst_kind_property_name("key_and_colon"); if let Some(box (key, colon)) = key_and_colon { - builder.push_cst_kind_property("key", key); - builder.push_cst_kind_property("colon", colon); + builder.push_indented_foldable(|builder| { + builder.push_cst_kind_property("key", key); + builder.push_cst_kind_property("colon", colon); + }); } else { builder.push_simple(" None"); } diff --git a/compiler/frontend/src/string_to_rcst/expression.rs b/compiler/frontend/src/string_to_rcst/expression.rs index 51491c843..be467ae46 100644 --- a/compiler/frontend/src/string_to_rcst/expression.rs +++ b/compiler/frontend/src/string_to_rcst/expression.rs @@ -1462,11 +1462,11 @@ mod test { fields: StructField: key_and_colon: - key: Symbol "Foo" - colon: TrailingWhitespace: - child: Colon - whitespace: - Whitespace " " + key: Symbol "Foo" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " value: Identifier "foo" comma: None closing_bracket: ClosingBracket diff --git a/compiler/frontend/src/string_to_rcst/struct_.rs b/compiler/frontend/src/string_to_rcst/struct_.rs index 6cddf6ce2..95a877064 100644 --- a/compiler/frontend/src/string_to_rcst/struct_.rs +++ b/compiler/frontend/src/string_to_rcst/struct_.rs @@ -220,8 +220,8 @@ mod test { fields: StructField: key_and_colon: - key: Identifier "foo" - colon: Colon + key: Identifier "foo" + colon: Colon value: Identifier "bar" comma: None closing_bracket: ClosingBracket @@ -237,8 +237,8 @@ mod test { comma: Comma StructField: key_and_colon: - key: Identifier "bar" - colon: Colon + key: Identifier "bar" + colon: Colon value: Identifier "baz" comma: None closing_bracket: ClosingBracket @@ -275,11 +275,11 @@ mod test { TrailingWhitespace: child: StructField: key_and_colon: - key: Identifier "foo" - colon: TrailingWhitespace: - child: Colon - whitespace: - Whitespace " " + key: Identifier "foo" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " value: Identifier "bar" comma: Comma whitespace: @@ -288,14 +288,14 @@ mod test { TrailingWhitespace: child: StructField: key_and_colon: - key: Int: - radix_prefix: None - value: 4 - string: "4" - colon: TrailingWhitespace: - child: Colon - whitespace: - Whitespace " " + key: Int: + radix_prefix: None + value: 4 + string: "4" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " value: Text: opening: OpeningText: opening_single_quotes: From 3cb01e8021d2bcff197e3a5d530d04498af1b45b Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Thu, 21 Mar 2024 22:11:16 +0100 Subject: [PATCH 2/5] Fix parsing of multiline struct field values without preceding line break --- compiler/formatter/src/format.rs | 13 +- .../frontend/src/string_to_rcst/struct_.rs | 130 +++++++++++++++++- 2 files changed, 141 insertions(+), 2 deletions(-) diff --git a/compiler/formatter/src/format.rs b/compiler/formatter/src/format.rs index 4ba025454..93bf54c07 100644 --- a/compiler/formatter/src/format.rs +++ b/compiler/formatter/src/format.rs @@ -19,7 +19,7 @@ use extension_trait::extension_trait; use itertools::Itertools; use traversal::dft_post_rev; -#[derive(Clone, Default)] +#[derive(Clone, Debug, Default)] pub struct FormattingInfo { pub indentation: Indentation, @@ -1779,6 +1779,17 @@ mod test { "[foo: bar # abc\n , baz]", "[\n foo: bar, # abc\n baz,\n]\n", ); + + // https://github.com/candy-lang/candy/issues/828 + // More [ + // State: [ + // YieldedAfterLastMatch: True, + // ] + // ] + test( + "More [\n State: [\n YieldedAfterLastMatch: True,\n ]\n]", + "More [State: [YieldedAfterLastMatch: True]]\n", + ); } #[test] fn test_struct_access() { diff --git a/compiler/frontend/src/string_to_rcst/struct_.rs b/compiler/frontend/src/string_to_rcst/struct_.rs index 95a877064..829d8c6d6 100644 --- a/compiler/frontend/src/string_to_rcst/struct_.rs +++ b/compiler/frontend/src/string_to_rcst/struct_.rs @@ -71,6 +71,7 @@ pub fn struct_(input: &str, indentation: usize, allow_function: bool) -> Option< // Whitespace between colon and value. let (input, whitespace) = whitespaces_and_newlines(input, fields_indentation + 1, true); + let whitespace_is_multiline = whitespace.is_multiline(); if whitespace.is_multiline() { fields_indentation = indentation + 1; } @@ -79,7 +80,11 @@ pub fn struct_(input: &str, indentation: usize, allow_function: bool) -> Option< // Value. let (input, value, has_value) = match expression( input, - fields_indentation + 1, + if whitespace_is_multiline { + fields_indentation + 1 + } else { + fields_indentation + }, ExpressionParsingOptions { allow_assignment: false, allow_call: true, @@ -310,5 +315,128 @@ mod test { Newline "\n" closing_bracket: ClosingBracket "###); + // https://github.com/candy-lang/candy/issues/828 + assert_rich_ir_snapshot!( + struct_( + "[\n State: [YieldedAfterLastMatch: True]\n]", + 0, + true + ), + @r###" + Remaining input: "" + Parsed: Struct: + opening_bracket: TrailingWhitespace: + child: OpeningBracket + whitespace: + Newline "\n" + Whitespace " " + fields: + TrailingWhitespace: + child: StructField: + key_and_colon: + key: Symbol "State" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " + value: Struct: + opening_bracket: OpeningBracket + fields: + StructField: + key_and_colon: + key: Symbol "YieldedAfterLastMatch" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " + value: Symbol "True" + comma: None + closing_bracket: ClosingBracket + comma: None + whitespace: + Newline "\n" + closing_bracket: ClosingBracket + "### + ); + assert_rich_ir_snapshot!( + struct_( + "[\n YieldedAfterLastMatch: True,\n]", + 0, + true + ), + @r###" + Remaining input: "" + Parsed: Struct: + opening_bracket: TrailingWhitespace: + child: OpeningBracket + whitespace: + Newline "\n" + Whitespace " " + fields: + TrailingWhitespace: + child: StructField: + key_and_colon: + key: Symbol "YieldedAfterLastMatch" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " + value: Symbol "True" + comma: Comma + whitespace: + Newline "\n" + closing_bracket: ClosingBracket + "### + ); + assert_rich_ir_snapshot!( + struct_( + "[\n State: [\n YieldedAfterLastMatch: True,\n ]\n]", + 0, + true + ), + @r###" + Remaining input: "" + Parsed: Struct: + opening_bracket: TrailingWhitespace: + child: OpeningBracket + whitespace: + Newline "\n" + Whitespace " " + fields: + TrailingWhitespace: + child: StructField: + key_and_colon: + key: Symbol "State" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " + value: Struct: + opening_bracket: TrailingWhitespace: + child: OpeningBracket + whitespace: + Newline "\n" + Whitespace " " + fields: + TrailingWhitespace: + child: StructField: + key_and_colon: + key: Symbol "YieldedAfterLastMatch" + colon: TrailingWhitespace: + child: Colon + whitespace: + Whitespace " " + value: Symbol "True" + comma: Comma + whitespace: + Newline "\n" + Whitespace " " + closing_bracket: ClosingBracket + comma: None + whitespace: + Newline "\n" + closing_bracket: ClosingBracket + "### + ); } } From f1b16d39fe29bd32a9a056803d10f49e9bbcce2d Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 23 Mar 2024 17:12:43 +0100 Subject: [PATCH 3/5] Fix trailing sandwich-like argument --- compiler/formatter/src/format.rs | 347 ++++++++++++++------ compiler/formatter/src/format_collection.rs | 13 +- compiler/formatter/src/formatted_cst.rs | 39 +++ compiler/formatter/src/width.rs | 1 + 4 files changed, 287 insertions(+), 113 deletions(-) diff --git a/compiler/formatter/src/format.rs b/compiler/formatter/src/format.rs index 93bf54c07..318eb204e 100644 --- a/compiler/formatter/src/format.rs +++ b/compiler/formatter/src/format.rs @@ -60,18 +60,19 @@ impl FormattingInfo { &self, previous_width: Width, first_line_extra_width: Width, - ) -> Self { - Self { - indentation: if self.is_single_expression_in_assignment_body - && previous_width.last_line_fits(self.indentation, first_line_extra_width) - { + ) -> (Self, bool) { + let uses_sandwich_like_multiline_formatting = self.is_single_expression_in_assignment_body + && previous_width.last_line_fits(self.indentation, first_line_extra_width); + let info = Self { + indentation: if uses_sandwich_like_multiline_formatting { self.indentation.with_dedent() } else { self.indentation }, trailing_comma_condition: None, is_single_expression_in_assignment_body: false, - } + }; + (info, uses_sandwich_like_multiline_formatting) } } @@ -87,6 +88,8 @@ pub fn format_csts<'a>( let mut formatted = FormattedCst::new(Width::default(), ExistingWhitespace::empty(fallback_offset)); let mut expression_count = 0; + let mut is_sandwich_like_multiline_formatting = true; + let mut ends_with_sandwich_like_multiline_formatting = true; loop { let (new_whitespace, rest) = split_leading_whitespace(offset, csts); csts = rest; @@ -118,6 +121,9 @@ pub fn format_csts<'a>( }; formatted = format_cst(edits, previous_width + width, expression, info); + is_sandwich_like_multiline_formatting &= formatted.is_sandwich_like_multiline_formatting(); + ends_with_sandwich_like_multiline_formatting &= + formatted.ends_with_sandwich_like_multiline_formatting(); offset = formatted.whitespace.end_offset(); expression_count += 1; } @@ -127,7 +133,12 @@ pub fn format_csts<'a>( width = width.without_first_line_width(); } - FormattedCst::new(width, formatted.whitespace) + FormattedCst::new_maybe_sandwich_like_multiline_formatting( + width, + expression_count == 1 && is_sandwich_like_multiline_formatting, + expression_count == 1 && ends_with_sandwich_like_multiline_formatting, + formatted.whitespace, + ) } fn split_leading_whitespace(start_offset: Offset, csts: &[Cst]) -> (ExistingWhitespace, &[Cst]) { @@ -268,10 +279,11 @@ pub fn format_cst<'a>( parts, closing, } => { - let info = info.resolve_for_expression_with_indented_lines( - previous_width, - SinglelineWidth::PARENTHESIS.into(), - ); + let (info, uses_sandwich_like_multiline_formatting) = info + .resolve_for_expression_with_indented_lines( + previous_width, + SinglelineWidth::DOUBLE_QUOTE.into(), + ); let opening = format_cst(edits, previous_width, opening, &info); let closing = format_cst( @@ -309,15 +321,17 @@ pub fn format_cst<'a>( return if total_parts_width.is_singleline() && (quotes_width + total_parts_width).fits(info.indentation) { - FormattedCst::new( + FormattedCst::new_maybe_sandwich_like_multiline_formatting( opening.into_empty_trailing(edits) + first_parts_width + last_part.into_empty_trailing(edits) + closing_width, + uses_sandwich_like_multiline_formatting, + uses_sandwich_like_multiline_formatting, whitespace, ) } else { - FormattedCst::new( + FormattedCst::new_maybe_sandwich_like_multiline_formatting( opening.into_trailing( edits, TrailingWhitespace::Indentation(info.indentation.with_indent()), @@ -327,6 +341,8 @@ pub fn format_cst<'a>( TrailingWhitespace::Indentation(info.indentation), ) + closing_width, + uses_sandwich_like_multiline_formatting, + uses_sandwich_like_multiline_formatting, whitespace, ) }; @@ -370,7 +386,7 @@ pub fn format_cst<'a>( let left_min_width = left.min_width(info.indentation); // Right - let (right_width, whitespace) = { + let (ends_with_sandwich_like_multiline_formatting, right_width, whitespace) = { let (right, right_parentheses) = ExistingParentheses::split_from(edits, right); // Depending on the precedence of `right` and whether there's an opening parenthesis // with a comment, we might be able to remove the parentheses. However, we won't insert @@ -393,7 +409,9 @@ pub fn format_cst<'a>( (width_for_right_side + bar_width, info.clone()) }; let right = format_cst(edits, previous_width_for_right, right, &info_for_right); - if right_needs_parentheses { + let ends_with_sandwich_like_multiline_formatting = + right.ends_with_sandwich_like_multiline_formatting(); + let (width, whitespace) = if right_needs_parentheses { assert!(right_parentheses.is_some()); right_parentheses.into_some( edits, @@ -408,10 +426,20 @@ pub fn format_cst<'a>( } else { right_parentheses.into_none(edits, right) } - .split() + .split(); + ( + ends_with_sandwich_like_multiline_formatting, + width, + whitespace, + ) }; - let left_width = if let Some(right_first_line_width) = right_width.first_line_width() + let left_width = if (left_min_width + SinglelineWidth::SPACE + bar_width + right_width) + .fits(info.indentation) + { + left.into_trailing_with_space(edits) + } else if ends_with_sandwich_like_multiline_formatting + && let Some(right_first_line_width) = right_width.first_line_width() && (left_min_width + SinglelineWidth::SPACE + bar_width + right_first_line_width) .fits(info.indentation) { @@ -453,79 +481,182 @@ pub fn format_cst<'a>( // Arguments let previous_width_for_arguments = Width::multiline(None, info.indentation.width()); - let last_argument_index = arguments.len() - 1; + let (last_argument, arguments) = arguments.split_last().unwrap(); let mut arguments = arguments .iter() - .enumerate() - .map(|(index, argument)| { + .map(|argument| { + let (argument, parentheses) = ExistingParentheses::split_from(edits, argument); Argument::new( edits, previous_width_for_arguments, - argument, &info.with_indent(), - index == last_argument_index, + argument, + parentheses, ) }) .collect_vec(); - let min_width = receiver.min_width(info.indentation) + // Check whether the last argument is eligible for special sandwich-like formatting. + let (last_argument, last_argument_parentheses) = + ExistingParentheses::split_from(edits, last_argument); + let sandwich_like_last_argument = if !last_argument_parentheses + .are_required_due_to_comments() + && last_argument.is_sandwich_like() + { + Some((last_argument, last_argument_parentheses)) + } else { + arguments.push(Argument::new( + edits, + previous_width_for_arguments, + &info.with_indent(), + last_argument, + last_argument_parentheses, + )); + None + }; + + let min_width_without_sandwich_like_last_argument = receiver + .min_width(info.indentation) + arguments .iter() - .map(|it| SinglelineWidth::SPACE + it.min_singleline_width()) + .map(|it| SinglelineWidth::SPACE + it.min_singleline_width) .sum::(); - let (is_singleline, argument_info, trailing) = - if previous_width.last_line_fits(info.indentation, min_width) { - (true, info.clone(), TrailingWhitespace::Space) + + // `is_quasi_singleline` is true if the call fits into a single line or the last + // argument is a sandwich-like and only that argument continues onto following lines. + let ( + ends_with_sandwich_like_multiline_formatting, + is_quasi_singleline, + sandwich_like_last_argument, + min_width, + ) = if let Some((argument, parentheses)) = sandwich_like_last_argument { + let min_width_before_last_argument = + min_width_without_sandwich_like_last_argument + SinglelineWidth::SPACE; + + let is_singleline_before_last_argument = + previous_width.last_line_fits(info.indentation, min_width_before_last_argument); + let last_argument_info = if info.is_single_expression_in_assignment_body { + if is_singleline_before_last_argument { + info.with_dedent() + .for_single_expression_in_assignment_body() + } else { + info.clone() + } + } else if is_singleline_before_last_argument { + // FIXME: rename method + info.for_single_expression_in_assignment_body() } else { - ( - false, - info.with_indent(), - TrailingWhitespace::Indentation(info.indentation.with_indent()), - ) + info.with_indent() }; + let argument = format_cst( + edits, + previous_width + min_width_before_last_argument, + argument, + &last_argument_info, + ); + let is_sandwich_like_multiline_formatting = + argument.is_sandwich_like_multiline_formatting(); - let width = receiver.into_trailing(edits, trailing); + assert_eq!(last_argument.precedence(), Some(PrecedenceCategory::High)); + let argument = parentheses.into_none(edits, argument); + + let (argument_width, whitespace) = argument.split(); + ( + is_sandwich_like_multiline_formatting, + // Can only be true if the rest fits into a single line. + is_sandwich_like_multiline_formatting, + Some(( + argument_width, + whitespace, + is_sandwich_like_multiline_formatting, + )), + min_width_before_last_argument + argument_width, + ) + } else { + ( + false, + previous_width.last_line_fits( + info.indentation, + min_width_without_sandwich_like_last_argument, + ), + None, + min_width_without_sandwich_like_last_argument, + ) + }; + + // Handle last argument specially to support sandwich-likes. + // let (last_argument, last_argument_parentheses) = + // ExistingParentheses::split_from(edits, cst); + // let last_argument_precedence = last_argument.precedence(); + + let (argument_info, trailing) = if is_quasi_singleline { + (info.clone(), TrailingWhitespace::Space) + } else { + ( + info.with_indent(), + TrailingWhitespace::Indentation(info.indentation.with_indent()), + ) + }; - let last_argument = arguments.pop().unwrap(); + let last_argument_not_sandwich_like = if sandwich_like_last_argument.is_none() { + Some(arguments.pop().unwrap()) + } else { + None + }; + let width = receiver.into_trailing(edits, trailing); let width = arguments.into_iter().fold(width, |old_width, argument| { let argument = argument.format( edits, previous_width + old_width, &argument_info, - is_singleline, + is_quasi_singleline, ); - let width = if is_singleline { + let width = if is_quasi_singleline { argument.into_trailing_with_space(edits) } else { argument.into_trailing_with_indentation(edits, argument_info.indentation) }; old_width + width }); - let last_argument_is_sandwich_like = matches!( - &last_argument.argument, - MaybeSandwichLikeArgument::SandwichLike(_) - ); + // let last_argument_is_sandwich_like = matches!( + // &last_argument.argument, + // MaybeSandwichLikeArgument::SandwichLike(_) + // ); let info_for_last_argument = - if info.is_single_expression_in_assignment_body && is_singleline { + if info.is_single_expression_in_assignment_body && is_quasi_singleline { argument_info.with_dedent() } else { argument_info }; - let (last_argument_width, whitespace) = last_argument - .format( - edits, - previous_width + width, - &info_for_last_argument, - is_singleline, - ) - .split(); + let (last_argument_width, whitespace) = + if let Some((last_argument_width, last_argument_whitespace, _)) = + sandwich_like_last_argument + { + (last_argument_width, last_argument_whitespace) + } else { + let last_argument = last_argument_not_sandwich_like.unwrap(); + last_argument + .format( + edits, + previous_width + width, + &info_for_last_argument, + is_quasi_singleline, + ) + .split() + }; - let mut width = width + last_argument_width; - if !is_singleline && !last_argument_is_sandwich_like { - width = width.without_first_line_width(); - } + let width = width + last_argument_width; + // FIXME: Is this necessary? + // if !is_singleline && !last_argument_is_sandwich_like { + // width = width.without_first_line_width(); + // } - return FormattedCst::new(width, whitespace); + return FormattedCst::new_maybe_sandwich_like_multiline_formatting( + width, + false, + ends_with_sandwich_like_multiline_formatting, + whitespace, + ); } CstKind::List { opening_parenthesis, @@ -715,18 +846,18 @@ pub fn format_cst<'a>( return FormattedCst::new(expression_width + percent_width, whitespace); }; - let case_info = info + let (case_info, is_sandwich_like_multiline_formatting) = info .resolve_for_expression_with_indented_lines( previous_width, expression_width + SinglelineWidth::PERCENT, - ) - .with_indent(); + ); + let case_info = case_info.with_indent(); let percent_width = percent.into_trailing_with_indentation(edits, case_info.indentation); let (last_case_width, whitespace) = format_cst(edits, previous_width_for_indented, last_case, &case_info).split(); - return FormattedCst::new( + return FormattedCst::new_maybe_sandwich_like_multiline_formatting( expression_width + percent_width + cases @@ -737,6 +868,8 @@ pub fn format_cst<'a>( }) .sum::() + last_case_width, + is_sandwich_like_multiline_formatting, + is_sandwich_like_multiline_formatting, whitespace, ); } @@ -784,10 +917,11 @@ pub fn format_cst<'a>( body, closing_curly_brace, } => { - let info = info.resolve_for_expression_with_indented_lines( - previous_width, - SinglelineWidth::PARENTHESIS.into(), - ); + let (info, is_sandwich_like_multiline_formatting) = info + .resolve_for_expression_with_indented_lines( + previous_width, + SinglelineWidth::PARENTHESIS.into(), + ); let opening_curly_brace = format_cst(edits, previous_width, opening_curly_brace, &info); @@ -936,11 +1070,13 @@ pub fn format_cst<'a>( }) .unwrap_or_default(); - return FormattedCst::new( + return FormattedCst::new_maybe_sandwich_like_multiline_formatting( opening_curly_brace.into_trailing(edits, opening_curly_brace_trailing) + parameters_and_arrow_width + body.into_trailing(edits, body_trailing) + closing_curly_brace_width, + is_sandwich_like_multiline_formatting, + is_sandwich_like_multiline_formatting, whitespace, ); } @@ -968,7 +1104,7 @@ pub fn format_cst<'a>( } else { info.with_indent() }; - let (body_width, body_whitespace) = format_csts( + let formatted_body = format_csts( edits, previous_width_for_assignment_sign + assignment_sign.min_width(info.indentation) @@ -976,8 +1112,10 @@ pub fn format_cst<'a>( body, assignment_sign.whitespace.end_offset(), &body_info, - ) - .split(); + ); + let body_ends_with_sandwich_like_multiline_formatting = + formatted_body.ends_with_sandwich_like_multiline_formatting(); + let (body_width, body_whitespace) = formatted_body.split(); let body_whitespace_has_comments = body_whitespace.has_comments(); let body_whitespace_width = body_whitespace.into_trailing_with_indentation( edits, @@ -1003,6 +1141,7 @@ pub fn format_cst<'a>( TrailingWhitespace::Space } else if !contains_single_assignment && !body_whitespace_has_comments + && body_ends_with_sandwich_like_multiline_formatting && let Some(body_first_line_width) = body_width.first_line_width() && left_width.last_line_fits( info.indentation, @@ -1065,7 +1204,8 @@ fn format_receiver<'a>( struct Argument<'a> { #[allow(clippy::struct_field_names)] - argument: MaybeSandwichLikeArgument<'a>, + argument: FormattedCst<'a>, + min_singleline_width: Width, precedence: Option, parentheses: ExistingParentheses<'a>, } @@ -1073,26 +1213,20 @@ impl<'a> Argument<'a> { fn new( edits: &mut TextEdits, previous_width: Width, - cst: &'a Cst, info: &FormattingInfo, - is_last: bool, + argument: &'a Cst, + parentheses: ExistingParentheses<'a>, ) -> Self { - let (argument, parentheses) = ExistingParentheses::split_from(edits, cst); let precedence = argument.precedence(); - let argument = if parentheses.are_required_due_to_comments() { + let (argument, min_singleline_width) = if parentheses.are_required_due_to_comments() { let argument = format_cst( edits, previous_width, argument, &info.with_indent().with_indent(), ); - MaybeSandwichLikeArgument::Other { - argument, - min_singleline_width: Width::multiline(None, None), - } - } else if is_last && argument.is_sandwich_like() { - MaybeSandwichLikeArgument::SandwichLike(argument) + (argument, Width::multiline(None, None)) } else { let argument = format_cst(edits, previous_width, argument, info); let mut min_singleline_width = argument.min_width(info.indentation.with_indent()); @@ -1103,13 +1237,11 @@ impl<'a> Argument<'a> { None if parentheses.is_some() => min_singleline_width += parentheses_width, None => {} } - MaybeSandwichLikeArgument::Other { - argument, - min_singleline_width, - } + (argument, min_singleline_width) }; Argument { argument, + min_singleline_width, precedence, parentheses, } @@ -1117,65 +1249,48 @@ impl<'a> Argument<'a> { /// Width of the opening parenthesis / bracket / curly brace const SANDWICH_LIKE_MIN_SINGLELINE_WIDTH: SinglelineWidth = SinglelineWidth::PARENTHESIS; - fn min_singleline_width(&self) -> Width { - match &self.argument { - MaybeSandwichLikeArgument::SandwichLike(_) => { - Self::SANDWICH_LIKE_MIN_SINGLELINE_WIDTH.into() - } - MaybeSandwichLikeArgument::Other { - min_singleline_width, - .. - } => *min_singleline_width, - } - } fn format( self, edits: &mut TextEdits, previous_width: Width, info: &FormattingInfo, - is_singleline: bool, + is_quasi_singleline: bool, ) -> FormattedCst<'a> { - let argument = match self.argument { - MaybeSandwichLikeArgument::SandwichLike(it) => { - format_cst(edits, previous_width, it, info) - } - MaybeSandwichLikeArgument::Other { argument, .. } => argument, - }; - let are_parentheses_necessary_due_to_precedence = match self.precedence { Some(PrecedenceCategory::High) => false, Some(PrecedenceCategory::Low) | None => true, }; if self.parentheses.is_some() { // We already have parentheses … - if is_singleline && are_parentheses_necessary_due_to_precedence + if is_quasi_singleline && are_parentheses_necessary_due_to_precedence || self.parentheses.are_required_due_to_comments() { // … and we actually need them. self.parentheses - .into_some(edits, previous_width, argument, info) + .into_some(edits, previous_width, self.argument, info) } else { // … but we don't need them. - self.parentheses.into_none(edits, argument) + self.parentheses.into_none(edits, self.argument) } } else { // We don't have parentheses … - if is_singleline && are_parentheses_necessary_due_to_precedence { + if is_quasi_singleline && are_parentheses_necessary_due_to_precedence { // … but we need them. self.parentheses - .into_some(edits, previous_width, argument, info) + .into_some(edits, previous_width, self.argument, info) } else { // … and we don't need them. - self.parentheses.into_none(edits, argument) + self.parentheses.into_none(edits, self.argument) } } } } -enum MaybeSandwichLikeArgument<'a> { +enum LastArgument<'a> { SandwichLike(&'a Cst), Other { argument: FormattedCst<'a>, min_singleline_width: Width, + parentheses: ExistingParentheses<'a>, }, } @@ -1455,6 +1570,15 @@ mod test { "looooooooooooooooooooooooooooooooongReceiver | looooooooooooooooooooooooooooooooongFunction longArgument0 longArgument1 longArgument2 longArgument3", "looooooooooooooooooooooooooooooooongReceiver\n| looooooooooooooooooooooooooooooooongFunction\n longArgument0\n longArgument1\n longArgument2\n longArgument3\n", ); + // looooooooooooooooooooooooooooooooongReceiver | looooooooooooooooooooooooooooooooongFunction { + // LooooooooooongTag + // } + // looooooooooooooooooooooooooooooooongReceiver + // | looooooooooooooooooooooooooooooooongFunction { LooooooooooongTag } + test( + "looooooooooooooooooooooooooooooooongReceiver\n| looooooooooooooooooooooooooooooooongFunction { LooooooooooongTag }\n", + "looooooooooooooooooooooooooooooooongReceiver\n| looooooooooooooooooooooooooooooooongFunction { LooooooooooongTag }\n", + ); // foo // | looooooooooooooooooooooooooooooooooooooooongFunction0 looooooooooooooooooooooooooooongArgument0 // | function1 @@ -1991,7 +2115,7 @@ mod test { // foo = bar { // baz // blub - // + // } test( "foo = bar {\n baz\n blub\n}", "foo = bar {\n baz\n blub\n}\n", @@ -2003,6 +2127,13 @@ mod test { "looooooooooooooooongIdentifier = function loooooooooooooooooooooooooooooooooooooooooooooooongArgument", "looooooooooooooooongIdentifier =\n function\n loooooooooooooooooooooooooooooooooooooooooooooooongArgument\n", ); + // looooooooooooooooongIdentifier = function loooooooooooooooooooooooooooongArgument { + // LooooooooooongTag + // } + test( + "looooooooooooooooongIdentifier = function loooooooooooooooooooooooooooongArgument {\n LooooooooooongTag\n}\n", + "looooooooooooooooongIdentifier = function loooooooooooooooooooooooooooongArgument {\n LooooooooooongTag\n}\n", + ); // Function definition diff --git a/compiler/formatter/src/format_collection.rs b/compiler/formatter/src/format_collection.rs index f28d163d0..4199fdd7e 100644 --- a/compiler/formatter/src/format_collection.rs +++ b/compiler/formatter/src/format_collection.rs @@ -17,10 +17,11 @@ pub fn format_collection<'a>( is_comma_required_for_single_item: bool, info: &FormattingInfo, ) -> FormattedCst<'a> { - let info = info.resolve_for_expression_with_indented_lines( - previous_width, - SinglelineWidth::PARENTHESIS.into(), - ); + let (info, uses_sandwich_like_multiline_formatting) = info + .resolve_for_expression_with_indented_lines( + previous_width, + SinglelineWidth::PARENTHESIS.into(), + ); let opening_punctuation = format_cst(edits, previous_width, opening_punctuation, &info); let closing_punctuation = format_cst( @@ -100,7 +101,7 @@ pub fn format_collection<'a>( let last_item_index = items.len().checked_sub(1); let (closing_punctuation_width, whitespace) = closing_punctuation.split(); - FormattedCst::new( + FormattedCst::new_maybe_sandwich_like_multiline_formatting( opening_punctuation.into_trailing(edits, opening_punctuation_trailing) + items .into_iter() @@ -117,6 +118,8 @@ pub fn format_collection<'a>( }) .sum::() + closing_punctuation_width, + uses_sandwich_like_multiline_formatting, + uses_sandwich_like_multiline_formatting, whitespace, ) } diff --git a/compiler/formatter/src/formatted_cst.rs b/compiler/formatter/src/formatted_cst.rs index c406a9ca4..f45be55b1 100644 --- a/compiler/formatter/src/formatted_cst.rs +++ b/compiler/formatter/src/formatted_cst.rs @@ -22,12 +22,42 @@ pub struct FormattedCst<'a> { /// If there are trailing comments, this is [Width::Multiline]. Otherwise, it's the child's own /// width. child_width: Width, + + /// Whether this CST node was formatted as a multiline sandwich-like. + /// + /// This means the previous width had sufficient space to fit the sandwich-like's opening + /// character(s) (e.g., the opening parenthesis of a function call or the opening quote(s) of a + /// text) and the rest of the sandwich-like expression is formatted over multiple lines. + is_sandwich_like_multiline_formatting: bool, + + /// Whether this CST node is mostly singleline and ends with a CST node formatted as a multiline + /// sandwich-like. + /// + /// For example, if the single expression of an assignment is a call with a trailing multiline + /// list argument, it can start on the same line as the assignment but end on a new line. + ends_with_sandwich_like_multiline_formatting: bool, + pub whitespace: ExistingWhitespace<'a>, } impl<'a> FormattedCst<'a> { pub const fn new(child_width: Width, whitespace: ExistingWhitespace<'a>) -> Self { Self { child_width, + is_sandwich_like_multiline_formatting: false, + ends_with_sandwich_like_multiline_formatting: false, + whitespace, + } + } + pub const fn new_maybe_sandwich_like_multiline_formatting( + child_width: Width, + is_sandwich_like_multiline_formatting: bool, + ends_with_sandwich_like_multiline_formatting: bool, + whitespace: ExistingWhitespace<'a>, + ) -> Self { + Self { + child_width, + is_sandwich_like_multiline_formatting, + ends_with_sandwich_like_multiline_formatting, whitespace, } } @@ -45,6 +75,15 @@ impl<'a> FormattedCst<'a> { } } + #[must_use] + pub const fn is_sandwich_like_multiline_formatting(&self) -> bool { + self.is_sandwich_like_multiline_formatting + } + #[must_use] + pub const fn ends_with_sandwich_like_multiline_formatting(&self) -> bool { + self.ends_with_sandwich_like_multiline_formatting + } + pub fn split(self) -> (Width, ExistingWhitespace<'a>) { (self.child_width, self.whitespace) } diff --git a/compiler/formatter/src/width.rs b/compiler/formatter/src/width.rs index e1dabb909..0363be4a1 100644 --- a/compiler/formatter/src/width.rs +++ b/compiler/formatter/src/width.rs @@ -37,6 +37,7 @@ pub struct SinglelineWidth(usize); impl SinglelineWidth { pub const SPACE: Self = Self(1); pub const PERCENT: Self = Self(1); + pub const DOUBLE_QUOTE: Self = Self(1); pub const fn new_const(width: usize) -> Self { Self(width) From 213366baf086887e384fe909eb5b6fe1ba92b144 Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 23 Mar 2024 18:29:57 +0100 Subject: [PATCH 4/5] Extract logic for last argument --- compiler/formatter/src/format.rs | 317 +++++++++++++++++-------------- 1 file changed, 174 insertions(+), 143 deletions(-) diff --git a/compiler/formatter/src/format.rs b/compiler/formatter/src/format.rs index 318eb204e..613a09063 100644 --- a/compiler/formatter/src/format.rs +++ b/compiler/formatter/src/format.rs @@ -473,6 +473,7 @@ pub fn format_cst<'a>( receiver, arguments, } => { + // Receiver let receiver = format_receiver(edits, previous_width, receiver, info, ReceiverParent::Call); if arguments.is_empty() { @@ -482,7 +483,7 @@ pub fn format_cst<'a>( // Arguments let previous_width_for_arguments = Width::multiline(None, info.indentation.width()); let (last_argument, arguments) = arguments.split_last().unwrap(); - let mut arguments = arguments + let arguments = arguments .iter() .map(|argument| { let (argument, parentheses) = ExistingParentheses::split_from(edits, argument); @@ -496,166 +497,48 @@ pub fn format_cst<'a>( }) .collect_vec(); - // Check whether the last argument is eligible for special sandwich-like formatting. - let (last_argument, last_argument_parentheses) = - ExistingParentheses::split_from(edits, last_argument); - let sandwich_like_last_argument = if !last_argument_parentheses - .are_required_due_to_comments() - && last_argument.is_sandwich_like() - { - Some((last_argument, last_argument_parentheses)) - } else { - arguments.push(Argument::new( - edits, - previous_width_for_arguments, - &info.with_indent(), - last_argument, - last_argument_parentheses, - )); - None - }; + let last_argument = LastArgument::new( + edits, + previous_width_for_arguments, + &info.with_indent(), + last_argument, + ); - let min_width_without_sandwich_like_last_argument = receiver - .min_width(info.indentation) + let min_width_without_last_argument = receiver.min_width(info.indentation) + arguments .iter() .map(|it| SinglelineWidth::SPACE + it.min_singleline_width) .sum::(); - // `is_quasi_singleline` is true if the call fits into a single line or the last - // argument is a sandwich-like and only that argument continues onto following lines. - let ( - ends_with_sandwich_like_multiline_formatting, - is_quasi_singleline, - sandwich_like_last_argument, - min_width, - ) = if let Some((argument, parentheses)) = sandwich_like_last_argument { - let min_width_before_last_argument = - min_width_without_sandwich_like_last_argument + SinglelineWidth::SPACE; - - let is_singleline_before_last_argument = - previous_width.last_line_fits(info.indentation, min_width_before_last_argument); - let last_argument_info = if info.is_single_expression_in_assignment_body { - if is_singleline_before_last_argument { - info.with_dedent() - .for_single_expression_in_assignment_body() - } else { - info.clone() - } - } else if is_singleline_before_last_argument { - // FIXME: rename method - info.for_single_expression_in_assignment_body() - } else { - info.with_indent() - }; - let argument = format_cst( - edits, - previous_width + min_width_before_last_argument, - argument, - &last_argument_info, - ); - let is_sandwich_like_multiline_formatting = - argument.is_sandwich_like_multiline_formatting(); - - assert_eq!(last_argument.precedence(), Some(PrecedenceCategory::High)); - let argument = parentheses.into_none(edits, argument); - - let (argument_width, whitespace) = argument.split(); - ( - is_sandwich_like_multiline_formatting, - // Can only be true if the rest fits into a single line. - is_sandwich_like_multiline_formatting, - Some(( - argument_width, - whitespace, - is_sandwich_like_multiline_formatting, - )), - min_width_before_last_argument + argument_width, - ) - } else { - ( - false, - previous_width.last_line_fits( - info.indentation, - min_width_without_sandwich_like_last_argument, - ), - None, - min_width_without_sandwich_like_last_argument, - ) - }; - - // Handle last argument specially to support sandwich-likes. - // let (last_argument, last_argument_parentheses) = - // ExistingParentheses::split_from(edits, cst); - // let last_argument_precedence = last_argument.precedence(); + let last_argument = + last_argument.format(edits, previous_width, info, min_width_without_last_argument); - let (argument_info, trailing) = if is_quasi_singleline { - (info.clone(), TrailingWhitespace::Space) - } else { - ( - info.with_indent(), - TrailingWhitespace::Indentation(info.indentation.with_indent()), - ) - }; + let argument_info = last_argument.derived_call_info.argument_info(info); + let trailing = last_argument.derived_call_info.trailing(info.indentation); - let last_argument_not_sandwich_like = if sandwich_like_last_argument.is_none() { - Some(arguments.pop().unwrap()) - } else { - None - }; let width = receiver.into_trailing(edits, trailing); let width = arguments.into_iter().fold(width, |old_width, argument| { let argument = argument.format( edits, previous_width + old_width, &argument_info, - is_quasi_singleline, + last_argument.derived_call_info.is_quasi_singleline, ); - let width = if is_quasi_singleline { + let width = if last_argument.derived_call_info.is_quasi_singleline { argument.into_trailing_with_space(edits) } else { argument.into_trailing_with_indentation(edits, argument_info.indentation) }; old_width + width }); - // let last_argument_is_sandwich_like = matches!( - // &last_argument.argument, - // MaybeSandwichLikeArgument::SandwichLike(_) - // ); - let info_for_last_argument = - if info.is_single_expression_in_assignment_body && is_quasi_singleline { - argument_info.with_dedent() - } else { - argument_info - }; - let (last_argument_width, whitespace) = - if let Some((last_argument_width, last_argument_whitespace, _)) = - sandwich_like_last_argument - { - (last_argument_width, last_argument_whitespace) - } else { - let last_argument = last_argument_not_sandwich_like.unwrap(); - last_argument - .format( - edits, - previous_width + width, - &info_for_last_argument, - is_quasi_singleline, - ) - .split() - }; - - let width = width + last_argument_width; - // FIXME: Is this necessary? - // if !is_singleline && !last_argument_is_sandwich_like { - // width = width.without_first_line_width(); - // } return FormattedCst::new_maybe_sandwich_like_multiline_formatting( - width, + width + last_argument.width, false, - ends_with_sandwich_like_multiline_formatting, - whitespace, + last_argument + .derived_call_info + .ends_with_sandwich_like_multiline_formatting, + last_argument.whitespace, ); } CstKind::List { @@ -1247,8 +1130,6 @@ impl<'a> Argument<'a> { } } - /// Width of the opening parenthesis / bracket / curly brace - const SANDWICH_LIKE_MIN_SINGLELINE_WIDTH: SinglelineWidth = SinglelineWidth::PARENTHESIS; fn format( self, edits: &mut TextEdits, @@ -1285,13 +1166,163 @@ impl<'a> Argument<'a> { } } } +#[must_use] enum LastArgument<'a> { - SandwichLike(&'a Cst), - Other { - argument: FormattedCst<'a>, - min_singleline_width: Width, + SandwichLike { + argument: &'a Cst, parentheses: ExistingParentheses<'a>, }, + Other(Argument<'a>), +} +/// Intermediate state of the last argument of a function call. +/// +/// Represents a sandwich-like argument already formatted or a non-sandwich-like argument yet to be +/// formatted. +#[must_use] +struct LastArgumentFormattedIfSandwichLike<'a> { + derived_call_info: LastArgumentDerivedCallInfo, + width: Width, + whitespace: ExistingWhitespace<'a>, +} +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +struct LastArgumentDerivedCallInfo { + /// True if the call fits into a single line or the last argument is a sandwich-like and only + /// that argument continues onto following lines. + is_quasi_singleline: bool, + + ends_with_sandwich_like_multiline_formatting: bool, +} +impl<'a> LastArgument<'a> { + fn new( + edits: &mut TextEdits, + previous_width: Width, + info: &FormattingInfo, + argument: &'a Cst, + ) -> Self { + let (argument, parentheses) = ExistingParentheses::split_from(edits, argument); + if !parentheses.are_required_due_to_comments() && argument.is_sandwich_like() { + assert_eq!(argument.precedence(), Some(PrecedenceCategory::High)); + LastArgument::SandwichLike { + argument, + parentheses, + } + } else { + LastArgument::Other(Argument::new( + edits, + previous_width, + info, + argument, + parentheses, + )) + } + } + + fn format( + self, + edits: &mut TextEdits, + previous_width: Width, + call_info: &FormattingInfo, + min_width_without_last_argument: Width, + ) -> LastArgumentFormattedIfSandwichLike<'a> { + match self { + LastArgument::SandwichLike { + argument, + parentheses, + } => { + let min_width_before_last_argument = + min_width_without_last_argument + SinglelineWidth::SPACE; + + let is_singleline_before_last_argument = previous_width + .last_line_fits(call_info.indentation, min_width_before_last_argument); + let last_argument_info = if call_info.is_single_expression_in_assignment_body { + if is_singleline_before_last_argument { + call_info + .with_dedent() + .for_single_expression_in_assignment_body() + } else { + call_info.clone() + } + } else if is_singleline_before_last_argument { + // FIXME: rename method + call_info.for_single_expression_in_assignment_body() + } else { + call_info.with_indent() + }; + let argument = format_cst( + edits, + previous_width + min_width_before_last_argument, + argument, + &last_argument_info, + ); + let is_sandwich_like_multiline_formatting = + argument.is_sandwich_like_multiline_formatting(); + + let argument = parentheses.into_none(edits, argument); + + let (width, whitespace) = argument.split(); + LastArgumentFormattedIfSandwichLike { + derived_call_info: LastArgumentDerivedCallInfo { + // Can only be true if the rest fits into a single line. + is_quasi_singleline: is_sandwich_like_multiline_formatting, + ends_with_sandwich_like_multiline_formatting: + is_sandwich_like_multiline_formatting, + }, + width, + whitespace, + } + } + LastArgument::Other(argument) => { + let derived_call_info = LastArgumentDerivedCallInfo { + is_quasi_singleline: previous_width.last_line_fits( + call_info.indentation, + min_width_without_last_argument + + SinglelineWidth::SPACE + + argument.min_singleline_width, + ), + ends_with_sandwich_like_multiline_formatting: false, + }; + let info = if call_info.is_single_expression_in_assignment_body + && derived_call_info.is_quasi_singleline + { + derived_call_info.argument_info(call_info).with_dedent() + } else { + derived_call_info.argument_info(call_info) + }; + let (width, whitespace) = argument + .format( + edits, + previous_width, + &info, + derived_call_info.is_quasi_singleline, + ) + .split(); + + LastArgumentFormattedIfSandwichLike { + derived_call_info, + width, + whitespace, + } + } + } + } +} +impl LastArgumentDerivedCallInfo { + #[must_use] + fn argument_info(self, call_info: &FormattingInfo) -> FormattingInfo { + if self.is_quasi_singleline { + call_info.clone() + } else { + call_info.with_indent() + } + } + #[must_use] + const fn trailing(self, indentation: Indentation) -> TrailingWhitespace { + if self.is_quasi_singleline { + TrailingWhitespace::Space + } else { + TrailingWhitespace::Indentation(indentation.with_indent()) + } + } } #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] From 0d55ecfb28dad02a0194638dbcdfe150ce28463b Mon Sep 17 00:00:00 2001 From: Jonas Wanke Date: Sat, 23 Mar 2024 18:32:50 +0100 Subject: [PATCH 5/5] Rename sandwich-like-related FormattingInfo methods --- compiler/formatter/src/existing_whitespace.rs | 2 +- compiler/formatter/src/format.rs | 45 ++++++++----------- compiler/formatter/src/format_collection.rs | 7 +-- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/compiler/formatter/src/existing_whitespace.rs b/compiler/formatter/src/existing_whitespace.rs index 6f5831a8d..05d170f2b 100644 --- a/compiler/formatter/src/existing_whitespace.rs +++ b/compiler/formatter/src/existing_whitespace.rs @@ -425,7 +425,7 @@ impl<'a> ExistingWhitespace<'a> { &FormattingInfo { indentation, trailing_comma_condition: None, - is_single_expression_in_assignment_body: false, + supports_sandwich_like_formatting: false, }, ) .split(); diff --git a/compiler/formatter/src/format.rs b/compiler/formatter/src/format.rs index 613a09063..06f9a15fa 100644 --- a/compiler/formatter/src/format.rs +++ b/compiler/formatter/src/format.rs @@ -25,43 +25,43 @@ pub struct FormattingInfo { // The fields below apply only for direct descendants. pub trailing_comma_condition: Option, - pub is_single_expression_in_assignment_body: bool, + pub supports_sandwich_like_formatting: bool, } impl FormattingInfo { pub const fn with_indent(&self) -> Self { Self { indentation: self.indentation.with_indent(), trailing_comma_condition: None, - is_single_expression_in_assignment_body: false, + supports_sandwich_like_formatting: false, } } pub const fn with_dedent(&self) -> Self { Self { indentation: self.indentation.with_dedent(), trailing_comma_condition: None, - is_single_expression_in_assignment_body: false, + supports_sandwich_like_formatting: false, } } pub const fn with_trailing_comma_condition(&self, condition: TrailingCommaCondition) -> Self { Self { indentation: self.indentation, trailing_comma_condition: Some(condition), - is_single_expression_in_assignment_body: false, + supports_sandwich_like_formatting: false, } } - pub const fn for_single_expression_in_assignment_body(&self) -> Self { + pub const fn with_sandwich_like_support(&self) -> Self { Self { indentation: self.indentation.with_indent(), trailing_comma_condition: None, - is_single_expression_in_assignment_body: true, + supports_sandwich_like_formatting: true, } } - pub fn resolve_for_expression_with_indented_lines( + pub fn resolve_for_sandwich_like( &self, previous_width: Width, first_line_extra_width: Width, ) -> (Self, bool) { - let uses_sandwich_like_multiline_formatting = self.is_single_expression_in_assignment_body + let uses_sandwich_like_multiline_formatting = self.supports_sandwich_like_formatting && previous_width.last_line_fits(self.indentation, first_line_extra_width); let info = Self { indentation: if uses_sandwich_like_multiline_formatting { @@ -70,7 +70,7 @@ impl FormattingInfo { self.indentation }, trailing_comma_condition: None, - is_single_expression_in_assignment_body: false, + supports_sandwich_like_formatting: false, }; (info, uses_sandwich_like_multiline_formatting) } @@ -280,10 +280,7 @@ pub fn format_cst<'a>( closing, } => { let (info, uses_sandwich_like_multiline_formatting) = info - .resolve_for_expression_with_indented_lines( - previous_width, - SinglelineWidth::DOUBLE_QUOTE.into(), - ); + .resolve_for_sandwich_like(previous_width, SinglelineWidth::DOUBLE_QUOTE.into()); let opening = format_cst(edits, previous_width, opening, &info); let closing = format_cst( @@ -730,7 +727,7 @@ pub fn format_cst<'a>( }; let (case_info, is_sandwich_like_multiline_formatting) = info - .resolve_for_expression_with_indented_lines( + .resolve_for_sandwich_like( previous_width, expression_width + SinglelineWidth::PERCENT, ); @@ -800,11 +797,8 @@ pub fn format_cst<'a>( body, closing_curly_brace, } => { - let (info, is_sandwich_like_multiline_formatting) = info - .resolve_for_expression_with_indented_lines( - previous_width, - SinglelineWidth::PARENTHESIS.into(), - ); + let (info, is_sandwich_like_multiline_formatting) = + info.resolve_for_sandwich_like(previous_width, SinglelineWidth::PARENTHESIS.into()); let opening_curly_brace = format_cst(edits, previous_width, opening_curly_brace, &info); @@ -983,7 +977,7 @@ pub fn format_cst<'a>( let body_info = if body.len() == 1 { // Avoid double indentation for bodies/items/entries in trailing functions/lists/ // structs. - info.for_single_expression_in_assignment_body() + info.with_sandwich_like_support() } else { info.with_indent() }; @@ -1234,17 +1228,14 @@ impl<'a> LastArgument<'a> { let is_singleline_before_last_argument = previous_width .last_line_fits(call_info.indentation, min_width_before_last_argument); - let last_argument_info = if call_info.is_single_expression_in_assignment_body { + let last_argument_info = if call_info.supports_sandwich_like_formatting { if is_singleline_before_last_argument { - call_info - .with_dedent() - .for_single_expression_in_assignment_body() + call_info.with_dedent().with_sandwich_like_support() } else { call_info.clone() } } else if is_singleline_before_last_argument { - // FIXME: rename method - call_info.for_single_expression_in_assignment_body() + call_info.with_sandwich_like_support() } else { call_info.with_indent() }; @@ -1281,7 +1272,7 @@ impl<'a> LastArgument<'a> { ), ends_with_sandwich_like_multiline_formatting: false, }; - let info = if call_info.is_single_expression_in_assignment_body + let info = if call_info.supports_sandwich_like_formatting && derived_call_info.is_quasi_singleline { derived_call_info.argument_info(call_info).with_dedent() diff --git a/compiler/formatter/src/format_collection.rs b/compiler/formatter/src/format_collection.rs index 4199fdd7e..c5535284e 100644 --- a/compiler/formatter/src/format_collection.rs +++ b/compiler/formatter/src/format_collection.rs @@ -17,11 +17,8 @@ pub fn format_collection<'a>( is_comma_required_for_single_item: bool, info: &FormattingInfo, ) -> FormattedCst<'a> { - let (info, uses_sandwich_like_multiline_formatting) = info - .resolve_for_expression_with_indented_lines( - previous_width, - SinglelineWidth::PARENTHESIS.into(), - ); + let (info, uses_sandwich_like_multiline_formatting) = + info.resolve_for_sandwich_like(previous_width, SinglelineWidth::PARENTHESIS.into()); let opening_punctuation = format_cst(edits, previous_width, opening_punctuation, &info); let closing_punctuation = format_cst(