Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b75aa49

Browse files
committed
Better call chain formatting with method add blocks factored in
1 parent ffb6531 commit b75aa49

File tree

1 file changed

+76
-13
lines changed

1 file changed

+76
-13
lines changed

lib/syntax_tree/node.rb

Lines changed: 76 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)