From 462074df8c75895b28697c8dcfaf75a8bb12e2d7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 12 May 2015 17:45:54 +0300 Subject: [PATCH] MergeWhenIntention code refactoring + smaller range --- .../kotlin/idea/JetBundle.properties | 2 - .../branchedTransformationUtils.kt | 60 +--------------- .../intentions/MergeWhenIntention.kt | 69 +++++++++++++++++-- 3 files changed, 63 insertions(+), 68 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index ec1338c6649..9e192fcb12d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -202,8 +202,6 @@ unfold.call.to.if=Replace method call with 'if' expression unfold.call.to.if.family=Replace Method Call with 'if' Expression unfold.call.to.when=Replace method call with 'when' expression unfold.call.to.when.family=Replace Method Call with 'when' Expression -merge.when=Merge 'when' expressions -merge.when.family=Merge 'when' Expression transform.if.statement.with.assignments.to.expression=Transform 'if' statement with assignments to expression transform.assignment.with.if.expression.to.statement=Transform assignment with 'if' expression to statement transform.if.statement.with.assignments.to.expression.family=Transform 'if' Statement with Assignments to Expression diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt index 5ff2a57a6c5..6041a4c1c1e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/branchedTransformationUtils.kt @@ -16,15 +16,10 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches -import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.psi.typeRefHelpers.getTypeReference +import org.jetbrains.kotlin.psi.psiUtil.replaced fun JetWhenCondition.toExpression(subject: JetExpression?): JetExpression { val factory = JetPsiFactory(this) @@ -150,56 +145,3 @@ public fun JetWhenExpression.introduceSubject(): JetWhenExpression { return replaced(whenExpression) } - -public fun JetWhenExpression.canMergeWithNext(): Boolean { - fun checkConditions(e1: JetWhenEntry, e2: JetWhenEntry): Boolean = - e1.getConditions().toList().toRange().matches(e2.getConditions().toList().toRange()) - - fun JetWhenEntry.declarationNames(): Set = - getExpression()?.blockExpressionsOrSingle() - ?.filter { it is JetNamedDeclaration } - ?.map { it.getName() } - ?.filterNotNull()?.toSet() ?: emptySet() - - fun checkBodies(e1: JetWhenEntry, e2: JetWhenEntry): Boolean { - if (ContainerUtil.intersects(e1.declarationNames(), e2.declarationNames())) return false - - return when (e1.getExpression()?.outermostLastBlockElement()) { - is JetReturnExpression, is JetThrowExpression, is JetBreakExpression, is JetContinueExpression -> false - else -> true - } - } - - val sibling = PsiTreeUtil.skipSiblingsForward(this, javaClass()) - - if (sibling !is JetWhenExpression) return false - if (!getSubjectExpression().matches(sibling.getSubjectExpression())) return false - - val entries1 = getEntries() - val entries2 = sibling.getEntries() - return entries1.size() == entries2.size() && (entries1 zip entries2).all { pair -> - checkConditions(pair.first, pair.second) && checkBodies(pair.first, pair.second) - } -} - -public fun JetWhenExpression.mergeWithNext() { - fun JetExpression?.mergeWith(that: JetExpression?): JetExpression? = when { - this == null -> that - that == null -> this - else -> { - val block = if (this is JetBlockExpression) this else replaced(wrapInBlock()) - for (element in that.blockExpressionsOrSingle()) { - val expression = block.appendElement(element) - block.addBefore(JetPsiFactory(this).createNewLine(), expression) - } - block - } - } - - val sibling = PsiTreeUtil.skipSiblingsForward(this, javaClass()) as JetWhenExpression - for ((entry1, entry2) in getEntries() zip sibling.getEntries()) { - entry1.getExpression() mergeWith entry2.getExpression() - } - - getParent()?.deleteChildRange(getNextSibling(), sibling) -} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt index 06467b1eb68..6c864b5a5e9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/branchedTransformations/intentions/MergeWhenIntention.kt @@ -17,15 +17,70 @@ package org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions import com.intellij.openapi.editor.Editor -import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingOffsetIndependentIntention -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.canMergeWithNext -import org.jetbrains.kotlin.idea.intentions.branchedTransformations.mergeWithNext -import org.jetbrains.kotlin.psi.JetWhenExpression +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.idea.intentions.JetSelfTargetingRangeIntention +import org.jetbrains.kotlin.idea.util.psi.patternMatching.matches +import org.jetbrains.kotlin.idea.util.psi.patternMatching.toRange +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.* -public class MergeWhenIntention : JetSelfTargetingOffsetIndependentIntention("merge.when", javaClass()) { - override fun isApplicableTo(element: JetWhenExpression): Boolean = element.canMergeWithNext() +public class MergeWhenIntention : JetSelfTargetingRangeIntention(javaClass(), "Merge 'when' expressions") { + override fun applicabilityRange(element: JetWhenExpression): TextRange? { + val next = PsiTreeUtil.skipSiblingsForward(element, javaClass()) as? JetWhenExpression ?: return null + + if (!element.getSubjectExpression().matches(next.getSubjectExpression())) return null + + val entries1 = element.getEntries() + val entries2 = next.getEntries() + if (entries1.size() != entries2.size()) return null + if (!entries1.zip(entries2).all { pair -> + conditionsMatch(pair.first, pair.second) && checkBodies(pair.first, pair.second) + }) return null + + return element.getWhenKeyword().getTextRange() + } + + private fun conditionsMatch(e1: JetWhenEntry, e2: JetWhenEntry): Boolean = + e1.getConditions().toList().toRange().matches(e2.getConditions().toList().toRange()) + + private fun checkBodies(e1: JetWhenEntry, e2: JetWhenEntry): Boolean { + val names1 = e1.declarationNames() + val names2 = e2.declarationNames() + if (names1.any { it in names2 }) return false + + return when (e1.getExpression()?.outermostLastBlockElement()) { + is JetReturnExpression, is JetThrowExpression, is JetBreakExpression, is JetContinueExpression -> false + else -> true + } + } + + private fun JetWhenEntry.declarationNames(): Set = + getExpression()?.blockExpressionsOrSingle() + ?.filter { it is JetNamedDeclaration } + ?.map { it.getName() } + ?.filterNotNull()?.toSet() ?: emptySet() override fun applyTo(element: JetWhenExpression, editor: Editor) { - element.mergeWithNext() + val nextWhen = PsiTreeUtil.skipSiblingsForward(element, javaClass()) as JetWhenExpression + for ((entry1, entry2) in element.getEntries().zip(nextWhen.getEntries())) { + entry1.getExpression().mergeWith(entry2.getExpression()) + } + + element.getParent().deleteChildRange(element.getNextSibling(), nextWhen) + } + + private fun JetExpression?.mergeWith(that: JetExpression?): JetExpression? = when { + this == null -> that + that == null -> this + else -> { + val block = if (this is JetBlockExpression) this else replaced(wrapInBlock()) + for (element in that.blockExpressionsOrSingle()) { + val expression = block.appendElement(element) + block.addBefore(JetPsiFactory(this).createNewLine(), expression) + } + block + } } }