fix(js_formatter): refactor indention for arrow chains #1136
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
Playground Link with all the affected cases.
This PR reorganizes where indents are placed in
ArrowChain
to better match Prettier, and to avoid the need to usededent
. The primary issue this was made to resolve was parameter lists appearing overly-dedented when the parameters broke over multiple lines:Here, the
b
signature is appropriately indented, and the following signature is appropriately indented one additional level, but the parameters within that second signature are dedented outward, causing them to not line up with thee
signature that follows. The correct print for this statement is:The reason this happened was because of a
dedent
getting added around the parameters for each signature. That was originally added in #804 as an appropriate fix to parameters that were being overly indented two levels when breaking. But the root cause of the issue was further up the chain. Why were the parameters being indented twice at all?It has to do with the difference between how Biome and Prettier print out arrow chains. Prettier prints them recursively, nesting each signature as the body of the previous expression, up until the tail of the chain, which is at the top-level. All signatures other than the first in a chain are also indented a second level in, and so in the body for the first signature, Prettier adds another
indent
, and then the rest of the chain continues recursively. A simplified IR for this could be:The
indent
around eachsignatureParams
then ensures they are indented one level further in if the parameter list breaks (see the first corrected example at the top). And this works exactly as expected.Biome, however, prints the signature chain as a flat list, which is more efficient to process, but has different semantics. Instead of being able to rely on the first signature adding the indent and all of the subsequent ones using it implicitly, each signature has to manage its own indent level. A simplified IR here could be:
This example is stripped down from a direct export from Biome before these changes. Importantly, notice that there are two
indent
s around the entire signature chain. Somehow the first signature was appropriately not indenting a second time, but all of the following ones were. I'm not at all sure how, and I think that's part of why this was not functioning as expected. Unfortunately I do not have the capacity to figure that out now, but it isn't an issue anymore anyway since these changes refactor it entirely.Anyway, this IR also shows that along with the two outer indents, each signature is also wrapped with both a
dedent
and anotherindent
, effectively cancelling each other out. But something internally obviously doesn't fully cancel, since we encountered the original issue here as shown in the first example: the parameter list ends up overly dedented when it breaks over multiple lines. I'm also not entirely sure why here, but this combination ofindent
anddedent
was clearly causing some of it.So, this PR refactors all of the indention handling for arrow chains to more closely match Prettier. After the refactor, a simplified IR from Biome looks like this:
This matches much closer to Prettier's representation, removes the need for
dedent
at all, and also removes the mysteriously-correct behavior with behavior that follows very clearly from the IR. The entire chain is indented by the outer indent, then the signatures are written as a flat list. The first signature writes itself directly, with the parameters gaining an indent for if they break. All of the subsequent signatures then also indent themselves an additional level, as expected in the printed output.Another nice consequence of this refactor is that some of the conditional logic that was correcting the previous odd behaviors is no longer necessary, like checking if the expression is within a call expression or not. That behavior now comes naturally from the existing IR, matching Prettier as well.
Note also that there are other permutations of arrow chains that are handled slightly differently, hence the reason that the
=>
tokens are not part of the indent. Those cases are commented inline in the code.Test Plan
There were somehow no tests that covered this! Like nothing about indention of expanded parameters in arrow chains. Not from Prettier and not in our own specs.....wild. So I added a new spec test with all of the permutations of this pattern I've seen, and the snapshot appears as expected, matching the output of Prettier.