From b60e5d4506586857bedbb94ec9fd653213880c04 Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Fri, 22 Aug 2025 14:38:52 +0200 Subject: [PATCH] [kotlin] Migrate Add/Remove BracesIntention to ModCommand #KTIJ-35337 GitOrigin-RevId: 850cb03915b972e739afb2893460af76cb26592d --- .../KotlinPsiUpdateModCommandAction.kt | 3 +- .../intentions/shared/AddBracesIntention.kt | 55 +++++++++---- .../shared/AddBracesToAllBranchesIntention.kt | 45 +++++++---- .../RemoveBracesFromAllBranchesIntention.kt | 48 ++++++++---- .../shared/RemoveBracesIntention.kt | 78 ++++++++++++------- .../intentions/addBraces/elseWithComment.kt | 7 +- .../addBraces/elseWithComment.kt.after | 7 +- .../idea/codeinsight/utils/AddBracesUtils.kt | 35 ++++----- .../intentions/AbstractIntentionTestBase.kt | 5 +- 9 files changed, 178 insertions(+), 105 deletions(-) diff --git a/plugins/kotlin/code-insight/api/src/org/jetbrains/kotlin/idea/codeinsight/api/applicable/intentions/KotlinPsiUpdateModCommandAction.kt b/plugins/kotlin/code-insight/api/src/org/jetbrains/kotlin/idea/codeinsight/api/applicable/intentions/KotlinPsiUpdateModCommandAction.kt index b18801686992..9c7f607ed492 100644 --- a/plugins/kotlin/code-insight/api/src/org/jetbrains/kotlin/idea/codeinsight/api/applicable/intentions/KotlinPsiUpdateModCommandAction.kt +++ b/plugins/kotlin/code-insight/api/src/org/jetbrains/kotlin/idea/codeinsight/api/applicable/intentions/KotlinPsiUpdateModCommandAction.kt @@ -35,7 +35,8 @@ sealed class KotlinPsiUpdateModCommandAction( context: ActionContext, element: E, ): ModCommand = try { - ModCommand.psiUpdate(element) { e, updater -> + ModCommand.psiUpdate(context) { updater -> + val e = updater.getWritable(element) val elementContext = getElementContext(context, e) ?: throw NoContextException() invoke(context, e, elementContext, updater) diff --git a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesIntention.kt b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesIntention.kt index e8b903517144..c9ad216e52d7 100644 --- a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesIntention.kt +++ b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesIntention.kt @@ -2,11 +2,16 @@ package org.jetbrains.kotlin.idea.codeInsight.intentions.shared -import com.intellij.openapi.editor.Editor +import com.intellij.codeInspection.util.IntentionFamilyName +import com.intellij.modcommand.ActionContext +import com.intellij.modcommand.ModPsiUpdater +import com.intellij.modcommand.Presentation import com.intellij.openapi.util.IntellijInternalApi +import com.intellij.psi.PsiElement import org.jetbrains.annotations.ApiStatus.Internal +import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.idea.base.resources.KotlinBundle -import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention +import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinPsiUpdateModCommandAction import org.jetbrains.kotlin.idea.codeinsight.utils.AddBracesUtils import org.jetbrains.kotlin.idea.codeinsight.utils.getControlFlowElementDescription import org.jetbrains.kotlin.psi.* @@ -14,30 +19,46 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset @Internal @IntellijInternalApi -class AddBracesIntention : SelfTargetingIntention(KtElement::class.java, KotlinBundle.lazyMessage("add.braces")) { - override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean { - val expression = element.getTargetExpression(caretOffset) ?: return false - if (expression is KtBlockExpression) return false +class AddBracesIntention : KotlinPsiUpdateModCommandAction.ClassBased(KtElement::class) { + override fun getFamilyName(): @IntentionFamilyName String = + KotlinBundle.message("add.braces") + + override fun isElementApplicable(element: KtElement, context: ActionContext): Boolean { + val expression = element.getTargetExpression(context.offset) ?: return false + // it stops further element look-up at `com.intellij.modcommand.PsiBasedModCommandAction.getElement` + if (expression is KtBlockExpression) return true + + return when (val parent = expression.parent) { + is KtContainerNode -> parent.getControlFlowElementDescription() != null + is KtWhenEntry -> true + else -> false + } + } + + override fun stopSearchAt(element: PsiElement, context: ActionContext): Boolean { + return super.stopSearchAt(element, context) + } + + override fun getPresentation(context: ActionContext, element: KtElement): Presentation? { + val expression = element.getTargetExpression(context.offset) ?: return null + if (expression is KtBlockExpression) return null return when (val parent = expression.parent) { is KtContainerNode -> { - val description = parent.getControlFlowElementDescription() ?: return false - setTextGetter(KotlinBundle.lazyMessage("add.braces.to.0.statement", description)) - true + val description = parent.getControlFlowElementDescription() ?: return null + Presentation.of(KotlinBundle.message("add.braces.to.0.statement", description)) } is KtWhenEntry -> { - setTextGetter(KotlinBundle.lazyMessage("add.braces.to.when.entry")) - true + Presentation.of(KotlinBundle.message("add.braces.to.when.entry")) } else -> { - false + null } } } - override fun applyTo(element: KtElement, editor: Editor?) { - if (editor == null) throw IllegalArgumentException("This intention requires an editor") - val expression = element.getTargetExpression(editor.caretModel.offset) ?: return + override fun invoke(actionContext: ActionContext, element: KtElement, elementContext: Unit, updater: ModPsiUpdater) { + val expression = element.getTargetExpression(updater.caretOffset) ?: return AddBracesUtils.addBraces(element, expression) } @@ -46,7 +67,7 @@ class AddBracesIntention : SelfTargetingIntention(KtElement::class.ja is KtIfExpression -> { val thenExpr = then ?: return null val elseExpr = `else` - if (elseExpr != null && caretLocation >= (elseKeyword?.startOffset ?: return null)) { + if (elseExpr != null && (caretLocation >= (elseKeyword?.startOffset ?: return null))) { elseExpr } else { thenExpr @@ -58,4 +79,6 @@ class AddBracesIntention : SelfTargetingIntention(KtElement::class.ja else -> null } } + + override fun KaSession.prepareContext(element: KtElement) {} } diff --git a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesToAllBranchesIntention.kt b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesToAllBranchesIntention.kt index 16119b9e50d3..9742f1b5f43c 100644 --- a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesToAllBranchesIntention.kt +++ b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/AddBracesToAllBranchesIntention.kt @@ -2,14 +2,18 @@ package org.jetbrains.kotlin.idea.codeInsight.intentions.shared -import com.intellij.openapi.editor.Editor +import com.intellij.codeInspection.util.IntentionFamilyName +import com.intellij.modcommand.ActionContext +import com.intellij.modcommand.ModPsiUpdater +import com.intellij.modcommand.Presentation import com.intellij.psi.PsiElement import com.intellij.psi.util.elementType import com.intellij.psi.util.endOffset import com.intellij.util.containers.addIfNotNull +import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.codeInsight.intentions.shared.AddBracesToAllBranchesIntention.Util.allBranchExpressions -import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention +import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinPsiUpdateModCommandAction import org.jetbrains.kotlin.idea.codeinsight.utils.AddBracesUtils import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* @@ -19,25 +23,35 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.util.match -internal class AddBracesToAllBranchesIntention : SelfTargetingIntention( - KtExpression::class.java, - KotlinBundle.lazyMessage("add.braces.to.all.branches") -) { - override fun isApplicableTo(element: KtExpression, caretOffset: Int): Boolean { +internal class AddBracesToAllBranchesIntention : KotlinPsiUpdateModCommandAction.ClassBased(KtExpression::class) { + override fun getFamilyName(): @IntentionFamilyName String = + KotlinBundle.message("add.braces.to.all.branches") + + override fun isElementApplicable(element: KtExpression, context: ActionContext): Boolean { val targetIfOrWhenExpression = Util.targetIfOrWhenExpression(element) ?: return false val targetBranchExpressions = targetIfOrWhenExpression.targetBranchExpressions() if (targetBranchExpressions.isEmpty()) return false - if (caretIsOnSingleTargetBranch(targetIfOrWhenExpression, targetBranchExpressions, caretOffset)) return false + if (caretIsOnSingleTargetBranch(targetIfOrWhenExpression, targetBranchExpressions, context.offset)) return false - when (targetIfOrWhenExpression) { - is KtIfExpression -> setTextGetter(KotlinBundle.lazyMessage("add.braces.to.if.all.statements")) - is KtWhenExpression -> setTextGetter(KotlinBundle.lazyMessage("add.braces.to.when.all.entries")) - } - return true + return targetIfOrWhenExpression is KtIfExpression || targetIfOrWhenExpression is KtWhenExpression } - override fun applyTo(element: KtExpression, editor: Editor?) { + override fun getPresentation(context: ActionContext, element: KtExpression): Presentation? { + val targetIfOrWhenExpression = Util.targetIfOrWhenExpression(element) ?: return null + + val targetBranchExpressions = targetIfOrWhenExpression.targetBranchExpressions() + if (targetBranchExpressions.isEmpty()) return null + if (caretIsOnSingleTargetBranch(targetIfOrWhenExpression, targetBranchExpressions, context.offset)) return null + + return when (targetIfOrWhenExpression) { + is KtIfExpression -> Presentation.of(KotlinBundle.message("add.braces.to.if.all.statements")) + is KtWhenExpression -> Presentation.of(KotlinBundle.message("add.braces.to.when.all.entries")) + else -> null + } + } + + override fun invoke(actionContext: ActionContext, element: KtExpression, elementContext: Unit, updater: ModPsiUpdater) { val targetIfOrWhenExpression = Util.targetIfOrWhenExpression(element) ?: return val branches = targetIfOrWhenExpression.targetBranchExpressions() for (branch in branches) { @@ -105,4 +119,7 @@ internal class AddBracesToAllBranchesIntention : SelfTargetingIntention emptyList() } } + + override fun KaSession.prepareContext(element: KtExpression) { + } } diff --git a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/RemoveBracesFromAllBranchesIntention.kt b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/RemoveBracesFromAllBranchesIntention.kt index 20cd95c15333..99a9d0e722e2 100644 --- a/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/RemoveBracesFromAllBranchesIntention.kt +++ b/plugins/kotlin/code-insight/intentions-shared/src/org/jetbrains/kotlin/idea/codeInsight/intentions/shared/RemoveBracesFromAllBranchesIntention.kt @@ -2,37 +2,51 @@ package org.jetbrains.kotlin.idea.codeInsight.intentions.shared -import com.intellij.openapi.editor.Editor +import com.intellij.codeInspection.util.IntentionFamilyName +import com.intellij.modcommand.ActionContext +import com.intellij.modcommand.ModPsiUpdater +import com.intellij.modcommand.Presentation +import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.codeInsight.intentions.shared.AddBracesToAllBranchesIntention.Util.allBranchExpressions import org.jetbrains.kotlin.idea.codeInsight.intentions.shared.AddBracesToAllBranchesIntention.Util.targetIfOrWhenExpression -import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention +import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinPsiUpdateModCommandAction import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset -internal class RemoveBracesFromAllBranchesIntention : SelfTargetingIntention( - KtExpression::class.java, - KotlinBundle.lazyMessage("remove.braces.from.all.branches") -) { - override fun isApplicableTo(element: KtExpression, caretOffset: Int): Boolean { +internal class RemoveBracesFromAllBranchesIntention : KotlinPsiUpdateModCommandAction.ClassBased(KtExpression::class) { + override fun getFamilyName(): @IntentionFamilyName String = + KotlinBundle.message("remove.braces.from.all.branches") + + override fun isElementApplicable(element: KtExpression, context: ActionContext): Boolean { val targetIfOrWhenExpression = targetIfOrWhenExpression(element) ?: return false val targetBranchExpressions = targetIfOrWhenExpression.targetBranchExpressions() if (targetBranchExpressions.isEmpty() || targetBranchExpressions.any { !RemoveBracesIntention.Holder.isApplicableTo(it) }) return false - if (caretIsOnSingleTargetBranch(targetIfOrWhenExpression, targetBranchExpressions, caretOffset)) return false + if (caretIsOnSingleTargetBranch(targetIfOrWhenExpression, targetBranchExpressions, context.offset)) return false - when (targetIfOrWhenExpression) { - is KtIfExpression -> setTextGetter(KotlinBundle.lazyMessage("remove.braces.from.if.all.statements")) - is KtWhenExpression -> setTextGetter(KotlinBundle.lazyMessage("remove.braces.from.when.all.entries")) - } - return true + return targetIfOrWhenExpression is KtIfExpression || targetIfOrWhenExpression is KtWhenExpression } - override fun applyTo(element: KtExpression, editor: Editor?) { + override fun getPresentation(context: ActionContext, element: KtExpression): Presentation? { + val targetIfOrWhenExpression = targetIfOrWhenExpression(element) ?: return null + + val targetBranchExpressions = targetIfOrWhenExpression.targetBranchExpressions() + if (targetBranchExpressions.isEmpty() || targetBranchExpressions.any { !RemoveBracesIntention.Holder.isApplicableTo(it) }) return null + if (caretIsOnSingleTargetBranch(targetIfOrWhenExpression, targetBranchExpressions, context.offset)) return null + + return when (targetIfOrWhenExpression) { + is KtIfExpression -> Presentation.of(KotlinBundle.message("remove.braces.from.if.all.statements")) + is KtWhenExpression -> Presentation.of(KotlinBundle.message("remove.braces.from.when.all.entries")) + else -> null + } + } + + override fun invoke(actionContext: ActionContext, element: KtExpression, elementContext: Unit, updater: ModPsiUpdater) { val targetIfOrWhenExpression = targetIfOrWhenExpression(element) ?: return targetIfOrWhenExpression.targetBranchExpressions().forEach { - RemoveBracesIntention.Holder.removeBraces(targetIfOrWhenExpression, it) + RemoveBracesIntention.Holder.removeBraces(actionContext, targetIfOrWhenExpression, it, updater) } } @@ -55,4 +69,8 @@ internal class RemoveBracesFromAllBranchesIntention : SelfTargetingIntention(KtElement::class.java, KotlinBundle.lazyMessage("remove.braces")) { - override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean { +internal class RemoveBracesIntention: KotlinApplicableModCommandAction(KtElement::class) { + override fun getFamilyName(): @IntentionFamilyName String = + KotlinBundle.message("remove.braces") + + override fun isApplicableByPsi(element: KtElement): Boolean { val block = element.findChildBlock() ?: return false if (!Holder.isApplicableTo(block)) return false - when (val container = block.parent) { + return when(block.parent) { + is KtContainerNode, is KtWhenEntry -> true + else -> false + } + } + + override fun getPresentation(context: ActionContext, element: KtElement): Presentation? { + val block = element.findChildBlock() ?: return null + if (!Holder.isApplicableTo(block)) return null + + return when (val container = block.parent) { is KtContainerNode -> { - val description = container.getControlFlowElementDescription() ?: return false - setTextGetter(KotlinBundle.lazyMessage("remove.braces.from.0.statement", description)) + val description = container.getControlFlowElementDescription() ?: return null + Presentation.of(KotlinBundle.message("remove.braces.from.0.statement", description)) } is KtWhenEntry -> { - setTextGetter(KotlinBundle.lazyMessage("remove.braces.from.when.entry")) + Presentation.of(KotlinBundle.message("remove.braces.from.when.entry")) } + + else -> null } - return true } - override fun applyTo(element: KtElement, editor: Editor?) { + override fun invoke(actionContext: ActionContext, element: KtElement, elementContext: Unit, updater: ModPsiUpdater) { val block = element.findChildBlock() ?: return - Holder.removeBraces(element, block, editor) + Holder.removeBraces(actionContext, element, block, updater) } - override fun skipProcessingFurtherElementsAfter(element: PsiElement): Boolean = - element is KtBlockExpression && element.parent !is KtWhenEntry + override fun stopSearchAt(element: PsiElement, context: ActionContext): Boolean { + return element is KtBlockExpression && element.parent !is KtWhenEntry + } private fun KtElement.findChildBlock(): KtBlockExpression? = when (this) { is KtBlockExpression -> this @@ -69,25 +86,28 @@ internal class RemoveBracesIntention : SelfTargetingIntention(KtEleme } } - fun removeBraces(element: KtElement, block: KtBlockExpression, editor: Editor? = null) { - val factory = KtPsiFactory(element.project) + fun removeBraces(actionContext: ActionContext, element: KtElement, block: KtBlockExpression, updater: ModPsiUpdater) { + val project = element.project + val factory = KtPsiFactory(project) val statement = block.statements.single() - val caretOnAfterStatement = if (editor != null) editor.caretModel.offset >= statement.endOffset else false + val caretOnAfterStatement = updater.caretOffset >= statement.endOffset val container = block.parent val construct = container.parent as KtExpression - statement.handleComments(block, factory) + statement.handleComments(block) val newElement = block.replace(statement.copy()) - editor?.caretModel?.moveToOffset(if (caretOnAfterStatement) newElement.endOffset else newElement.startOffset) + updater.moveCaretTo(if (caretOnAfterStatement) newElement.endOffset else newElement.startOffset) if (construct is KtDoWhileExpression) { newElement.parent!!.addAfter(factory.createNewLine(), newElement) - } else if (editor != null) { - val document = editor.document + } else { + val document = actionContext.file.fileDocument + val rightMargin = CodeStyle.getSettings(project).getRightMargin(element.language) val line = document.getLineNumber(newElement.startOffset) - val rightMargin = editor.settings.getRightMargin(editor.project) - if (document.getLineEndOffset(line) - document.getLineStartOffset(line) >= rightMargin) { + val lineStartOffset = document.getLineStartOffset(line) + val lineEndOffset = document.getLineEndOffset(line) + newElement.textLength + if (lineEndOffset - lineStartOffset >= rightMargin) { newElement.parent.addBefore(factory.createNewLine(), newElement) } } @@ -99,12 +119,12 @@ internal class RemoveBracesIntention : SelfTargetingIntention(KtEleme ) { val replaced = construct.replace(factory.createExpressionByPattern("($0)", construct)) (replaced.children[0] as? KtIfExpression)?.`else`?.let { - editor?.caretModel?.moveToOffset(if (caretOnAfterStatement) it.endOffset else it.startOffset) + updater.moveCaretTo(if (caretOnAfterStatement) it.endOffset else it.startOffset) } } } - private fun KtExpression.handleComments(block: KtBlockExpression, factory: KtPsiFactory) { + private fun KtExpression.handleComments(block: KtBlockExpression) { val nextComments = comments(forward = true) val prevComments = comments(forward = false).reversed() val blockParent = block.parent @@ -123,7 +143,7 @@ internal class RemoveBracesIntention : SelfTargetingIntention(KtEleme return if (elements.any { it is PsiComment }) elements else emptyList() } - private fun List.hasLineBreak(): Boolean = - any { it is PsiWhiteSpace && it.textContains('\n') } } + + override fun KaSession.prepareContext(element: KtElement) {} } diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt index aadad142e1a3..ae9bc1a049d4 100644 --- a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt +++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt @@ -1,6 +1,7 @@ -fun foo() {} +fun foo1() {} +fun foo2() {} fun test(b: Boolean) { - if (b) foo() else - /* aaa */ foo() // bbb + if (b) foo1() else + /* aaa */ foo2() // bbb } diff --git a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt.after b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt.after index 776139161733..5de9f7b5f6db 100644 --- a/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt.after +++ b/plugins/kotlin/code-insight/intentions-shared/tests/testData/intentions/addBraces/elseWithComment.kt.after @@ -1,7 +1,8 @@ -fun foo() {} +fun foo1() {} +fun foo2() {} fun test(b: Boolean) { - if (b) foo() else { - /* aaa */ foo() // bbb + if (b) foo1() else { + /* aaa */ foo2() // bbb } } diff --git a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/AddBracesUtils.kt b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/AddBracesUtils.kt index 52cff7eda252..36c9935dbd2a 100644 --- a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/AddBracesUtils.kt +++ b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/AddBracesUtils.kt @@ -6,20 +6,8 @@ import com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.idea.base.psi.getLineNumber import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtDoWhileExpression -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtForExpression -import org.jetbrains.kotlin.psi.KtIfExpression -import org.jetbrains.kotlin.psi.KtPsiFactory -import org.jetbrains.kotlin.psi.KtWhenEntry -import org.jetbrains.kotlin.psi.KtWhileExpression -import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange -import org.jetbrains.kotlin.psi.psiUtil.allChildren -import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespace -import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments -import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments -import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.* object AddBracesUtils { fun addBraces(element: KtElement, expression: KtExpression) { @@ -72,15 +60,18 @@ object AddBracesUtils { } val result = expression.replace(psiFactory.createSingleStatementBlock(expression, nextComment = nextComment?.text)) - when (element) { - is KtDoWhileExpression -> { - // remove new line between '}' and while - (element.body?.parent?.nextSibling as? PsiWhiteSpace)?.delete() - } - is KtIfExpression -> { - (result?.parent?.nextSibling as? PsiWhiteSpace)?.delete() - } + val parent = when (element) { + is KtDoWhileExpression -> element.body?.parent + is KtIfExpression -> result.parent + is KtWhenEntry -> result + else -> null + } + parent?.let { + // remove new line between '{' and if/when + (it.prevSibling as? PsiWhiteSpace)?.delete() + // remove new line between '}' and while + (it.nextSibling as? PsiWhiteSpace)?.delete() } saver?.restore(result, forceAdjustIndent = false) } diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTestBase.kt b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTestBase.kt index 64d2753c44ce..f46022c86ca0 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTestBase.kt +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/intentions/AbstractIntentionTestBase.kt @@ -215,12 +215,13 @@ abstract class AbstractIntentionTestBase : KotlinLightCodeInsightFixtureTestCase val isApplicableOnPooled: Boolean = project.computeOnBackground { runReadAction { intentionAction.isAvailable(project, editor, file) } } + + val modCommandAction: ModCommandAction? = intentionAction.asModCommandAction() Assert.assertTrue( - "isAvailable() for " + intentionAction.javaClass + " should return " + isApplicableExpected, + "isAvailable() for " + (modCommandAction ?: intentionAction).javaClass + " should return " + isApplicableExpected, isApplicableExpected == isApplicableOnPooled ) - val modCommandAction: ModCommandAction? = intentionAction.asModCommandAction() if (modCommandAction == null) { val isApplicableOnEdt = intentionAction.isAvailable(project, editor, file)