diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0278587e..9d242160dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). * Fix null pointer exception for if-else statement with empty THEN block `if-else-bracing` [#2135](https://github.com/pinterest/ktlint/issues/2135) * Do not wrap a single line enum class `statement-wrapping` [#2177](https://github.com/pinterest/ktlint/issues/2177) * Fix alignment of type constraints after `where` keyword in function signature `indent` [#2175](https://github.com/pinterest/ktlint/issues/2175) +* Fix wrapping of multiline postfix expression `multiline-expression-wrapping` [#2183](https://github.com/pinterest/ktlint/issues/2183) ### Changed diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt index a6b7928872..74ff9e15e7 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/IndentationRule.kt @@ -581,10 +581,11 @@ public class IndentationRule : node.prevSibling { it.isWhiteSpaceWithNewline() } == null && node == node.treeParent.findChildByType(VALUE_PARAMETER) ) { - nextToAstNode = startIndentContext( - fromAstNode = fromAstNode, - toAstNode = nextToAstNode, - ).fromASTNode.prevLeaf { !it.isWhiteSpace() }!! + nextToAstNode = + startIndentContext( + fromAstNode = fromAstNode, + toAstNode = nextToAstNode, + ).fromASTNode.prevLeaf { !it.isWhiteSpace() }!! } else { startIndentContext( fromAstNode = node, diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt index a0e9491f15..026aba982a 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRule.kt @@ -15,6 +15,7 @@ import com.pinterest.ktlint.rule.engine.core.api.ElementType.IF import com.pinterest.ktlint.rule.engine.core.api.ElementType.IS_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_LITERAL import com.pinterest.ktlint.rule.engine.core.api.ElementType.OPERATION_REFERENCE +import com.pinterest.ktlint.rule.engine.core.api.ElementType.POSTFIX_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.PREFIX_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.REFERENCE_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.RPAR @@ -180,6 +181,7 @@ public class MultilineExpressionWrappingRule : IS_EXPRESSION, OBJECT_LITERAL, PREFIX_EXPRESSION, + POSTFIX_EXPRESSION, REFERENCE_EXPRESSION, SAFE_ACCESS_EXPRESSION, TRY, diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt index 6df3b0cfe7..3b09e61414 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRule.kt @@ -210,23 +210,24 @@ public class WrappingRule : autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { - val rElementType = MATCHING_RTOKEN_MAP[node.elementType] + val closingElementType = MATCHING_RTOKEN_MAP[node.elementType] var newlineInBetween = false var parameterListInBetween = false var numberOfArgs = 0 var firstArg: ASTNode? = null // matching ), ] or } - val r = node.nextSibling { - val isValueArgument = it.elementType == VALUE_ARGUMENT - val hasLineBreak = if (isValueArgument) it.hasLineBreak(LAMBDA_EXPRESSION, FUN) else it.hasLineBreak() - newlineInBetween = newlineInBetween || hasLineBreak - parameterListInBetween = parameterListInBetween || it.elementType == VALUE_PARAMETER_LIST - if (isValueArgument) { - numberOfArgs++ - firstArg = it - } - it.elementType == rElementType - }!! + val closingElement = + node.nextSibling { + val isValueArgument = it.elementType == VALUE_ARGUMENT + val hasLineBreak = if (isValueArgument) it.hasLineBreak(LAMBDA_EXPRESSION, FUN) else it.hasLineBreak() + newlineInBetween = newlineInBetween || hasLineBreak + parameterListInBetween = parameterListInBetween || it.elementType == VALUE_PARAMETER_LIST + if (isValueArgument) { + numberOfArgs++ + firstArg = it + } + it.elementType == closingElementType + }!! if ( !newlineInBetween || // keep { p -> @@ -279,8 +280,8 @@ public class WrappingRule : ) { requireNewlineAfterLeaf(node, autoCorrect, emit) } - if (!r.prevLeaf().isWhiteSpaceWithNewline()) { - requireNewlineBeforeLeaf(r, autoCorrect, emit, indentConfig.parentIndentOf(node)) + if (!closingElement.prevLeaf().isWhiteSpaceWithNewline()) { + requireNewlineBeforeLeaf(closingElement, autoCorrect, emit, indentConfig.parentIndentOf(node)) } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRuleTest.kt index 26218077d2..2f8f3b0567 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/MultilineExpressionWrappingRuleTest.kt @@ -793,4 +793,23 @@ class MultilineExpressionWrappingRuleTest { .addAdditionalRuleProvider { IndentationRule() } .hasNoLintViolations() } + + @Test + fun `Issue 2183 - Given a multiline postfix expression then reformat`() { + val code = + """ + val foobar = foo!! + .bar() + """.trimIndent() + val formattedCode = + """ + val foobar = + foo!! + .bar() + """.trimIndent() + multilineExpressionWrappingRuleAssertThat(code) + .addAdditionalRuleProvider { IndentationRule() } + .hasLintViolation(1, 14, "A multiline expression should start on a new line") + .isFormattedAs(formattedCode) + } }