Skip to content

Commit

Permalink
Model call chains as an AbstractTokenTarget (#443)
Browse files Browse the repository at this point in the history
This PR implements call chains as a new AbstractTokenTarget: BreakableCallChainEntry. This means that multilining for call chains -- just like for existing breakables -- will be decided on-the-fly during rendering, where we have more contextual information and can better enforce line length.

This is done with a combination of the normal breakable machinery and some additional "magic tokens." Specifically, this adds tokens at the beginning and end of the indent-able portion of the call chain, and then during rendering, we maintain a count of how many nested call chains we're in and adjust indentation accordingly. This isn't exactly the cleanest implementation, but given the complexity in use-cases for call chains, it was the best I could muster in a way that functioned how users would generally expect.

This is a fairly large PR, but there's primarily three main portions which each are about a third of the total diff each: fixtures, the boilerplate in ripper_tree_types to support getting expression starting lines, and finally the main formatting changes. I've done my best to comment these areas appropriately, but it's helpful to add further explanation there, I'm more than happy to add more.
  • Loading branch information
reese authored Aug 23, 2023
1 parent 46e08eb commit d170616
Show file tree
Hide file tree
Showing 35 changed files with 1,385 additions and 496 deletions.
9 changes: 8 additions & 1 deletion fixtures/small/aref_in_call_actual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@
)
.void
end
def foo; end
def foo; end

Array[
@root_fragment,
@lemon_tea_fragment,
@green_tea_fragment,
@cake_fragment
].sort_by(&:name)
7 changes: 7 additions & 0 deletions fixtures/small/aref_in_call_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@
end
def foo
end

Array[
@root_fragment,
@lemon_tea_fragment,
@green_tea_fragment,
@cake_fragment
].sort_by(&:name)
4 changes: 4 additions & 0 deletions fixtures/small/begin_end_stack_actual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
a = begin
end
end

begin
end
.freeze
3 changes: 3 additions & 0 deletions fixtures/small/begin_end_stack_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
a = begin
end
end

begin
end.freeze
9 changes: 9 additions & 0 deletions fixtures/small/brace_blocks_with_no_args_actual.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
def func
brace_block_with_no_args { p 'hi' }
end

{
"/v1/transfers" => lambda {
foo
bar
# TODO: add baz here
# Oh and maybe quux if you have time
}
}
9 changes: 9 additions & 0 deletions fixtures/small/brace_blocks_with_no_args_expected.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
def func
brace_block_with_no_args { p("hi") }
end

{
"/v1/transfers" => lambda {
foo
bar
# TODO: add baz here
# Oh and maybe quux if you have time
}
}
4 changes: 4 additions & 0 deletions fixtures/small/conditional_actual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ def foo
puts("hi")
end
end

do_stuff! if it_isnt_dangerous(
i_promise: true
)
6 changes: 6 additions & 0 deletions fixtures/small/conditional_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ def foo
puts("hi")
end
end

if it_isnt_dangerous(
i_promise: true
)
do_stuff!
end
11 changes: 11 additions & 0 deletions fixtures/small/heredoc_method_call_actual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,14 @@ class William::Carlos::Williams
Icarus drowning
LANDSCAPE
end

optp
.on do |value|
<<~EOF
There's some lines here
But that one's a blank line!
There shouldn't be any whitespace on those
EOF
end
11 changes: 11 additions & 0 deletions fixtures/small/heredoc_method_call_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,14 @@ class William::Carlos::Williams
Williams
)
end

optp
.on do |value|
<<~EOF
There's some lines here
But that one's a blank line!
There shouldn't be any whitespace on those
EOF
end
21 changes: 21 additions & 0 deletions fixtures/small/heredocs_actual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ class Foo
rubocop.smash(bad_thing)
end

this_one_is
.in_a_call_chain {
# some stuff
}
.each do
<<~MYHEREDOC
Words are pale shadows of forgotten names.
As names have power, words have power.
Words can light fires in the minds of men.
Words can wring tears from the hardest hearts.
MYHEREDOC
end

def foo
"#{stuff.each do
<<~MESSAGE.strip
#{message.text}
MESSAGE
end.join("\n\n")}"
end


puts a
puts b
Expand Down
22 changes: 22 additions & 0 deletions fixtures/small/heredocs_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,28 @@ class Foo
rubocop.smash(bad_thing)
end

this_one_is
.in_a_call_chain {
# some stuff
}
.each do
<<~MYHEREDOC
Words are pale shadows of forgotten names.
As names have power, words have power.
Words can light fires in the minds of men.
Words can wring tears from the hardest hearts.
MYHEREDOC
end

def foo
"#{stuff.each do
<<~MESSAGE
#{message.text}
MESSAGE
.strip
end.join("\n\n")}"
end

