@@ -2214,29 +2214,88 @@ def initialize(node)
2214
2214
end
2215
2215
2216
2216
def format ( q )
2217
+ q . group { q . if_break { format_chain ( q ) } . if_flat { node . format_contents ( q ) } }
2218
+ end
2219
+
2220
+ def format_chain ( q )
2217
2221
children = [ node ]
2218
- children << children . last . receiver while children . last . receiver . is_a? ( Call )
2222
+
2223
+ # First, walk down the chain until we get to the point where we're not
2224
+ # longer at a chainable node.
2225
+ while true
2226
+ case children . last
2227
+ in Call [ receiver : Call ]
2228
+ children << children . last . receiver
2229
+ in Call [ receiver : MethodAddBlock [ call : Call ] ]
2230
+ children << children . last . receiver
2231
+ in MethodAddBlock [ call : Call ]
2232
+ children << children . last . call
2233
+ else
2234
+ break
2235
+ end
2236
+ end
2237
+
2238
+ # We're going to have some specialized behavior for if it's an entire
2239
+ # chain of calls without arguments except for the last one. This is common
2240
+ # enough in Ruby source code that it's worth the extra complexity here.
2241
+ empty_except_last =
2242
+ children . drop ( 1 ) . all? do |child |
2243
+ child . is_a? ( Call ) && child . arguments . nil?
2244
+ end
2245
+
2246
+ # Here, we're going to add all of the children onto the stack of the
2247
+ # formatter so it's as if we had descending normally into them. This is
2248
+ # necessary so they can check their parents as normal.
2249
+ q . stack . concat ( children )
2219
2250
q . format ( children . last . receiver )
2220
2251
2221
2252
q . group do
2222
- format_child ( q , children . pop ) if attach_directly? ( children . last )
2253
+ if attach_directly? ( children . last )
2254
+ format_child ( q , children . pop )
2255
+ q . stack . pop
2256
+ end
2223
2257
2224
2258
q . indent do
2225
- children . reverse_each do | child |
2259
+ while child = children . pop
2226
2260
case child
2227
- in { receiver : Call [ message : { value : "where" } ] , message : { value : "not" } }
2261
+ in Call [ receiver : Call [ message : { value : "where" } ] , message : { value : "not" } ]
2228
2262
# This is very specialized behavior wherein we group
2229
2263
# .where.not calls together because it looks better. For more
2230
2264
# information, see
2231
2265
# https://github.com/prettier/plugin-ruby/issues/862.
2232
- else
2266
+ in Call
2267
+ # If we're at a Call node and not a MethodAddBlock node in the
2268
+ # chain then we're going to add a newline so it indents properly.
2233
2269
q . breakable ( "" )
2270
+ else
2234
2271
end
2235
2272
2236
- format_child ( q , child )
2273
+ format_child ( q , child , skip_attached : empty_except_last && children . empty? )
2274
+
2275
+ # Pop off the formatter's stack so that it aligns with what would
2276
+ # have happened if we had been formatting normally.
2277
+ q . stack . pop
2237
2278
end
2238
2279
end
2239
2280
end
2281
+
2282
+ if empty_except_last
2283
+ case node
2284
+ in Call
2285
+ node . format_arguments ( q )
2286
+ in MethodAddBlock [ block :]
2287
+ q . format ( block )
2288
+ end
2289
+ end
2290
+ end
2291
+
2292
+ def self . chained? ( node )
2293
+ case node
2294
+ in Call | MethodAddBlock [ call : Call ]
2295
+ true
2296
+ else
2297
+ false
2298
+ end
2240
2299
end
2241
2300
2242
2301
private
@@ -2249,10 +2308,16 @@ def attach_directly?(child)
2249
2308
. include? ( child . receiver . class )
2250
2309
end
2251
2310
2252
- def format_child ( q , child )
2253
- q . format ( CallOperatorFormatter . new ( child . operator ) )
2254
- q . format ( child . message ) if child . message != :call
2255
- child . format_arguments ( q )
2311
+ def format_child ( q , child , skip_attached : false )
2312
+ # First, format the actual contents of the child.
2313
+ case child
2314
+ in Call
2315
+ q . format ( CallOperatorFormatter . new ( child . operator ) )
2316
+ q . format ( child . message ) if child . message != :call
2317
+ child . format_arguments ( q ) unless skip_attached
2318
+ in MethodAddBlock
2319
+ q . format ( child . block ) unless skip_attached
2320
+ end
2256
2321
2257
2322
# If there are any comments on this node then we need to explicitly print
2258
2323
# them out here since we're bypassing the normal comment printing.
@@ -2335,7 +2400,7 @@ def format(q)
2335
2400
# If we're at the top of a call chain, then we're going to do some
2336
2401
# specialized printing in case we can print it nicely. We _only_ do this
2337
2402
# at the top of the chain to avoid weird recursion issues.
2338
- if !q . parent . is_a? ( Call ) && receiver . is_a? ( Call )
2403
+ if !CallChainFormatter . chained? ( q . parent ) && CallChainFormatter . chained? ( receiver )
2339
2404
q . group { q . if_break { CallChainFormatter . new ( self ) . format ( q ) } . if_flat { format_contents ( q ) } }
2340
2405
else
2341
2406
format_contents ( q )
@@ -2354,8 +2419,6 @@ def format_arguments(q)
2354
2419
end
2355
2420
end
2356
2421
2357
- private
2358
-
2359
2422
def format_contents ( q )
2360
2423
call_operator = CallOperatorFormatter . new ( operator )
2361
2424
0 commit comments