From bed5f8feab4a7fdb8c13aeccd9494955336c1c7c Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Tue, 2 Aug 2022 15:13:22 +0200 Subject: [PATCH] Add missing whitespace when else is on same line as true condition `multiline-if-else` Closes #1560 --- CHANGELOG.md | 1 + .../ruleset/standard/MultiLineIfElseRule.kt | 30 ++++++++++++++++--- .../standard/MultiLineIfElseRuleTest.kt | 23 ++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51577e604b..c635e92e6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ The callback function provided as parameter to the format function is now called * When a glob is specified then ensure that it matches files in the current directory and not only in subdirectories of the current directory ([#1533](https://github.com/pinterest/ktlint/issue/1533)). * Execute `ktlint` cli on default kotlin extensions only when an (existing) path to a directory is given. ([#917](https://github.com/pinterest/ktlint/issue/917)). * Invoke callback on `format` function for all errors including errors that are autocorrected ([#1491](https://github.com/pinterest/ktlint/issues/1491)) +* Add missing whitespace when else is on same line as true condition `multiline-if-else` ([#1560](https://github.com/pinterest/ktlint/issues/1560)) ### Changed 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 ac5c93f170..03c50b9fae 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 @@ -3,13 +3,18 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType.ELSE import com.pinterest.ktlint.core.ast.ElementType.ELSE_KEYWORD +import com.pinterest.ktlint.core.ast.ElementType.IF 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.isPartOf import com.pinterest.ktlint.core.ast.isPartOfComment import com.pinterest.ktlint.core.ast.isWhiteSpaceWithoutNewline +import com.pinterest.ktlint.core.ast.nextSibling 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.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement @@ -42,9 +47,17 @@ class MultiLineIfElseRule : Rule("multiline-if-else") { private fun autocorrect(node: ASTNode) { val prevLeaves = - node.leaves(forward = false).takeWhile { it.elementType !in listOf(RPAR, ELSE_KEYWORD) }.toList().reversed() + node + .leaves(forward = false) + .takeWhile { it.elementType !in listOf(RPAR, ELSE_KEYWORD) } + .toList() + .reversed() val nextLeaves = - node.leaves(forward = true).takeWhile { it.isWhiteSpaceWithoutNewline() || it.isPartOfComment() }.toList() + node + .leaves(forward = true) + .takeWhile { it.isWhiteSpaceWithoutNewline() || it.isPartOfComment() } + .toList() + .dropLastWhile { it.isWhiteSpaceWithoutNewline() } val rightBraceIndent = node.treeParent .prevLeaf { it is PsiWhiteSpace && it.textContains('\n') }?.text.orEmpty() .let { "\n${it.substringAfterLast("\n")}" } @@ -62,8 +75,17 @@ class MultiLineIfElseRule : Rule("multiline-if-else") { } // Make sure else starts on same line as newly inserted right brace - if (node.elementType == THEN && node.treeNext?.treeNext?.elementType == ELSE_KEYWORD) { - node.treeParent.replaceChild(node.treeNext, PsiWhiteSpaceImpl(" ")) + 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(" ") + } + } } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt index 2d2e8e0128..6c06d2bae0 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/MultiLineIfElseRuleTest.kt @@ -492,4 +492,27 @@ class MultiLineIfElseRuleTest { .hasLintViolation(6, 13, "Missing { ... }") .isFormattedAs(formattedCode) } + + @Test + fun `Issue 1560 - Given an if statement with else keyword on same line as true branch`() { + val code = + """ + fun foo() = if (bar()) + "a" else + "b" + """.trimIndent() + val formattedCode = + """ + fun foo() = if (bar()) { + "a" + } else { + "b" + } + """.trimIndent() + multiLineIfElseRuleAssertThat(code) + .hasLintViolations( + LintViolation(2, 5, "Missing { ... }"), + LintViolation(3, 5, "Missing { ... }") + ).isFormattedAs(formattedCode) + } }