[kotlin] Extract utility for adding braces to codeinsight utils

KTIJ-22055

GitOrigin-RevId: 874b8837408d6b58d8bd332ed59236878435f36b
This commit is contained in:
Alexey Belkov
2025-05-30 17:11:02 +04:00
committed by intellij-monorepo-bot
parent 154ff71fce
commit 36cde461fb
4 changed files with 94 additions and 69 deletions

View File

@@ -12,7 +12,7 @@ import org.jetbrains.idea.devkit.DevKitBundle
import org.jetbrains.idea.devkit.inspections.quickfix.CancellationCheckInLoopsFixProvider
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.idea.base.codeInsight.ShortenReferencesFacility
import org.jetbrains.kotlin.idea.codeInsight.intentions.shared.AddBracesIntention
import org.jetbrains.kotlin.idea.codeinsight.utils.AddBracesUtils
import org.jetbrains.kotlin.idea.codeinsight.utils.isRedundantSemicolon
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBlockExpression
@@ -52,7 +52,7 @@ internal class KtInsertCancellationCheckInLoopFix(cancellationCheckCallFqn: Stri
is KtBlockExpression -> loopBody
// single-line loops
is KtExpression -> {
AddBracesIntention.Util.addBraces(this, loopBody)
AddBracesUtils.addBraces(this, loopBody)
body as KtBlockExpression
}
// no-body loops like `for (i in 1..10);`

View File

@@ -4,17 +4,13 @@ package org.jetbrains.kotlin.idea.codeInsight.intentions.shared
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.IntellijInternalApi
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.annotations.ApiStatus.Internal
import org.jetbrains.kotlin.idea.base.psi.getLineNumber
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention
import org.jetbrains.kotlin.idea.codeinsight.utils.AddBracesUtils
import org.jetbrains.kotlin.idea.codeinsight.utils.getControlFlowElementDescription
import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.psi.psiUtil.startOffset
@Internal
@IntellijInternalApi
@@ -42,7 +38,7 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
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
Util.addBraces(element, expression)
AddBracesUtils.addBraces(element, expression)
}
private fun KtElement.getTargetExpression(caretLocation: Int): KtExpression? {
@@ -62,63 +58,4 @@ class AddBracesIntention : SelfTargetingIntention<KtElement>(KtElement::class.ja
else -> null
}
}
object Util {
fun addBraces(element: KtElement, expression: KtExpression) {
val psiFactory = KtPsiFactory(element.project)
val semicolon = element.getNextSiblingIgnoringWhitespaceAndComments()?.takeIf { it.node.elementType == KtTokens.SEMICOLON }
if (semicolon != null) {
val afterSemicolon = semicolon.getNextSiblingIgnoringWhitespace()
if (semicolon.getLineNumber() == afterSemicolon?.getLineNumber())
semicolon.replace(psiFactory.createNewLine())
else
semicolon.delete()
}
val nextComment = when {
element is KtDoWhileExpression -> null // bound to the closing while
element is KtIfExpression && expression === element.then && element.`else` != null -> null // bound to else
else -> {
val nextSibling = element.getNextSiblingIgnoringWhitespace() ?: element.parent.getNextSiblingIgnoringWhitespace()
nextSibling.takeIf { it is PsiComment && it.getLineNumber() == element.getLineNumber(start = false) }
}
}
nextComment?.delete()
val allChildren = element.allChildren
val (first, last) = when (element) {
is KtIfExpression -> {
val containerNode = expression.parent
val first = containerNode.getPrevSiblingIgnoringWhitespaceAndComments()
val last = containerNode.siblings(withItself = false)
.takeWhile { it is PsiWhiteSpace || it is PsiComment }.lastOrNull() ?: containerNode
first to last
}
is KtForExpression -> element.rightParenthesis to allChildren.last
is KtWhileExpression -> element.rightParenthesis to allChildren.last
is KtWhenEntry -> element.arrow to allChildren.last
is KtDoWhileExpression -> allChildren.first to element.whileKeyword
else -> null to null
}
val saver = if (first != null && last != null) {
val range = PsiChildRange(first, last)
CommentSaver(range).also {
range.filterIsInstance<PsiComment>().toList().forEach { it.delete() }
}
} else {
null
}
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()
}
saver?.restore(result, forceAdjustIndent = false)
}
}
}

View File

@@ -10,6 +10,7 @@ import com.intellij.util.containers.addIfNotNull
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.utils.AddBracesUtils
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -41,7 +42,7 @@ internal class AddBracesToAllBranchesIntention : SelfTargetingIntention<KtExpres
val branches = targetIfOrWhenExpression.targetBranchExpressions()
for (branch in branches) {
val container = branch.parent as? KtWhenEntry ?: targetIfOrWhenExpression
AddBracesIntention.Util.addBraces(container, branch)
AddBracesUtils.addBraces(container, branch)
}
}

View File

@@ -0,0 +1,87 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.codeinsight.utils
import com.intellij.psi.PsiComment
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
object AddBracesUtils {
fun addBraces(element: KtElement, expression: KtExpression) {
val psiFactory = KtPsiFactory(element.project)
val semicolon = element.getNextSiblingIgnoringWhitespaceAndComments()?.takeIf { it.node.elementType == KtTokens.SEMICOLON }
if (semicolon != null) {
val afterSemicolon = semicolon.getNextSiblingIgnoringWhitespace()
if (semicolon.getLineNumber() == afterSemicolon?.getLineNumber()) {
semicolon.replace(psiFactory.createNewLine())
} else {
semicolon.delete()
}
}
val nextComment = when (element) {
is KtDoWhileExpression -> null // bound to the closing while
is KtIfExpression if expression === element.then && element.`else` != null -> null // bound to else
else -> {
val nextSibling = element.getNextSiblingIgnoringWhitespace() ?: element.parent.getNextSiblingIgnoringWhitespace()
nextSibling.takeIf { it is PsiComment && it.getLineNumber() == element.getLineNumber(start = false) }
}
}
nextComment?.delete()
val allChildren = element.allChildren
val (first, last) = when (element) {
is KtIfExpression -> {
val containerNode = expression.parent
val first = containerNode.getPrevSiblingIgnoringWhitespaceAndComments()
val last = containerNode.siblings(withItself = false)
.takeWhile { it is PsiWhiteSpace || it is PsiComment }.lastOrNull() ?: containerNode
first to last
}
is KtForExpression -> element.rightParenthesis to allChildren.last
is KtWhileExpression -> element.rightParenthesis to allChildren.last
is KtWhenEntry -> element.arrow to allChildren.last
is KtDoWhileExpression -> allChildren.first to element.whileKeyword
else -> null to null
}
val saver = if (first != null && last != null) {
val range = PsiChildRange(first, last)
CommentSaver(range).also {
range.filterIsInstance<PsiComment>().toList().forEach { it.delete() }
}
} else {
null
}
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()
}
}
saver?.restore(result, forceAdjustIndent = false)
}
}