Class: SyntaxTree::CallChainFormatter
- Inherits:
-
Object
- Object
- SyntaxTree::CallChainFormatter
- Defined in:
- lib/syntax_tree/node.rb
Overview
This is probably the most complicated formatter in this file. It’s responsible for formatting chains of method calls, with or without arguments or blocks. In general, we want to go from something like
foo..baz
to
foo
.
.baz
Of course there are a lot of caveats to that, including trailing operators when necessary, where comments are places, how blocks are aligned, etc.
Instance Attribute Summary collapse
-
#node ⇒ Object
readonly
- Call | MethodAddBlock
-
the top of the call chain.
Class Method Summary collapse
Instance Method Summary collapse
- #format(q) ⇒ Object
- #format_chain(q, children) ⇒ Object
-
#initialize(node) ⇒ CallChainFormatter
constructor
A new instance of CallChainFormatter.
Constructor Details
#initialize(node) ⇒ CallChainFormatter
Returns a new instance of CallChainFormatter.
2344 2345 2346 |
# File 'lib/syntax_tree/node.rb', line 2344 def initialize(node) @node = node end |
Instance Attribute Details
#node ⇒ Object (readonly)
- Call | MethodAddBlock
-
the top of the call chain
2342 2343 2344 |
# File 'lib/syntax_tree/node.rb', line 2342 def node @node end |
Class Method Details
.chained?(node) ⇒ Boolean
2477 2478 2479 2480 2481 2482 2483 2484 |
# File 'lib/syntax_tree/node.rb', line 2477 def self.chained?(node) case node in Call | MethodAddBlock[call: Call] true else false end end |
Instance Method Details
#format(q) ⇒ Object
2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 |
# File 'lib/syntax_tree/node.rb', line 2348 def format(q) children = [node] threshold = 3 # First, walk down the chain until we get to the point where we're not # longer at a chainable node. loop do case children.last in Call[receiver: Call] children << children.last.receiver in Call[receiver: MethodAddBlock[call: Call]] children << children.last.receiver in MethodAddBlock[call: Call] children << children.last.call else break end end # Here, we have very specialized behavior where if we're within a sig # block, then we're going to assume we're creating a Sorbet type # signature. In that case, we really want the threshold to be lowered so # that we create method chains off of any two method calls within the # block. For more details, see # https://github.com/prettier/plugin-ruby/issues/863. parents = q.parents.take(4) if (parent = parents[2]) # If we're at a do_block, then we want to go one more level up. This is # because do blocks have BodyStmt nodes instead of just Statements # nodes. parent = parents[3] if parent.is_a?(DoBlock) case parent in MethodAddBlock[call: FCall[value: { value: "sig" }]] threshold = 2 else end end if children.length >= threshold q.group do q .if_break { format_chain(q, children) } .if_flat { node.format_contents(q) } end else node.format_contents(q) end end |
#format_chain(q, children) ⇒ Object
2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 |
# File 'lib/syntax_tree/node.rb', line 2398 def format_chain(q, children) # We're going to have some specialized behavior for if it's an entire # chain of calls without arguments except for the last one. This is common # enough in Ruby source code that it's worth the extra complexity here. empty_except_last = children .drop(1) .all? { |child| child.is_a?(Call) && child.arguments.nil? } # Here, we're going to add all of the children onto the stack of the # formatter so it's as if we had descending normally into them. This is # necessary so they can check their parents as normal. q.stack.concat(children) q.format(children.last.receiver) q.group do if attach_directly?(children.last) format_child(q, children.pop) q.stack.pop end q.indent do # We track another variable that checks if you need to move the # operator to the previous line in case there are trailing comments # and a trailing operator. skip_operator = false while (child = children.pop) case child in Call[ receiver: Call[message: { value: "where" }], message: { value: "not" } ] # This is very specialized behavior wherein we group # .where.not calls together because it looks better. For more # information, see # https://github.com/prettier/plugin-ruby/issues/862. in Call # If we're at a Call node and not a MethodAddBlock node in the # chain then we're going to add a newline so it indents properly. q.breakable("") else end format_child( q, child, skip_comments: children.empty?, skip_operator: skip_operator, skip_attached: empty_except_last && children.empty? ) # If the parent call node has a comment on the message then we need # to print the operator trailing in order to keep it working. case children.last in Call[message: { comments: [_, *] }, operator:] q.format(CallOperatorFormatter.new(operator)) skip_operator = true else skip_operator = false end # Pop off the formatter's stack so that it aligns with what would # have happened if we had been formatting normally. q.stack.pop end end end if empty_except_last case node in Call node.format_arguments(q) in MethodAddBlock[block:] q.format(block) end end end |