fables-tales / rubyfmt

Ruby Autoformatter!
MIT License
1.07k stars 50 forks source link

Render multiline brace blocks as breakables #426

Closed reese closed 1 year ago

reese commented 1 year ago

A side effect of #397 is that some multiline-able constructs (notably brace blocks) that aren't modeled with breakables could have the inner contents break but not the surrounding block. For example, something like

sig { params(foo: SomePrettyLongClassName, bar: AnEvenLongerClassName::ThatMakesThisGoPrettyFar, baz: Hasdfasdfasdfasdas) }

would incorrectly render as

sig { params(
  foo: SomePrettyLongClassName,
  bar: AnEvenLongerClassName::ThatMakesThisGoPrettyFar,
  baz: Hasdfasdfasdfasdas
) }

instead of breaking at the block level. This caused rubyfmt to take multiple passes to settle on an output, which is incorrect.

This PR changes the way we do brace blocks to model them as breakables, which means they'll be more accurate when rendering on long lines and they won't fall prey to the multilining issues mentioned above. The implementation is admittedly not the cleanest in the world, but this is largely due to the fact that brace blocks aren't "traditional" breakables in the sense that we need to be able to support blockvars and because there are some additional requirements around when exactly to break them that aren't only about line length. As such, there's some hacks sprinkled in here, but I did my best to heavily comment and test them.