@@ -252,38 +252,82 @@ impl AbstractTokenTarget for BreakableCallChainEntry {
252252 // individual line and get _that_ max length.
253253 let mut tokens = self . tokens . clone ( ) ;
254254 if tokens. len ( ) > 2 {
255- if let Some ( AbstractLineToken :: ConcreteLineToken ( ConcreteLineToken :: End ) ) =
256- tokens. get ( tokens. len ( ) - 2 )
257- {
258- // Pop off all tokens that make up the `do`/`end` block (but not `do`!),
255+ let index = tokens. len ( ) - 2 ;
256+ let token = tokens. get_mut ( index) . unwrap ( ) ;
257+ if matches ! (
258+ token,
259+ AbstractLineToken :: ConcreteLineToken ( ConcreteLineToken :: End )
260+ ) {
261+ // Pop off all tokens that make up the block (but not the block params!),
259262 // since we assume that the block contents will handle their own line
260263 // length appropriately.
261264 while let Some ( token) = tokens. last ( ) {
262265 if matches ! (
263266 token,
264- AbstractLineToken :: ConcreteLineToken ( ConcreteLineToken :: DoKeyword )
267+ AbstractLineToken :: BreakableEntry ( BreakableEntry { delims , .. } ) if * delims == BreakableDelims :: for_block_params ( )
265268 ) {
266269 break ;
267270 }
268271 tokens. pop ( ) ;
269272 }
273+ } else if let AbstractLineToken :: BreakableEntry ( BreakableEntry {
274+ delims,
275+ ref mut tokens,
276+ ..
277+ } ) = token
278+ {
279+ if * delims == BreakableDelims :: for_brace_block ( ) {
280+ if let Some ( AbstractLineToken :: BreakableEntry ( BreakableEntry {
281+ delims, ..
282+ } ) ) = tokens. first ( )
283+ {
284+ if * delims == BreakableDelims :: for_block_params ( ) {
285+ // Wipe away the body of the block and leave only the params
286+ * tokens = vec ! [ tokens. first( ) . unwrap( ) . clone( ) ] ;
287+ } else {
288+ // No params, so wipe away the whole thing
289+ * tokens = Vec :: new ( ) ;
290+ }
291+ } else {
292+ // No params, so wipe away the whole thing
293+ * tokens = Vec :: new ( ) ;
294+ }
295+ }
270296 }
271297 }
272298
273299 if let Some ( AbstractLineToken :: BreakableEntry ( _) ) = tokens. first ( ) {
274300 tokens. remove ( 0 ) ;
275301 }
276- // EndCallChainIndent, which we don't care about
277- tokens. pop ( ) ;
278- // If the last breakable extends beyond the line length but the call chain doesn't,
279- // the breakable will break itself, e.g.
280- // ```ruby
281- // # ↓ if the break is here, we'll break the parens instead of the call chain
282- // AssumeThisIs.one_hundred_twenty_characters(breaks_here)
283- // ```
284- if let Some ( AbstractLineToken :: BreakableEntry ( _) ) = tokens. last ( ) {
302+ if let Some ( AbstractLineToken :: ConcreteLineToken ( ConcreteLineToken :: EndCallChainIndent ) ) =
303+ tokens. last ( )
304+ {
285305 tokens. pop ( ) ;
286306 }
307+ let call_count = tokens
308+ . iter ( )
309+ . filter ( |t| {
310+ matches ! (
311+ t,
312+ AbstractLineToken :: ConcreteLineToken (
313+ ConcreteLineToken :: Dot | ConcreteLineToken :: LonelyOperator
314+ )
315+ )
316+ } )
317+ . count ( ) ;
318+ // If the last breakable is multiline (and not a block/block params), ignore it. The user likely
319+ // intentionally chose a line break strategy, so try our best to respect it.
320+ //
321+ // However, if there's only one item in the chain, try our best to leave that in place.
322+ // `foo\n.bar` is always a little awkward.
323+ if let Some ( AbstractLineToken :: BreakableEntry ( be) ) = tokens. last ( ) {
324+ if ( call_count == 1 || be. is_multiline ( ) )
325+ && be. delims != BreakableDelims :: for_brace_block ( )
326+ && be. delims != BreakableDelims :: for_block_params ( )
327+ {
328+ tokens. pop ( ) ;
329+ }
330+ }
287331 tokens. insert (
288332 0 ,
289333 AbstractLineToken :: ConcreteLineToken (
0 commit comments