From d53cc427b207b978534e59a39b833c72c47cb717 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 31 Oct 2022 19:31:20 +0100 Subject: [PATCH 1/2] Add methods "ASTNode.upsertWhitespaceBeforeMe" and "ASTNode.upsertWhitespaceAfterMe" as replacements for "LeafElement.upsertWhitespaceBeforeMe" and "LeafElement.upsertWhitespaceAfterMe". The new methods are more versatile and allow code to be written more readable in most places. --- .../com/pinterest/ktlint/core/ast/package.kt | 85 +++++++ .../ktlint/core/ast/PackageKtTest.kt | 230 ++++++++++++++++++ .../experimental/CommentWrappingRule.kt | 6 +- .../FunctionReturnTypeSpacingRule.kt | 10 +- .../experimental/FunctionSignatureRule.kt | 53 ++-- .../FunctionStartOfBodySpacingRule.kt | 27 +- .../ruleset/experimental/KdocWrappingRule.kt | 7 +- .../experimental/ParameterListSpacingRule.kt | 2 +- .../TypeParameterListSpacingRule.kt | 6 +- .../FunctionReturnTypeSpacingRuleTest.kt | 4 +- .../experimental/FunctionSignatureRuleTest.kt | 1 + .../FunctionStartOfBodySpacingRuleTest.kt | 12 +- .../ktlint/ruleset/standard/AnnotationRule.kt | 15 +- .../ruleset/standard/ChainWrappingRule.kt | 48 ++-- .../ruleset/standard/CommentSpacingRule.kt | 7 +- .../ruleset/standard/MultiLineIfElseRule.kt | 10 +- .../ruleset/standard/NoSemicolonsRule.kt | 4 +- .../standard/ParameterListWrappingRule.kt | 5 +- .../standard/SpacingAroundColonRule.kt | 10 +- .../standard/SpacingAroundCommaRule.kt | 2 +- .../standard/SpacingAroundCurlyRule.kt | 5 +- .../standard/SpacingAroundKeywordRule.kt | 2 +- .../standard/SpacingAroundOperatorsRule.kt | 2 - .../TrailingCommaOnDeclarationSiteRule.kt | 7 +- .../ktlint/ruleset/standard/WrappingRule.kt | 4 +- .../ruleset/standard/WrappingRuleTest.kt | 3 +- 26 files changed, 411 insertions(+), 156 deletions(-) diff --git a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/ast/package.kt b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/ast/package.kt index 7b54ab2091..42d3f6b974 100644 --- a/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/ast/package.kt +++ b/ktlint-core/src/main/kotlin/com/pinterest/ktlint/core/ast/package.kt @@ -212,6 +212,26 @@ public fun ASTNode.isPartOfComment(): Boolean = public fun ASTNode.children(): Sequence = generateSequence(firstChildNode) { node -> node.treeNext } +@Deprecated(message = """Marked for removal in KtLint 0.49. See KDOC""") +/** + * Marked for removal in KtLint 0.49. + * + * Use [ASTNode.upsertWhitespaceBeforeMe] which operates on the [ASTNode] instead of the [LeafElement]. The new method + * handles more edge case and as of that a lot of code can be simplified. + * + * *Code using [LeafElement.upsertWhitespaceBeforeMe]* + * ``` + * if (elementType == WHITE_SPACE) { + * (this as LeafPsiElement).rawReplaceWithText("\n${blockCommentNode.lineIndent()}") + * } else { + * (this as LeafPsiElement).upsertWhitespaceBeforeMe("\n${blockCommentNode.lineIndent()}") + * } + * ``` + * *Code using [ASTNode.upsertWhitespaceBeforeMe]* + * ``` + * this.upsertWhitespaceBeforeMe(text) + * ``` + */ public fun LeafElement.upsertWhitespaceBeforeMe(text: String): LeafElement { val s = treePrev return if (s != null && s.elementType == WHITE_SPACE) { @@ -223,6 +243,14 @@ public fun LeafElement.upsertWhitespaceBeforeMe(text: String): LeafElement { } } +@Deprecated( + message = + "Marked for removal in KtLint 0.49. The new insertOrReplaceWhitespaceAfterMe is more versatile as it " + + "operates on an AstNode instead of a LeafElement. In a lot of cases the code can be simplified as it is " + + "no longer needed to check whether the current node is already a whitespace or a leaf element before " + + "calling this method or the rawReplaceWithText.", + ReplaceWith("insertOrReplaceWhitespaceBeforeMe"), +) public fun LeafElement.upsertWhitespaceAfterMe(text: String): LeafElement { val s = treeNext return if (s != null && s.elementType == WHITE_SPACE) { @@ -234,6 +262,63 @@ public fun LeafElement.upsertWhitespaceAfterMe(text: String): LeafElement { } } +/** + * Updates or inserts a new whitespace element with [text] before the given node. If the node itself is a whitespace + * then its contents is replaced with [text]. If the node is a (nested) composite element, the whitespace element is + * added after the previous leaf node. + */ +public fun ASTNode.upsertWhitespaceBeforeMe(text: String) { + if (this is LeafElement) { + if (this.elementType == WHITE_SPACE) { + return replaceWhitespaceWith(text) + } + val previous = treePrev ?: prevLeaf() + if (previous != null && previous.elementType == WHITE_SPACE) { + previous.replaceWhitespaceWith(text) + } else { + PsiWhiteSpaceImpl(text).also { psiWhiteSpace -> + (psi as LeafElement).rawInsertBeforeMe(psiWhiteSpace) + } + } + } else { + val prevLeaf = + requireNotNull(prevLeaf()) { + "Can not upsert a whitespace if the first node is a non-leaf node" + } + prevLeaf.upsertWhitespaceAfterMe(text) + } +} + +private fun ASTNode.replaceWhitespaceWith(text: String) { + require(this.elementType == WHITE_SPACE) + if (this.text != text) { + (this.psi as LeafElement).rawReplaceWithText(text) + } +} + +/** + * Updates or inserts a new whitespace element with [text] after the given node. If the node itself is a whitespace + * then its contents is replaced with [text]. If the node is a (nested) composite element, the whitespace element is + * added after the last child leaf. + */ +public fun ASTNode.upsertWhitespaceAfterMe(text: String) { + if (this is LeafElement) { + if (this.elementType == WHITE_SPACE) { + return replaceWhitespaceWith(text) + } + val next = treeNext ?: nextLeaf() + if (next != null && next.elementType == WHITE_SPACE) { + next.replaceWhitespaceWith(text) + } else { + PsiWhiteSpaceImpl(text).also { psiWhiteSpace -> + (psi as LeafElement).rawInsertAfterMe(psiWhiteSpace) + } + } + } else { + lastChildLeafOrSelf().upsertWhitespaceAfterMe(text) + } +} + @Deprecated(message = "Marked for removal in Ktlint 0.48. See Ktlint 0.47.0 changelog for more information.") public fun ASTNode.visit(enter: (node: ASTNode) -> Unit) { enter(this) diff --git a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/ast/PackageKtTest.kt b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/ast/PackageKtTest.kt index c4e8da7b9e..4857d7f15b 100644 --- a/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/ast/PackageKtTest.kt +++ b/ktlint-core/src/test/kotlin/com/pinterest/ktlint/core/ast/PackageKtTest.kt @@ -6,6 +6,12 @@ import com.pinterest.ktlint.core.RuleProvider import com.pinterest.ktlint.core.ast.ElementType.CLASS import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY import com.pinterest.ktlint.core.ast.ElementType.ENUM_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.FUN +import com.pinterest.ktlint.core.ast.ElementType.LPAR +import com.pinterest.ktlint.core.ast.ElementType.RPAR +import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER +import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST +import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.internal.createRuleExecutionContext import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.com.intellij.lang.ASTNode @@ -230,6 +236,230 @@ class PackageKtTest { assertThat(actual).isTrue } + @Nested + inner class UpsertWhitespaceBeforeMe { + @Test + fun `Given a whitespace node and upsert a whitespace before the node (RPAR) then replace the current whitespace element`() { + val code = + """ + fun foo( ) = 42 + """.trimIndent() + val formattedCode = + """ + fun foo( + ) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + findChildByType(FUN) + ?.findChildByType(VALUE_PARAMETER_LIST) + ?.findChildByType(WHITE_SPACE) + ?.upsertWhitespaceBeforeMe("\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (RPAR) which is preceded by a non-whitespace leaf element (LPAR) and upsert a whitespace before the node (RPAR) then create a new whitespace element before the node (RPAR)`() { + val code = + """ + fun foo() = 42 + """.trimIndent() + val formattedCode = + """ + fun foo( + + ) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + nextLeaf { it.elementType == RPAR } + ?.upsertWhitespaceBeforeMe("\n\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (RPAR) which is preceded by a whitespace leaf element and upsert a whitespace before the node (RPAR) then replace the whitespace element before the node (RPAR)`() { + val code = + """ + fun foo( ) = 42 + """.trimIndent() + val formattedCode = + """ + fun foo( + + ) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + nextLeaf { it.elementType == RPAR } + ?.upsertWhitespaceBeforeMe("\n\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (VALUE_PARAMETER) which is preceded by a non-whitespace leaf element (LPAR) and upsert a whitespace before the node (VALUE_PARAMETER) then create a new whitespace element before the node (VALUE_PARAMETER)`() { + val code = + """ + fun foo(string: String) = 42 + """.trimIndent() + val formattedCode = + """ + fun foo( + string: String) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + findChildByType(FUN) + ?.findChildByType(VALUE_PARAMETER_LIST) + ?.findChildByType(VALUE_PARAMETER) + ?.upsertWhitespaceBeforeMe("\n ") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (FUN bar) which is preceded by a composite element (FUN foo) and upsert a whitespace before the node (FUN bar) then create a new whitespace element before the node (FUN bar)`() { + val code = + """ + fun foo() = "foo" + fun bar() = "bar" + """.trimIndent() + val formattedCode = + """ + fun foo() = "foo" + + fun bar() = "bar" + """.trimIndent() + + val actual = + code + .transformAst { + children() + .last { it.elementType == FUN } + .upsertWhitespaceBeforeMe("\n\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + } + + @Nested + inner class UpsertWhitespaceAfterMe { + @Test + fun `Given a node (LPAR) which is followed by a non-whitespace leaf element (RPAR) and upsert a whitespace after the node (LPAR) then create a new whitespace element after the node (LPAR)`() { + val code = + """ + fun foo() = 42 + """.trimIndent() + val formattedCode = + """ + fun foo( + + ) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + nextLeaf { it.elementType == LPAR } + ?.upsertWhitespaceAfterMe("\n\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (LPAR) which is followed by a whitespace leaf element and upsert a whitespace after the node (LPAR) then replace the whitespace element after the node (LPAR)`() { + val code = + """ + fun foo( ) = 42 + """.trimIndent() + val formattedCode = + """ + fun foo( + + ) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + nextLeaf { it.elementType == LPAR } + ?.upsertWhitespaceAfterMe("\n\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (VALUE_PARAMETER) which is followed by a non-whitespace leaf element (RPAR) and upsert a whitespace after the node (VALUE_PARAMETER) then create a new whitespace element after the node (VALUE_PARAMETER)`() { + val code = + """ + fun foo(string: String) = 42 + """.trimIndent() + val formattedCode = + """ + fun foo(string: String + ) = 42 + """.trimIndent() + + val actual = + code + .transformAst { + findChildByType(FUN) + ?.findChildByType(VALUE_PARAMETER_LIST) + ?.findChildByType(VALUE_PARAMETER) + ?.upsertWhitespaceAfterMe("\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + + @Test + fun `Given a node (FUN foo) which is followed by a composite element (FUN bar) and upsert a whitespace after the node (FUN foo) then create a new whitespace element after the node (FUN foo)`() { + val code = + """ + fun foo() = "foo" + fun bar() = "bar" + """.trimIndent() + val formattedCode = + """ + fun foo() = "foo" + + fun bar() = "bar" + """.trimIndent() + + val actual = + code + .transformAst { + children() + .first { it.elementType == FUN } + .upsertWhitespaceAfterMe("\n\n") + }.text + + assertThat(actual).isEqualTo(formattedCode) + } + } + + private inline fun String.transformAst(block: FileASTNode.() -> Unit): FileASTNode = + transformCodeToAST(this) + .apply(block) + private fun transformCodeToAST(code: String) = createRuleExecutionContext( KtLint.ExperimentalParams( diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/CommentWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/CommentWrappingRule.kt index 6eaa816e60..8c8724709e 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/CommentWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/CommentWrappingRule.kt @@ -115,11 +115,7 @@ public class CommentWrappingRule : ) { emit(startOffset, "A block comment may not be followed by any other element on that same line", true) if (autoCorrect) { - if (elementType == WHITE_SPACE) { - (this as LeafPsiElement).rawReplaceWithText("\n${blockCommentNode.lineIndent()}") - } else { - (this as LeafPsiElement).upsertWhitespaceBeforeMe("\n${blockCommentNode.lineIndent()}") - } + this.upsertWhitespaceBeforeMe("\n${blockCommentNode.lineIndent()}") } } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRule.kt index fe6db6b09b..bb0e08567c 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRule.kt @@ -8,7 +8,6 @@ import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement public class FunctionReturnTypeSpacingRule : Rule("$experimentalRulesetId:function-return-type-spacing") { override fun beforeVisitChildNodes( @@ -53,15 +52,10 @@ public class FunctionReturnTypeSpacingRule : Rule("$experimentalRulesetId:functi .nextLeaf() ?.takeIf { it.elementType == WHITE_SPACE } .let { whiteSpaceAfterColon -> - if (whiteSpaceAfterColon == null) { + if (whiteSpaceAfterColon?.text != " ") { emit(node.startOffset, "Single space expected between colon and return type", true) if (autoCorrect) { - (node as LeafElement).upsertWhitespaceAfterMe(" ") - } - } else if (whiteSpaceAfterColon.text != " ") { - emit(node.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - (whiteSpaceAfterColon as LeafElement).rawReplaceWithText(" ") + node.upsertWhitespaceAfterMe(" ") } } } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRule.kt index eddf2a29f3..a7af434e02 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRule.kt @@ -324,13 +324,7 @@ public class FunctionSignatureRule : ) } if (autoCorrect && !dryRun) { - if (whiteSpaceBeforeIdentifier == null) { - (valueParameterList.firstChildNode as LeafElement).upsertWhitespaceAfterMe(expectedParameterIndent) - } else { - (whiteSpaceBeforeIdentifier as LeafElement).rawReplaceWithText( - expectedParameterIndent, - ) - } + valueParameterList.firstChildNode.upsertWhitespaceAfterMe(expectedParameterIndent) } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -394,13 +388,7 @@ public class FunctionSignatureRule : ) } if (autoCorrect && !dryRun) { - if (whiteSpaceBeforeIdentifier == null) { - (firstChildNodeInValueParameter as LeafElement).upsertWhitespaceBeforeMe(expectedParameterIndent) - } else { - (whiteSpaceBeforeIdentifier as LeafElement).rawReplaceWithText( - expectedParameterIndent, - ) - } + firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(expectedParameterIndent) } else { whiteSpaceCorrection += expectedParameterIndent.length - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -415,11 +403,7 @@ public class FunctionSignatureRule : ) } if (autoCorrect && !dryRun) { - if (whiteSpaceBeforeIdentifier == null) { - (firstChildNodeInValueParameter as LeafElement).upsertWhitespaceBeforeMe(" ") - } else { - (whiteSpaceBeforeIdentifier as LeafElement).rawReplaceWithText(" ") - } + firstChildNodeInValueParameter.upsertWhitespaceBeforeMe(" ") } else { whiteSpaceCorrection += 1 - (whiteSpaceBeforeIdentifier?.textLength ?: 0) } @@ -460,7 +444,7 @@ public class FunctionSignatureRule : ) } if (autoCorrect && !dryRun) { - (closingParenthesis as LeafElement).upsertWhitespaceBeforeMe(newlineAndIndent) + closingParenthesis!!.upsertWhitespaceBeforeMe(newlineAndIndent) } else { whiteSpaceCorrection += newlineAndIndent.length - (whiteSpaceBeforeClosingParenthesis?.textLength ?: 0) } @@ -553,11 +537,9 @@ public class FunctionSignatureRule : true, ) if (autoCorrect) { - if (whiteSpaceBeforeFunctionBodyExpression != null) { - (whiteSpaceBeforeFunctionBodyExpression as LeafPsiElement).rawReplaceWithText(" ") - } else { - (functionBodyExpressionNodes.first() as LeafPsiElement).upsertWhitespaceBeforeMe(" ") - } + functionBodyExpressionNodes + .first() + .upsertWhitespaceBeforeMe(" ") } } } else if (firstLineOfBodyExpression.length + 1 > maxLengthRemainingForFirstLineOfBodyExpression || @@ -570,12 +552,9 @@ public class FunctionSignatureRule : true, ) if (autoCorrect) { - val newLineAndIndent = "\n" + node.lineIndent() + indent - if (whiteSpaceBeforeFunctionBodyExpression != null) { - (whiteSpaceBeforeFunctionBodyExpression as LeafPsiElement).rawReplaceWithText(newLineAndIndent) - } else { - (functionBodyExpressionNodes.first() as LeafPsiElement).upsertWhitespaceBeforeMe(newLineAndIndent) - } + functionBodyExpressionNodes + .first() + .upsertWhitespaceBeforeMe("\n" + node.lineIndent() + indent) } } } @@ -614,15 +593,13 @@ public class FunctionSignatureRule : .split("\n") .firstOrNull() ?.also { - if (whiteSpaceBeforeFunctionBodyExpression == null) { + if (whiteSpaceBeforeFunctionBodyExpression?.text != " ") { emit(functionBodyBlock.first().startOffset, "Expected a single space before body block", true) if (autoCorrect) { - (functionBodyBlock.first().prevLeaf(true) as LeafPsiElement).upsertWhitespaceAfterMe(" ") - } - } else if (whiteSpaceBeforeFunctionBodyExpression.text != " ") { - emit(whiteSpaceBeforeFunctionBodyExpression.startOffset, "Expected a single space", true) - if (autoCorrect) { - (whiteSpaceBeforeFunctionBodyExpression as LeafPsiElement).rawReplaceWithText(" ") + functionBodyBlock + .first() + .prevLeaf(true) + ?.upsertWhitespaceAfterMe(" ") } } } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRule.kt index e50c2451a7..f35403c8dc 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRule.kt @@ -8,8 +8,6 @@ import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement /** * Lints and formats the spacing after the fun keyword @@ -55,12 +53,12 @@ public class FunctionStartOfBodySpacingRule : Rule("$experimentalRulesetId:funct if (whiteSpaceBeforeAssignment == null) { emit(assignmentExpression.startOffset, "Expected a single white space before assignment of expression body", true) if (autoCorrect) { - (assignmentExpression as LeafPsiElement).upsertWhitespaceBeforeMe(" ") + assignmentExpression.upsertWhitespaceBeforeMe(" ") } } else if (whiteSpaceBeforeAssignment.text != " ") { emit(whiteSpaceBeforeAssignment.startOffset, "Unexpected whitespace", true) if (autoCorrect) { - (assignmentExpression as LeafPsiElement).upsertWhitespaceBeforeMe(" ") + assignmentExpression.upsertWhitespaceBeforeMe(" ") } } } @@ -79,19 +77,14 @@ public class FunctionStartOfBodySpacingRule : Rule("$experimentalRulesetId:funct .nextLeaf(includeEmpty = true) ?.takeIf { it.elementType == ElementType.WHITE_SPACE } .let { whiteSpaceAfterAssignment -> - if (whiteSpaceAfterAssignment == null) { + if (!(whiteSpaceAfterAssignment?.text == " " || whiteSpaceAfterAssignment?.textContains('\n') == true)) { emit( assignmentExpression.startOffset, "Expected a single white space between assignment and expression body on same line", true, ) if (autoCorrect) { - (assignmentExpression as LeafPsiElement).upsertWhitespaceAfterMe(" ") - } - } else if (whiteSpaceAfterAssignment.text != " " && !whiteSpaceAfterAssignment.textContains('\n')) { - emit(whiteSpaceAfterAssignment.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - (assignmentExpression as LeafPsiElement).upsertWhitespaceAfterMe(" ") + assignmentExpression.upsertWhitespaceAfterMe(" ") } } } @@ -110,15 +103,13 @@ public class FunctionStartOfBodySpacingRule : Rule("$experimentalRulesetId:funct .prevLeaf(includeEmpty = true) ?.takeIf { it.elementType == ElementType.WHITE_SPACE } .let { whiteSpaceBeforeExpressionBlock -> - if (whiteSpaceBeforeExpressionBlock == null) { + if (whiteSpaceBeforeExpressionBlock?.text != " ") { emit(block.startOffset, "Expected a single white space before start of function body", true) if (autoCorrect) { - (block.firstChildNode.prevLeaf(true) as LeafPsiElement).upsertWhitespaceAfterMe(" ") - } - } else if (whiteSpaceBeforeExpressionBlock.text != " ") { - emit(whiteSpaceBeforeExpressionBlock.startOffset, "Unexpected whitespace", true) - if (autoCorrect) { - (whiteSpaceBeforeExpressionBlock as LeafElement).rawReplaceWithText(" ") + block + .firstChildNode + .prevLeaf(true) + ?.upsertWhitespaceAfterMe(" ") } } } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/KdocWrappingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/KdocWrappingRule.kt index 0511106ad0..d28cdac6f9 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/KdocWrappingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/KdocWrappingRule.kt @@ -13,7 +13,6 @@ import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement /** * Checks external wrapping of KDoc comment. Wrapping inside the KDoc comment is not altered. @@ -91,11 +90,7 @@ public class KdocWrappingRule : ) { emit(startOffset, "A KDoc comment may not be followed by any other element on that same line", true) if (autoCorrect) { - if (elementType == WHITE_SPACE) { - (this as LeafPsiElement).rawReplaceWithText("\n${kdocCommentNode.lineIndent()}") - } else { - (this as LeafPsiElement).upsertWhitespaceBeforeMe("\n${kdocCommentNode.lineIndent()}") - } + this.upsertWhitespaceBeforeMe("\n${kdocCommentNode.lineIndent()}") } } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ParameterListSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ParameterListSpacingRule.kt index f6427467ab..9dcee1b36d 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ParameterListSpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/ParameterListSpacingRule.kt @@ -204,7 +204,7 @@ public class ParameterListSpacingRule : Rule("$experimentalRulesetId:parameter-l require(node.elementType == COLON || node.elementType == COMMA) emit(node.startOffset, "Whitespace after '${node.text}' is missing", true) if (autoCorrect) { - (node as LeafElement).upsertWhitespaceAfterMe(" ") + node.upsertWhitespaceAfterMe(" ") } } diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/TypeParameterListSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/TypeParameterListSpacingRule.kt index f9a22b2f3c..2d49c60bea 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/TypeParameterListSpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/TypeParameterListSpacingRule.kt @@ -187,11 +187,7 @@ public class TypeParameterListSpacingRule : Rule("$experimentalRulesetId:type-pa true, ) if (autoCorrect) { - if (node.elementType == WHITE_SPACE) { - (node as LeafPsiElement).rawReplaceWithText(" ") - } else { - (node as LeafPsiElement).upsertWhitespaceBeforeMe(" ") - } + node.upsertWhitespaceBeforeMe(" ") } } } diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRuleTest.kt index 14b36e8834..9451df92dd 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionReturnTypeSpacingRuleTest.kt @@ -72,7 +72,7 @@ class FunctionReturnTypeSpacingRuleTest { fun foo(): String = "some-result" """.trimIndent() functionReturnTypeSpacingRuleAssertThat(code) - .hasLintViolation(1, 10, "Unexpected whitespace") + .hasLintViolation(1, 10, "Single space expected between colon and return type") .isFormattedAs(formattedCode) } @@ -88,7 +88,7 @@ class FunctionReturnTypeSpacingRuleTest { fun foo(): String = "some-result" """.trimIndent() functionReturnTypeSpacingRuleAssertThat(code) - .hasLintViolation(1, 10, "Unexpected whitespace") + .hasLintViolation(1, 10, "Single space expected between colon and return type") .isFormattedAs(formattedCode) } } diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRuleTest.kt index faf7f1facf..8f00f8bc0a 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionSignatureRuleTest.kt @@ -10,6 +10,7 @@ import com.pinterest.ktlint.ruleset.standard.SpacingAroundCommaRule import com.pinterest.ktlint.ruleset.standard.SpacingAroundDotRule import com.pinterest.ktlint.ruleset.standard.SpacingAroundOperatorsRule import com.pinterest.ktlint.ruleset.standard.SpacingAroundParensRule +import com.pinterest.ktlint.test.KtLintAssertThat.Companion.EOL_CHAR import com.pinterest.ktlint.test.KtLintAssertThat.Companion.MAX_LINE_LENGTH_MARKER import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule import com.pinterest.ktlint.test.LintViolation diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRuleTest.kt index b411ecf44e..caab9119bc 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionStartOfBodySpacingRuleTest.kt @@ -121,8 +121,8 @@ class FunctionStartOfBodySpacingRuleTest { """.trimIndent() functionStartOfBodySpacingRuleAssertThat(code) .hasLintViolations( - LintViolation(1, 12, "Unexpected whitespace"), - LintViolation(2, 20, "Unexpected whitespace"), + LintViolation(1, 11, "Expected a single white space between assignment and expression body on same line"), + LintViolation(2, 19, "Expected a single white space between assignment and expression body on same line"), ).isFormattedAs(formattedCode) } @@ -206,8 +206,8 @@ class FunctionStartOfBodySpacingRuleTest { """.trimIndent() functionStartOfBodySpacingRuleAssertThat(code) .hasLintViolations( - LintViolation(1, 10, "Unexpected whitespace"), - LintViolation(4, 18, "Unexpected whitespace"), + LintViolation(1, 12, "Expected a single white space before start of function body"), + LintViolation(4, 20, "Expected a single white space before start of function body"), ).isFormattedAs(formattedCode) } @@ -235,8 +235,8 @@ class FunctionStartOfBodySpacingRuleTest { """.trimIndent() functionStartOfBodySpacingRuleAssertThat(code) .hasLintViolations( - LintViolation(1, 10, "Unexpected whitespace"), - LintViolation(5, 18, "Unexpected whitespace"), + LintViolation(2, 1, "Expected a single white space before start of function body"), + LintViolation(6, 1, "Expected a single white space before start of function body"), ).isFormattedAs(formattedCode) } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationRule.kt index b2ebb72eec..fb9b4655e2 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/AnnotationRule.kt @@ -99,8 +99,7 @@ public class AnnotationRule : Rule("annotation") { if (autoCorrect) { node .firstChildLeafOrSelf() - .safeAs() - ?.upsertWhitespaceBeforeMe(" ") + .upsertWhitespaceBeforeMe(" ") } } @@ -152,7 +151,6 @@ public class AnnotationRule : Rule("annotation") { node .lastChildLeafOrSelf() .nextCodeLeaf() - .safeAs() ?.upsertWhitespaceBeforeMe(getNewlineWithIndent(node.treeParent)) } } @@ -182,16 +180,7 @@ public class AnnotationRule : Rule("annotation") { node .lastChildLeafOrSelf() .nextLeaf() - .safeAs() - ?.let { - if (it.elementType == WHITE_SPACE) { - it.replaceWithText(getNewlineWithIndent(node.treeParent)) - } else { - it.upsertWhitespaceBeforeMe( - getNewlineWithIndent(node.treeParent), - ) - } - } + ?.upsertWhitespaceBeforeMe(getNewlineWithIndent(node.treeParent)) } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ChainWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ChainWrappingRule.kt index e811e24559..e2e4461ff5 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ChainWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ChainWrappingRule.kt @@ -1,6 +1,10 @@ package com.pinterest.ktlint.ruleset.standard +import com.pinterest.ktlint.core.IndentConfig import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.api.DefaultEditorConfigProperties +import com.pinterest.ktlint.core.api.EditorConfigProperties +import com.pinterest.ktlint.core.api.UsesEditorConfigProperties import com.pinterest.ktlint.core.ast.ElementType.ANDAND import com.pinterest.ktlint.core.ast.ElementType.COMMA import com.pinterest.ktlint.core.ast.ElementType.DIV @@ -20,11 +24,13 @@ import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isWhiteSpaceWithNewline import com.pinterest.ktlint.core.ast.isWhiteSpaceWithoutNewline +import com.pinterest.ktlint.core.ast.lineIndent import com.pinterest.ktlint.core.ast.nextCodeLeaf import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.prevCodeLeaf import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe +import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement @@ -33,13 +39,31 @@ import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.psiUtil.leaves -public class ChainWrappingRule : Rule("chain-wrapping") { +public class ChainWrappingRule : + Rule("chain-wrapping"), + UsesEditorConfigProperties { + override val editorConfigProperties: List> = + listOf( + DefaultEditorConfigProperties.indentSizeProperty, + DefaultEditorConfigProperties.indentStyleProperty, + ) + private var indent: String? = null private val sameLineTokens = TokenSet.create(MUL, DIV, PERC, ANDAND, OROR) private val prefixTokens = TokenSet.create(PLUS, MINUS) private val nextLineTokens = TokenSet.create(DOT, SAFE_ACCESS, ELVIS) private val noSpaceAroundTokens = TokenSet.create(DOT, SAFE_ACCESS) + override fun beforeFirstNode(editorConfigProperties: EditorConfigProperties) { + with(editorConfigProperties) { + val indentConfig = IndentConfig( + indentStyle = getEditorConfigValue(com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.indentStyleProperty), + tabWidth = getEditorConfigValue(com.pinterest.ktlint.core.api.DefaultEditorConfigProperties.indentSizeProperty), + ) + indent = indentConfig.indent + } + } + override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, @@ -65,15 +89,13 @@ public class ChainWrappingRule : Rule("chain-wrapping") { // (or) // to // - val prevLeaf = node.prevLeaf() - if (prevLeaf is PsiWhiteSpace) { - prevLeaf.node.treeParent.removeChild(prevLeaf.node) + if (node.elementType == ELVIS) { + node.upsertWhitespaceBeforeMe("\n" + node.lineIndent() + indent) + node.upsertWhitespaceAfterMe(" ") + } else { + node.treeParent.removeChild(node) + (nextLeaf as LeafElement).rawInsertAfterMe(node as LeafElement) } - if (!noSpaceAroundTokens.contains(elementType)) { - (nextLeaf as LeafElement).upsertWhitespaceAfterMe(" ") - } - node.treeParent.removeChild(node) - (nextLeaf as LeafElement).rawInsertAfterMe(node as LeafElement) } } } else if (sameLineTokens.contains(elementType) || prefixTokens.contains(elementType)) { @@ -102,11 +124,9 @@ public class ChainWrappingRule : Rule("chain-wrapping") { nextLeaf.node.treeParent.removeChild(nextLeaf.node) } val insertionPoint = prevLeaf.prevCodeLeaf() as LeafPsiElement - node.treeParent.removeChild(node) - insertionPoint.rawInsertAfterMe(node as LeafPsiElement) - if (!noSpaceAroundTokens.contains(elementType)) { - insertionPoint.upsertWhitespaceAfterMe(" ") - } + (node as LeafPsiElement).treeParent.removeChild(node) + insertionPoint.rawInsertAfterMe(node) + (insertionPoint as ASTNode).upsertWhitespaceAfterMe(" ") } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/CommentSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/CommentSpacingRule.kt index 7cbf9cd07a..d2dd0c4c09 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/CommentSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/CommentSpacingRule.kt @@ -1,21 +1,20 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.Rule +import com.pinterest.ktlint.core.ast.ElementType.EOL_COMMENT import com.pinterest.ktlint.core.ast.prevLeaf import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode -import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement public class CommentSpacingRule : Rule("comment-spacing") { - override fun beforeVisitChildNodes( node: ASTNode, autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { - if (node is PsiComment && node is LeafPsiElement && node.getText().startsWith("//")) { + if (node.elementType == EOL_COMMENT) { val prevLeaf = node.prevLeaf() if (prevLeaf !is PsiWhiteSpace && prevLeaf is LeafPsiElement) { emit(node.startOffset, "Missing space before //", true) @@ -33,7 +32,7 @@ public class CommentSpacingRule : Rule("comment-spacing") { ) { emit(node.startOffset, "Missing space after //", true) if (autoCorrect) { - node.rawReplaceWithText("// " + text.removePrefix("//")) + (node as LeafPsiElement).rawReplaceWithText("// " + text.removePrefix("//")) } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt index f09eea7bf7..d787e4668e 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRule.kt @@ -13,7 +13,6 @@ import com.pinterest.ktlint.core.ast.ElementType.LBRACE import com.pinterest.ktlint.core.ast.ElementType.RBRACE import com.pinterest.ktlint.core.ast.ElementType.RPAR import com.pinterest.ktlint.core.ast.ElementType.THEN -import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isWhiteSpace import com.pinterest.ktlint.core.ast.isWhiteSpaceWithoutNewline @@ -115,14 +114,7 @@ public class MultiLineIfElseRule : if (node.elementType == THEN) { node .nextSibling { !it.isPartOfComment() } - ?.let { nextSibling -> - if (nextSibling.elementType == ELSE_KEYWORD) { - (nextSibling as LeafPsiElement).upsertWhitespaceBeforeMe(" ") - } - if (nextSibling.elementType == WHITE_SPACE && nextSibling.text != " ") { - (nextSibling as LeafPsiElement).rawReplaceWithText(" ") - } - } + ?.upsertWhitespaceBeforeMe(" ") } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoSemicolonsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoSemicolonsRule.kt index 786f686686..f23932b7b9 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoSemicolonsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/NoSemicolonsRule.kt @@ -15,7 +15,6 @@ import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiComment import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.kdoc.psi.api.KDoc import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtAnnotationEntry @@ -37,8 +36,7 @@ public class NoSemicolonsRule : Rule("no-semi") { if (node.elementType == KDOC_TEXT) { return } - if (node is LeafPsiElement && - node.elementType == SEMICOLON && + if (node.elementType == SEMICOLON && !node.isPartOfString() && !node.isPartOfEnumEntry() ) { diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt index 3e42f708a3..6f242dcc99 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/ParameterListWrappingRule.kt @@ -25,7 +25,6 @@ import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.psi.KtTypeArgumentList @@ -97,13 +96,13 @@ public class ParameterListWrappingRule : true, ) if (autoCorrect) { - (it as LeafElement).upsertWhitespaceAfterMe("\n${indentConfig.indent}") + it.upsertWhitespaceAfterMe("\n${indentConfig.indent}") } } RPAR -> { emit(it.startOffset, errorMessage(it), true) if (autoCorrect) { - (it as LeafElement).upsertWhitespaceBeforeMe("\n") + it.upsertWhitespaceBeforeMe("\n") } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt index fc55594b5c..34324ee985 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundColonRule.kt @@ -83,7 +83,7 @@ public class SpacingAroundColonRule : Rule("colon-spacing") { } else { (prevLeaf as LeafPsiElement).rawReplaceWithText(" ") } - node.upsertWhitespaceAfterMe(text) + (node as ASTNode).upsertWhitespaceAfterMe(text) } } } @@ -105,20 +105,20 @@ public class SpacingAroundColonRule : Rule("colon-spacing") { missingSpacingBefore && missingSpacingAfter -> { emit(node.startOffset, "Missing spacing around \":\"", true) if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") - node.upsertWhitespaceAfterMe(" ") + (node as ASTNode).upsertWhitespaceBeforeMe(" ") + (node as ASTNode).upsertWhitespaceAfterMe(" ") } } missingSpacingBefore -> { emit(node.startOffset, "Missing spacing before \":\"", true) if (autoCorrect) { - node.upsertWhitespaceBeforeMe(" ") + (node as ASTNode).upsertWhitespaceBeforeMe(" ") } } missingSpacingAfter -> { emit(node.startOffset + 1, "Missing spacing after \":\"", true) if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") + (node as ASTNode).upsertWhitespaceAfterMe(" ") } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCommaRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCommaRule.kt index a720f9fc05..733a8caa9f 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCommaRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCommaRule.kt @@ -51,7 +51,7 @@ public class SpacingAroundCommaRule : Rule("comma-spacing") { if (nextLeaf !is PsiWhiteSpace && nextLeaf?.elementType !in rTokenSet) { emit(node.startOffset + 1, "Missing spacing after \"${node.text}\"", true) if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") + (node as ASTNode).upsertWhitespaceAfterMe(" ") } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt index 6597219091..2868aec4e8 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundCurlyRule.kt @@ -18,6 +18,7 @@ import com.pinterest.ktlint.core.ast.ElementType.RBRACKET import com.pinterest.ktlint.core.ast.ElementType.RPAR import com.pinterest.ktlint.core.ast.ElementType.SAFE_ACCESS import com.pinterest.ktlint.core.ast.ElementType.SEMICOLON +import com.pinterest.ktlint.core.ast.isLeaf import com.pinterest.ktlint.core.ast.isPartOfString import com.pinterest.ktlint.core.ast.nextLeaf import com.pinterest.ktlint.core.ast.prevLeaf @@ -38,7 +39,7 @@ public class SpacingAroundCurlyRule : Rule("curly-spacing") { autoCorrect: Boolean, emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { - if (node is LeafPsiElement && !node.isPartOfString()) { + if (node.isLeaf() && !node.isPartOfString()) { val prevLeaf = node.prevLeaf() val nextLeaf = node.nextLeaf() val spacingBefore: Boolean @@ -49,7 +50,7 @@ public class SpacingAroundCurlyRule : Rule("curly-spacing") { prevLeaf?.elementType == AT || ( prevLeaf?.elementType == LPAR && - (node.parent is KtLambdaExpression || node.parent.parent is KtLambdaExpression) + ((node as LeafPsiElement).parent is KtLambdaExpression || node.parent.parent is KtLambdaExpression) ) spacingAfter = nextLeaf is PsiWhiteSpace || nextLeaf?.elementType == RBRACE if (prevLeaf is PsiWhiteSpace && diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt index 44c3c5e1a5..f831805bd6 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundKeywordRule.kt @@ -46,7 +46,7 @@ public class SpacingAroundKeywordRule : Rule("keyword-spacing") { if (tokenSet.contains(node.elementType) && node.parent !is KDocName && node.nextLeaf() !is PsiWhiteSpace) { emit(node.startOffset + node.text.length, "Missing spacing after \"${node.text}\"", true) if (autoCorrect) { - node.upsertWhitespaceAfterMe(" ") + (node as ASTNode).upsertWhitespaceAfterMe(" ") } } else if (keywordsWithoutSpaces.contains(node.elementType) && node.nextLeaf() is PsiWhiteSpace) { val parent = node.parent diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundOperatorsRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundOperatorsRule.kt index c408ca7a11..5b946c84e1 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundOperatorsRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/SpacingAroundOperatorsRule.kt @@ -33,7 +33,6 @@ import com.pinterest.ktlint.core.ast.upsertWhitespaceAfterMe import com.pinterest.ktlint.core.ast.upsertWhitespaceBeforeMe import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.KtPrefixExpression @@ -51,7 +50,6 @@ public class SpacingAroundOperatorsRule : Rule("op-spacing") { emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { if (tokenSet.contains(node.elementType) && - node is LeafElement && !node.isPartOf(KtPrefixExpression::class) && // not unary !(node.elementType == MUL && node.treeParent.elementType == VALUE_ARGUMENT) && // fn(*array) !node.isPartOf(KtImportDirective::class) // import * diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaOnDeclarationSiteRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaOnDeclarationSiteRule.kt index ea5233fb61..2d45f95e4c 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaOnDeclarationSiteRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/TrailingCommaOnDeclarationSiteRule.kt @@ -26,7 +26,6 @@ import org.ec4j.core.model.PropertyType.PropertyValueParser import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace -import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression @@ -267,11 +266,7 @@ public class TrailingCommaOnDeclarationSiteRule : ) if (autoCorrect) { val parentIndent = "\n" + inspectNode.getWhenEntryIndent() - if (lastNodeBeforeArrow.elementType == WHITE_SPACE) { - (lastNodeBeforeArrow as LeafPsiElement).rawReplaceWithText(parentIndent) - } else { - (lastNodeBeforeArrow as LeafPsiElement).upsertWhitespaceAfterMe(parentIndent) - } + lastNodeBeforeArrow.upsertWhitespaceAfterMe(parentIndent) } } } diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRule.kt index 3ad75a0481..89a2fdc45b 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRule.kt @@ -471,7 +471,7 @@ public class WrappingRule : ) logger.trace { "$line: " + ((if (!autoCorrect) "would have " else "") + "inserted newline before ${node.text}") } if (autoCorrect) { - (node.psi as LeafPsiElement).upsertWhitespaceBeforeMe("\n" + indent) + node.upsertWhitespaceBeforeMe("\n" + indent) } } @@ -490,7 +490,7 @@ public class WrappingRule : logger.trace { "$line: " + (if (!autoCorrect) "would have " else "") + "inserted newline after ${nodeAfterWhichNewlineIsRequired.text}" } if (autoCorrect) { val tempIndent = indent ?: (nodeToFix.lineIndent() + indentConfig.indent) - (nodeToFix.psi as LeafPsiElement).upsertWhitespaceAfterMe("\n" + tempIndent) + nodeToFix.upsertWhitespaceAfterMe("\n" + tempIndent) } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRuleTest.kt index f6aa3407cc..7a3fce420b 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/WrappingRuleTest.kt @@ -1139,7 +1139,6 @@ internal class WrappingRuleTest { @Test fun `format kdoc with spaces`() { - @Suppress("RemoveCurlyBracesFromTemplate") val code = """ /** @@ -1179,7 +1178,7 @@ internal class WrappingRuleTest { ${TAB}} } """.trimIndent() - wrappingRuleAssertThat(code) + wrappingRuleAssertThat(codeTabs) .withEditorConfigOverride(DefaultEditorConfigProperties.indentStyleProperty to tab) .hasNoLintViolations() } From 434890d72d1c29befe1507a3612b645625644d5f Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Mon, 31 Oct 2022 19:34:13 +0100 Subject: [PATCH 2/2] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26df1c6793..a54ecf115e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ if (node.isRoot()) { * CLI options `--debug`, `--trace`, `--verbose` and `-v` are replaced with `--log-level=` or the short version `-l=, see [CLI log-level](https://pinterest.github.io/ktlint/install/cli/#logging). ([#1632](https://github.com/pinterest/ktlint/issue/1632)) * In CLI, disable logging entirely by setting `--log-level=none` or `-l=none` ([#1652](https://github.com/pinterest/ktlint/issue/1652)) * Rewrite `indent` rule. Solving problems in the old algorithm was very difficult. With the new algorithm this becomes a lot easier. Although the new implementation of the rule has been compared against several open source projects containing over 400,000 lines of code, it is still likely that new issues will be discovered. Please report your indentation issues so that these can be fixed as well. ([#1682](https://github.com/pinterest/ktlint/pull/1682), [#1321](https://github.com/pinterest/ktlint/issues/1321), [#1200](https://github.com/pinterest/ktlint/issues/1200), [#1562](https://github.com/pinterest/ktlint/issues/1562), [#1563](https://github.com/pinterest/ktlint/issues/1563), [#1639](https://github.com/pinterest/ktlint/issues/1639)) +* Add methods "ASTNode.upsertWhitespaceBeforeMe" and "ASTNode.upsertWhitespaceAfterMe" as replacements for "LeafElement.upsertWhitespaceBeforeMe" and "LeafElement.upsertWhitespaceAfterMe". The new methods are more versatile and allow code to be written more readable in most places. ([#1687](https://github.com/pinterest/ktlint/pull/1687)) ## [0.47.1] - 2022-09-07