@@ -2221,29 +2221,88 @@ def initialize(node)
2221
2221
end
2222
2222
2223
2223
def format ( q )
2224
+ q . group { q . if_break { format_chain ( q ) } . if_flat { node . format_contents ( q ) } }
2225
+ end
2226
+
2227
+ def format_chain ( q )
2224
2228
children = [ node ]
2225
- children << children . last . receiver while children . last . receiver . is_a? ( Call )
2229
+
2230
+ # First, walk down the chain until we get to the point where we're not
2231
+ # longer at a chainable node.
2232
+ while true
2233
+ case children . last
2234
+ in Call [ receiver : Call ]
2235
+ children << children . last . receiver
2236
+ in Call [ receiver : MethodAddBlock [ call : Call ] ]
2237
+ children << children . last . receiver
2238
+ in MethodAddBlock [ call : Call ]
2239
+ children << children . last . call
2240
+ else
2241
+ break
2242
+ end
2243
+ end
2244
+
2245
+ # We're going to have some specialized behavior for if it's an entire
2246
+ # chain of calls without arguments except for the last one. This is common
2247
+ # enough in Ruby source code that it's worth the extra complexity here.
2248
+ empty_except_last =
2249
+ children . drop ( 1 ) . all? do |child |
2250
+ child . is_a? ( Call ) && child . arguments . nil?
2251
+ end
2252
+
2253
+ # Here, we're going to add all of the children onto the stack of the
2254
+ # formatter so it's as if we had descending normally into them. This is
2255
+ # necessary so they can check their parents as normal.
2256
+ q . stack . concat ( children )
2226
2257
q . format ( children . last . receiver )
2227
2258
2228
2259
q . group do
2229
- format_child ( q , children . pop ) if attach_directly? ( children . last )
2260
+ if attach_directly? ( children . last )
2261
+ format_child ( q , children . pop )
2262
+ q . stack . pop
2263
+ end
2230
2264
2231
2265
q . indent do
2232
- children . reverse_each do | child |
2266
+ while child = children . pop
2233
2267
case child
2234
- in { receiver : Call [ message : { value : "where" } ] , message : { value : "not" } }
2268
+ in Call [ receiver : Call [ message : { value : "where" } ] , message : { value : "not" } ]
2235
2269
# This is very specialized behavior wherein we group
2236
2270
# .where.not calls together because it looks better. For more
2237
2271
# information, see
2238
2272
# https://github.com/prettier/plugin-ruby/issues/862.
2239
- else
2273
+ in Call
2274
+ # If we're at a Call node and not a MethodAddBlock node in the
2275
+ # chain then we're going to add a newline so it indents properly.
2240
2276
q . breakable ( "" )
2277
+ else
2241
2278
end
2242
2279
2243
- format_child ( q , child )
2280
+ format_child ( q , child , skip_attached : empty_except_last && children . empty? )
2281
+
2282
+ # Pop off the formatter's stack so that it aligns with what would
2283
+ # have happened if we had been formatting normally.
2284
+ q . stack . pop
2244
2285
end
2245
2286
end
2246
2287
end
2288
+
2289
+ if empty_except_last
2290
+ case node
2291
+ in Call
2292
+ node . format_arguments ( q )
2293
+ in MethodAddBlock [ block :]
2294
+ q . format ( block )
2295
+ end
2296
+ end
2297
+ end
2298
+
2299
+ def self . chained? ( node )
2300
+ case node
2301
+ in Call | MethodAddBlock [ call : Call ]
2302
+ true
2303
+ else
2304
+ false
2305
+ end
2247
2306
end
2248
2307
2249
2308
private
@@ -2256,10 +2315,16 @@ def attach_directly?(child)
2256
2315
. include? ( child . receiver . class )
2257
2316
end
2258
2317
2259
- def format_child ( q , child )
2260
- q . format ( CallOperatorFormatter . new ( child . operator ) )
2261
- q . format ( child . message ) if child . message != :call
2262
- child . format_arguments ( q )
2318
+ def format_child ( q , child , skip_attached : false )
2319
+ # First, format the actual contents of the child.
2320
+ case child
2321
+ in Call
2322
+ q . format ( CallOperatorFormatter . new ( child . operator ) )
2323
+ q . format ( child . message ) if child . message != :call
2324
+ child . format_arguments ( q ) unless skip_attached
2325
+ in MethodAddBlock
2326
+ q . format ( child . block ) unless skip_attached
2327
+ end
2263
2328
2264
2329
# If there are any comments on this node then we need to explicitly print
2265
2330
# them out here since we're bypassing the normal comment printing.
@@ -2342,7 +2407,7 @@ def format(q)
2342
2407
# If we're at the top of a call chain, then we're going to do some
2343
2408
# specialized printing in case we can print it nicely. We _only_ do this
2344
2409
# at the top of the chain to avoid weird recursion issues.
2345
- if !q . parent . is_a? ( Call ) && receiver . is_a? ( Call )
2410
+ if !CallChainFormatter . chained? ( q . parent ) && CallChainFormatter . chained? ( receiver )
2346
2411
q . group { q . if_break { CallChainFormatter . new ( self ) . format ( q ) } . if_flat { format_contents ( q ) } }
2347
2412
else
2348
2413
format_contents ( q )
@@ -2361,8 +2426,6 @@ def format_arguments(q)
2361
2426
end
2362
2427
end
2363
2428
2364
- private
2365
-
2366
2429
def format_contents ( q )
2367
2430
call_operator = CallOperatorFormatter . new ( operator )
2368
2431
0 commit comments