Kotlin recipe DSL: preserve dot-on-its-own-line layout across chain collapse#7729
Merged
Conversation
…ollapse
Real-world Kotlin chains are formatted with each `.member(...)` on its own
indented line:
xs
.filter { p }
.firstOrNull()
The K2 plugin's chain-collapse rewrite synthesizes a single fused call
(`xs.firstOrNull(p)`) by applying a Kotlin template at the matched outer's
coordinates. The result's outer-call `select.after` defaults to "" — the
whitespace that used to sit between the inner's text end and the outer's
"." is lost — and the synthesized `.firstOrNull(p)` ends up jammed onto
the previous line:
xs
.filter { p }.firstOrNull { p } // before this fix
`preserveSelectAfter` copies the matched outer call's `select.after`
onto the template-substituted result so the new outer sits on its own
dot-prefixed line, matching where the matched outer originally was:
xs
.filter { p }
.firstOrNull { p } // after this fix
Wired into every method-invocation rewrite path:
- `methodInvocationRewrite` (bare Kotlin)
- `methodInvocationRewriteJava` (Java template)
- `methodInvocationRewriteKotlinNotNull` (K.Unary unwrap)
- `chainMethodInvocationRewrite` (the two-segment chain path)
No-op when the matched outer has no `select.after` whitespace (already
single-line) or when the result has no select (template produced a
non-method shape).
Surfaced by Performance / BestPractices chain-collapse recipes in
moderneinc/recipes-kotlin running against real corpora.
4b4fcb9 to
8b5cd98
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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
Real-world Kotlin chains are formatted with each
.member(...)on its own indented line:xs .filter { p } .firstOrNull()The K2 plugin's chain-collapse rewrite synthesizes a single fused call (
xs.firstOrNull(p)) by applying aKotlinTemplateat the matched outer's coordinates. The result's outer-callselect.afterdefaults to""— the whitespace that used to sit between the inner's text end and the outer's.is lost — and the synthesized.firstOrNull(p)ends up jammed onto the previous line. Surfaced by Performance / BestPractices chain-collapse recipes inmoderneinc/recipes-kotlinrunning against real corpora.preserveSelectAftercopies the matched outer call'sselect.afteronto the template-substituted result so the new outer sits on its own dot-prefixed line, matching where the matched outer originally was.Wired into every method-invocation rewrite path so chain-shape and bare-shape rewrites both preserve the author's line layout:
methodInvocationRewrite(bare Kotlin)methodInvocationRewriteJava(Java template)methodInvocationRewriteKotlinNotNull(K.Unary unwrap)chainMethodInvocationRewrite(two-segment chain)No-op when the matched outer has no
select.afterwhitespace (already single-line) or when the result has no select (template produced a non-method shape).Test plan
Four new tests in
RecipePluginRewriteTest:chain collapse preserves dot-on-its-own-line layout— positive case for the chain path (filter(p).firstOrNull()collapse with multi-line layout in the source)chain collapse — single-line input stays single-line (no false-positive newline)— negative case proving the empty-whitespace guard doesn't insert newlines into single-line chainschain collapse — alternate shape (map then filterNotNull) also preserves layout— second chain-shape variant proving the fix isn't specific to filter/firstOrNull (this is the shape that surfaced the bug)bare single-call rewrite preserves dot-on-its-own-line layout— exercises the non-chainmethodInvocationRewritepath so we don't regress one branch while fixing another./gradlew :rewrite-kotlin:testgreen.