MergeWhenIntention code refactoring + smaller range

This commit is contained in:
Valentin Kipyatkov
2015-05-12 17:45:54 +03:00
parent 102aa661ee
commit 462074df8c
3 changed files with 63 additions and 68 deletions
@@ -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
@@ -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<String> =
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<PsiWhiteSpace>())
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<PsiWhiteSpace>()) as JetWhenExpression
for ((entry1, entry2) in getEntries() zip sibling.getEntries()) {
entry1.getExpression() mergeWith entry2.getExpression()
}
getParent()?.deleteChildRange(getNextSibling(), sibling)
}
@@ -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<JetWhenExpression>("merge.when", javaClass()) {
override fun isApplicableTo(element: JetWhenExpression): Boolean = element.canMergeWithNext()
public class MergeWhenIntention : JetSelfTargetingRangeIntention<JetWhenExpression>(javaClass(), "Merge 'when' expressions") {
override fun applicabilityRange(element: JetWhenExpression): TextRange? {
val next = PsiTreeUtil.skipSiblingsForward(element, javaClass<PsiWhiteSpace>()) 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<String> =
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<PsiWhiteSpace>()) 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
}
}
}