diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e69a2969e..6ef0db0c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Fixed +* Fix wrapping of nested function literals `wrapping` ([#2106](https://github.com/pinterest/ktlint/issues/2106)) * Do not indent class body for classes having a long super type list in code style `ktlint_official` as it is inconsistent compared to other class bodies `indent` [#2115](https://github.com/pinterest/ktlint/issues/2115) ### Changed 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 732ba49bd0..706001a6ea 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 @@ -158,6 +158,7 @@ public class WrappingRule : ?: return if (lbrace.followedByNewline() || lbrace.followedByEolComment() || + lbrace.followedByFunctionLiteralParameterList() || lbrace.isPartOf(LONG_STRING_TEMPLATE_ENTRY) ) { // String template inside raw string literal may exceed the maximum line length @@ -198,6 +199,12 @@ public class WrappingRule : .takeWhile { it.isWhiteSpaceWithoutNewline() || it.elementType == EOL_COMMENT } .firstOrNull { it.elementType == EOL_COMMENT } + private fun ASTNode.followedByFunctionLiteralParameterList() = + VALUE_PARAMETER_LIST == + takeIf { treeParent.elementType == FUNCTION_LITERAL } + ?.nextCodeSibling() + ?.elementType + private fun rearrangeBlock( node: ASTNode, autoCorrect: Boolean, @@ -425,8 +432,6 @@ public class WrappingRule : } } } - - Unit } } @@ -676,6 +681,7 @@ public class WrappingRule : ?: return node .getEndOfBlock() + ?.takeIf { it.elementType == RBRACE } ?.takeUnless { it.isPrecededByNewline() } ?.let { rbrace -> if (hasNewLineInClosedRange(lbrace, rbrace)) { @@ -695,28 +701,36 @@ public class WrappingRule : private fun ASTNode.isPrecededByNewline() = prevLeaf().isWhiteSpaceWithNewline() private fun ASTNode.getStartOfBlock() = - firstChildLeafOrSelf() - .let { node -> - if (node.elementType == LBRACE) { - // WHEN-entry block have LBRACE and RBRACE as first and last elements - node - } else { - // Other blocks have LBRACE and RBRACE as siblings of the block - node.prevLeaf { !it.isPartOfComment() && !it.isWhiteSpace() } + if (treeParent.elementType == FUNCTION_LITERAL) { + treeParent.findChildByType(LBRACE) + } else { + firstChildLeafOrSelf() + .let { node -> + if (node.elementType == LBRACE) { + // WHEN-entry block have LBRACE and RBRACE as first and last elements + node + } else { + // Other blocks have LBRACE and RBRACE as siblings of the block + node.prevSibling { !it.isPartOfComment() && !it.isWhiteSpace() } + } } - } + } private fun ASTNode.getEndOfBlock() = - lastChildLeafOrSelf() - .let { node -> - if (node.elementType == RBRACE && treeParent.elementType != FUNCTION_LITERAL) { - // WHEN-entry block have LBRACE and RBRACE as first and last elements - node - } else { - // Other blocks have LBRACE and RBRACE as siblings of the block - node.nextLeaf { !it.isPartOfComment() && !it.isWhiteSpace() } + if (treeParent.elementType == FUNCTION_LITERAL) { + treeParent.findChildByType(RBRACE) + } else { + lastChildLeafOrSelf() + .let { node -> + if (node.elementType == RBRACE) { + // WHEN-entry block have LBRACE and RBRACE as first and last elements + node + } else { + // Other blocks have LBRACE and RBRACE as siblings of the block + node.nextSibling { !it.isPartOfComment() && !it.isWhiteSpace() } + } } - } + } private companion object { private val LTOKEN_SET = TokenSet.create(LPAR, LBRACE, LBRACKET, LT) diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRuleTest.kt index 0d047bed59..a9bec73a4a 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/WrappingRuleTest.kt @@ -2629,6 +2629,18 @@ internal class WrappingRuleTest { """.trimIndent() wrappingRuleAssertThat(code).hasNoLintViolations() } + + @Test + fun `Given a nested function literal`() { + val code = + """ + val foo = + bar { + { Foobar() } + } + """.trimIndent() + wrappingRuleAssertThat(code).hasNoLintViolations() + } } // Replace the "$." placeholder with an actual "$" so that string "$.{expression}" is transformed to a String template