@@ -2270,9 +2270,9 @@ fn format_call_chain_segments<'src>(
22702270 } )
22712271 } ) ;
22722272
2273- let is_user_multilined = call_chain_elements_are_user_multilined ( ps, & chain_elements) ;
2273+ let should_multiline = call_chain_should_be_multilined ( ps, & chain_elements) ;
22742274
2275- ps. breakable_call_chain_of ( is_user_multilined , |ps| {
2275+ ps. breakable_call_chain_of ( should_multiline , |ps| {
22762276 // Recurse and format previous segments inside this breakable
22772277 format_call_chain_segments ( ps, segments) ;
22782278
@@ -2426,10 +2426,7 @@ fn format_call_body<'src>(
24262426 }
24272427}
24282428
2429- fn call_chain_elements_are_user_multilined (
2430- ps : & ParserState ,
2431- call_chain_elements : & [ prism:: Node ] ,
2432- ) -> bool {
2429+ fn call_chain_should_be_multilined ( ps : & ParserState , call_chain_elements : & [ prism:: Node ] ) -> bool {
24332430 // Making a mutable copy since we may pop some items off later
24342431 let mut call_chain_elements = call_chain_elements;
24352432
@@ -2475,7 +2472,9 @@ fn call_chain_elements_are_user_multilined(
24752472 let has_comment_between_expression_and_call =
24762473 ps. has_comments_in_line ( leading_expr_end_line + 1 , first_call_start_line + 1 ) ;
24772474
2478- if !has_comment_between_expression_and_call {
2475+ if !has_comment_between_expression_and_call
2476+ && !element_forces_chain_to_multiline ( & call_chain_elements[ 0 ] )
2477+ {
24792478 call_chain_elements = & call_chain_elements[ 1 ..] ;
24802479 }
24812480 }
@@ -2514,18 +2513,50 @@ fn call_chain_elements_are_user_multilined(
25142513 }
25152514 } ;
25162515
2517- call_chain_elements[ 1 ..] . iter ( ) . any ( |cce| {
2518- start_line
2516+ call_chain_elements[ 1 ..] . iter ( ) . enumerate ( ) . any ( |( i , cce) | {
2517+ let operator_on_new_line = start_line
25192518 != cce
25202519 . as_call_node ( )
25212520 . unwrap ( )
25222521 . call_operator_loc ( )
25232522 . map_or ( start_line, |loc| {
25242523 ps. get_line_number_for_offset ( loc. start_offset ( ) )
2525- } )
2524+ } ) ;
2525+ if operator_on_new_line {
2526+ return true ;
2527+ }
2528+
2529+ // If the *previous* element will be forced to render multi-line (a do/end
2530+ // block, a brace block with multiple statements, or the equivalent on a
2531+ // lambda receiver), the chain itself must break too — otherwise the next
2532+ // pass would see the calls on different lines and rewrite the chain,
2533+ // costing idempotency.
2534+ element_forces_chain_to_multiline ( & call_chain_elements[ i] )
25262535 } )
25272536}
25282537
2538+ fn element_forces_chain_to_multiline ( element : & prism:: Node ) -> bool {
2539+ if let Some ( block) = element
2540+ . as_call_node ( )
2541+ . and_then ( |c| c. block ( ) )
2542+ . and_then ( |b| b. as_block_node ( ) )
2543+ {
2544+ return block_body_renders_multiline ( block. opening_loc ( ) . as_slice ( ) , block. body ( ) ) ;
2545+ } else if let Some ( lambda) = element. as_lambda_node ( ) {
2546+ return block_body_renders_multiline ( lambda. opening_loc ( ) . as_slice ( ) , lambda. body ( ) ) ;
2547+ }
2548+
2549+ false
2550+ }
2551+
2552+ fn block_body_renders_multiline ( opening : & [ u8 ] , body : Option < prism:: Node > ) -> bool {
2553+ if opening == b"do" {
2554+ return true ;
2555+ }
2556+ body. and_then ( |n| n. as_statements_node ( ) )
2557+ . is_some_and ( |statements| statements. body ( ) . len ( ) > 1 )
2558+ }
2559+
25292560/// Finds an appropriate starting loc for a call node inside a call chain.
25302561/// In the middle of a chain, the node's `location().start_offset()` is always the
25312562/// beginning of the chain, since `receiver()` is the entirety of the chain so far.
0 commit comments