@@ -2221,29 +2221,88 @@ def initialize(node)
22212221 end
22222222
22232223 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 )
22242228 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 )
22262257 q . format ( children . last . receiver )
22272258
22282259 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
22302264
22312265 q . indent do
2232- children . reverse_each do | child |
2266+ while child = children . pop
22332267 case child
2234- in { receiver : Call [ message : { value : "where" } ] , message : { value : "not" } }
2268+ in Call [ receiver : Call [ message : { value : "where" } ] , message : { value : "not" } ]
22352269 # This is very specialized behavior wherein we group
22362270 # .where.not calls together because it looks better. For more
22372271 # information, see
22382272 # 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.
22402276 q . breakable ( "" )
2277+ else
22412278 end
22422279
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
22442285 end
22452286 end
22462287 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
22472306 end
22482307
22492308 private
@@ -2256,10 +2315,16 @@ def attach_directly?(child)
22562315 . include? ( child . receiver . class )
22572316 end
22582317
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
22632328
22642329 # If there are any comments on this node then we need to explicitly print
22652330 # them out here since we're bypassing the normal comment printing.
@@ -2342,7 +2407,7 @@ def format(q)
23422407 # If we're at the top of a call chain, then we're going to do some
23432408 # specialized printing in case we can print it nicely. We _only_ do this
23442409 # 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 )
23462411 q . group { q . if_break { CallChainFormatter . new ( self ) . format ( q ) } . if_flat { format_contents ( q ) } }
23472412 else
23482413 format_contents ( q )
@@ -2361,8 +2426,6 @@ def format_arguments(q)
23612426 end
23622427 end
23632428
2364- private
2365-
23662429 def format_contents ( q )
23672430 call_operator = CallOperatorFormatter . new ( operator )
23682431
0 commit comments