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
- CallNode | 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.
2725 2726 2727 |
# File 'lib/syntax_tree/node.rb', line 2725 def initialize(node) @node = node end |
Instance Attribute Details
#node ⇒ Object (readonly)
- CallNode | MethodAddBlock
-
the top of the call chain
2723 2724 2725 |
# File 'lib/syntax_tree/node.rb', line 2723 def node @node end |
Class Method Details
.chained?(node) ⇒ Boolean
2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 |
# File 'lib/syntax_tree/node.rb', line 2876 def self.chained?(node) return false if ENV["STREE_FAST_FORMAT"] case node when CallNode !node.receiver.nil? when MethodAddBlock call = node.call call.is_a?(CallNode) && !call.receiver.nil? else false end end |
Instance Method Details
#format(q) ⇒ Object
2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 |
# File 'lib/syntax_tree/node.rb', line 2729 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 CallNode case (receiver = child.receiver) when CallNode if receiver.receiver.nil? break else children << receiver end when MethodAddBlock if (call = receiver.call).is_a?(CallNode) && !call.receiver.nil? children << receiver else break end else break end when MethodAddBlock if (call = child.call).is_a?(CallNode) && !call.receiver.nil? children << call else break end 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 block with the `do` keywords, 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?(BlockNode) && parent.keywords? if parent.is_a?(MethodAddBlock) && (call = parent.call).is_a?(CallNode) && call..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
2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 |
# File 'lib/syntax_tree/node.rb', line 2795 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?(CallNode) && 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) if 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?(CallNode) if (receiver = child.receiver).is_a?(CallNode) && (receiver. != :call) && (receiver..value == "where") && (.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?(CallNode) && last_child..comments.any? && last_child.operator 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 CallNode node.format_arguments(q) when MethodAddBlock q.format(node.block) end end end |