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
2443 2444 2445 |
# File 'lib/syntax_tree/node.rb', line 2443 def initialize(node) @node = node end |
Instance Attribute Details
#node ⇒ Object (readonly)
- Call | MethodAddBlock
-
the top of the call chain
2441 2442 2443 |
# File 'lib/syntax_tree/node.rb', line 2441 def node @node end |
Class Method Details
.chained?(node) ⇒ Boolean
2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 |
# File 'lib/syntax_tree/node.rb', line 2581 def self.chained?(node) return false if ENV["STREE_FAST_FORMAT"] case node when Call true when MethodAddBlock node.call.is_a?(Call) else false end end |
Instance Method Details
#format(q) ⇒ Object
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 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 |
# File 'lib/syntax_tree/node.rb', line 2447 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 (child = children.last) when Call case (receiver = child.receiver) when Call children << receiver when MethodAddBlock receiver.call.is_a?(Call) ? children << receiver : break else break end when MethodAddBlock child.call.is_a?(Call) ? children << child.call : break 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) if parent.is_a?(MethodAddBlock) && parent.call.is_a?(FCall) && parent.call.value.value == "sig" threshold = 2 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
2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 |
# File 'lib/syntax_tree/node.rb', line 2501 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) if child.is_a?(Call) if child.receiver.is_a?(Call) && (child.receiver. != :call) && (child.receiver..value == "where") && (child..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. else # 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_empty end 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. last_child = children.last if last_child.is_a?(Call) && last_child..comments.any? q.format(CallOperatorFormatter.new(last_child.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 when Call node.format_arguments(q) when MethodAddBlock q.format(node.block) end end end |