puts(a)
puts(b)
foo
6 changes: 4 additions & 2 deletions fixtures/small/method_annotation_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def do_the_thing(a, b)
params(
a: String,
b: String
).void
)
.void
end
def example(a, b)
end
Expand Down Expand Up @@ -95,7 +96,8 @@ class Bees
first_param: MyClass,
# This one is the second one, nice
second_param: YourClass
).void
)
.void
# Please not the bees!
}
def not_the_bees!
Expand Down
36 changes: 34 additions & 2 deletions fixtures/small/method_chains_actual.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
foo.bar
.baz

# If they're all on the same line but different from
# the first receiver, consider that "on one line"
foo
.bar.baz

Expand Down Expand Up @@ -97,6 +95,24 @@ def example
b: ""
)

ThisIs::OnlyOneCall
# but it's explicitly multilined with a
.comment!

OnlyOneCall
# but it's explicitly multilined with a
.comment!

[
# foo
]
# bar
.baz

[]
# Please don't do this
.freeze

Paul::Blart::Mall::Cop::PerformedByTheLegendaryKevinJamesWhoIsAnAbsoluteLegendInAllOfHisFilmsWhichAreAbsolutelyIncredible.consume_pixie_sticks(mall: "downtown").each do |punch_list_type|
end

Expand Down Expand Up @@ -129,3 +145,19 @@ def gather_thanes!
puts "h"
end
&.foo

My::Error.soft(
"",
stuff: {
message_token: message.token,

# Some comments!
value: id_or_email.name
}
)

# rubocop:disable PrisonGuard/PrivateModule
(foo.load_one
# rubocop:enable PrisonGuard/PrivateModule
.bar)
.thing
44 changes: 40 additions & 4 deletions fixtures/small/method_chains_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@
.bar
.baz

# If they're all on the same line but different from
# the first receiver, consider that "on one line"
foo.bar.baz
foo
.bar
.baz

foo::bar&.nil?
foo::bar
&.nil?

foo::bar
&.nil?::klass
Expand Down Expand Up @@ -120,6 +121,24 @@ def example
b: ""
)

ThisIs::OnlyOneCall
# but it's explicitly multilined with a
.comment!

OnlyOneCall
# but it's explicitly multilined with a
.comment!

[
# foo
]
# bar
.baz

[]
# Please don't do this
.freeze

Paul::Blart::Mall::Cop::PerformedByTheLegendaryKevinJamesWhoIsAnAbsoluteLegendInAllOfHisFilmsWhichAreAbsolutelyIncredible
.consume_pixie_sticks(mall: "downtown")
.each do |punch_list_type|
Expand Down Expand Up @@ -160,3 +179,20 @@ def gather_thanes!
puts("h")
end
&.foo

My::Error.soft(
"",
stuff: {
message_token: message.token,

# Some comments!
value: id_or_email.name
}
)

# rubocop:disable PrisonGuard/PrivateModule
(foo
.load_one
# rubocop:enable PrisonGuard/PrivateModule
.bar)
.thing
3 changes: 2 additions & 1 deletion fixtures/small/multiline_chain_in_block_expected.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
sig {
params(
route: String
).void
)
.void
}
def ajax_get(route)
super
Expand Down
3 changes: 2 additions & 1 deletion fixtures/small/multiline_chained_call_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ def blorp
method_call(
arg_a,
arg_b
).chained_call
)
.chained_call
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ def stub_server(path:, body: {})
"https://example.com#{path}"
).to_return(body: body.to_json)
end

{
"original_fields" => foo,
"alternative_fields" => (thing_one(id, api) + thing_two(
id,
api
)).sort
}
12 changes: 11 additions & 1 deletion fixtures/small/multiline_method_chain_with_arguments_expected.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,15 @@ def stub_server(path:, body: {})
stub_request(
:get,
"https://example.com#{path}"
).to_return(body: body.to_json)
)
.to_return(body: body.to_json)
end

{
"original_fields" => foo,
"alternative_fields" => (thing_one(id, api) + thing_two(
id,
api
))
.sort
}
14 changes: 14 additions & 0 deletions fixtures/small/paren_expr_calls_actual.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
a (1)
other_cool_method (a + b).round(4)

# rubocop:disable PrisonGuard/PrivateModule
(foo(
foo # rubocop:enable PrisonGuard/PrivateModule
)).flatten

# rubocop:disable Style/Stuff
(MyModel::InSomeNamespace
.load_one(
# rubocop:enable Style/Stuff
{name: "name"}
)
&.rules)
.freeze
16 changes: 16 additions & 0 deletions fixtures/small/paren_expr_calls_expected.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,18 @@
a(1)
other_cool_method((a + b).round(4))

# rubocop:disable PrisonGuard/PrivateModule
(foo(
# rubocop:enable PrisonGuard/PrivateModule
foo
))
.flatten

# rubocop:disable Style/Stuff
(MyModel::InSomeNamespace
.load_one(
# rubocop:enable Style/Stuff
{name: "name"}
)
&.rules)
.freeze
Loading

0 comments on commit d170616

Please sign in to comment.