Skip to content

Commit 2bb150d

Browse files
lwasylAbdullinAM
authored andcommitted
Fix non-idempotent runs when lambda with chained call is broken (#640)
1 parent fdec601 commit 2bb150d

4 files changed

Lines changed: 31 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
2525
### Fixed
2626

2727
* Fix non-idempotent formatting when a managed trailing comma pushes a line over MAX_WIDTH (e.g. a long qualified expression as the last argument of a call). The comma is now accounted for by re-running the layout, so the line is broken correctly on the first pass. (https://github.com/facebook/ktfmt/pull/636)
28+
* Fix non-idempotent formatting when max width breaks a scoping-function lambda with a chained call (e.g. `runCatching { ... }.getOrNull()`): the chained call now breaks onto its own line together with the lambda on the first pass. (https://github.com/Kotlin/ktfmt/issues/640)
2829

2930

3031
## [0.64]

core/src/main/kotlin/org/jetbrains/ktfmt/format/KotlinInputAstVisitor.kt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1455,14 +1455,16 @@ open class KotlinInputAstVisitor(
14551455

14561456
visitLambdaOrScopingFunction(root, emitLeadingBreak = emitLeadingBreak)
14571457

1458-
builder.block(expressionBreakIndent) {
1459-
for (i in 1 until parts.size) {
1460-
val part = parts[i] as KtQualifiedExpression
1461-
if (forceBreakBeforeChain) {
1462-
builder.forcedBreak()
1463-
} else {
1464-
builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
1465-
}
1458+
// The break before each selector must stay outside the block below, at the same level as
1459+
// the lambda, so that it is taken exactly when the lambda breaks. Inside the block it
1460+
// would fire only when the selector itself is too long, so a lambda broken by max width
1461+
// would keep its selector on the closing brace's line — and the next format pass, seeing
1462+
// a multiline lambda in the source, would force the selector onto its own line (#640).
1463+
val fillMode = if (forceBreakBeforeChain) Doc.FillMode.FORCED else Doc.FillMode.UNIFIED
1464+
for (i in 1 until parts.size) {
1465+
val part = parts[i] as KtQualifiedExpression
1466+
builder.breakOp(fillMode, "", expressionBreakIndent)
1467+
builder.block(expressionBreakIndent) {
14661468
builder.token(part.operationSign.value)
14671469
val selectorExpression = part.selectorExpression
14681470
if (selectorExpression is KtCallExpression) {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// MAX_WIDTH 42
2+
// BLOCK_INDENT 2
3+
// CONTINUATION_INDENT 2
4+
fun test() {
5+
scope {
6+
val foo =
7+
runCatching { someFunction() }.getOrNull()
8+
}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// MAX_WIDTH 42
2+
// BLOCK_INDENT 2
3+
// CONTINUATION_INDENT 2
4+
fun test() {
5+
scope {
6+
val foo = runCatching {
7+
someFunction()
8+
}
9+
.getOrNull()
10+
}
11+
}

0 commit comments

Comments
 (0)