SwapBinaryExpression: implementation cleaned up, tests renamed

#KT-4868 Fixed
This commit is contained in:
Andrey Breslav
2014-05-21 12:37:36 +04:00
parent acf1dc1912
commit 269e4ae828
14 changed files with 88 additions and 54 deletions
@@ -20,60 +20,37 @@ import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetBinaryExpression
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.plugin.intentions.SwapBinaryExpression.Position.*
import org.jetbrains.jet.plugin.util.JetPsiPrecedences
import org.jetbrains.jet.lexer.JetTokens.*
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
public class SwapBinaryExpression : JetSelfTargetingIntention<JetBinaryExpression>(
"swap.binary.expression", javaClass()
) {
enum class Position {
FIRST_LEFT
FIRST_RIGHT
NEXT_LEFT
NEXT_RIGHT
}
class object {
val SUPPORTED_OPERATIONS = setOf(PLUS, MUL, OROR, ANDAND, EQEQ, EXCLEQ, EQEQEQ, EXCLEQEQEQ, GT, LT, GTEQ, LTEQ)
fun getInnermostOperand(element: JetBinaryExpression, position: Position): JetExpression? {
val left = element.getLeft()
val right = element.getRight()
if (left == null || right == null) {
return null
}
val parentPrecedence = JetPsiPrecedences.getPrecedence(element)
val leftPrecedence = JetPsiPrecedences.getPrecedence(left)
val rightPrecedence = JetPsiPrecedences.getPrecedence(right)
return when (position) {
FIRST_LEFT -> if (leftPrecedence < parentPrecedence) left
else if (left is JetBinaryExpression) getInnermostOperand(left, NEXT_RIGHT)
else left as JetExpression
FIRST_RIGHT -> if (rightPrecedence < parentPrecedence) right
else if (right is JetBinaryExpression) getInnermostOperand(right, NEXT_LEFT)
else right as JetExpression
NEXT_LEFT -> if (leftPrecedence < parentPrecedence || left !is JetBinaryExpression) left
else getInnermostOperand(left, NEXT_LEFT)
NEXT_RIGHT -> if (rightPrecedence < parentPrecedence || right !is JetBinaryExpression) right
else getInnermostOperand(right, NEXT_RIGHT)
}
val SUPPORTED_OPERATION_NAMES = SUPPORTED_OPERATIONS.map { OperatorConventions.BINARY_OPERATION_NAMES[it]?.asString() }.toSet().filterNotNull() +
setOf("xor", "or", "and", "equals", "identityEquals")
}
override fun isApplicableTo(element: JetBinaryExpression): Boolean {
if (getInnermostOperand(element, FIRST_LEFT) == null || getInnermostOperand(element, FIRST_RIGHT) == null) {
if (leftSubject(element) == null || rightSubject(element) == null) {
return false
}
val operatorTextLiteral = element.getOperationReference().getText()
val approvedOperators = setOf("+", "plus", "*", "times", "||", "or", "and",
"&&", "==", "!=", "xor", ">", "<", ">=", "<=", "equals")
if (approvedOperators.contains(operatorTextLiteral)) {
setText("Flip '$operatorTextLiteral'")
val operationToken = element.getOperationToken()
val operationTokenText = element.getOperationReference().getText()
if (operationToken in SUPPORTED_OPERATIONS
|| operationToken == IDENTIFIER && operationTokenText in SUPPORTED_OPERATION_NAMES) {
setText("Flip '$operationTokenText'")
return true
}
return false
}
override fun applyTo(element: JetBinaryExpression, editor: Editor) {
// Have to use text here to preserve names like "plus"
val operator = element.getOperationReference().getText()!!
val convertedOperator = when (operator) {
">" -> "<"
@@ -82,12 +59,30 @@ public class SwapBinaryExpression : JetSelfTargetingIntention<JetBinaryExpressio
">=" -> "<="
else -> operator
}
val left = getInnermostOperand(element, FIRST_LEFT)!!
val right = getInnermostOperand(element, FIRST_RIGHT)!!
val left = leftSubject(element)!!
val right = rightSubject(element)!!
val newRight = JetPsiFactory.createExpression(element.getProject(), left.getText())
val newLeft = JetPsiFactory.createExpression(element.getProject(), right.getText())
left.replace(newLeft)
right.replace(newRight)
element.replace(JetPsiFactory.createBinaryExpression(element.getProject(), element.getLeft(), convertedOperator, element.getRight()))
}
private fun leftSubject(element: JetBinaryExpression): JetExpression? {
return firstDescendantOfTighterPrecedence(element.getLeft(), JetPsiPrecedences.getPrecedence(element), JetBinaryExpression::getRight)
}
private fun rightSubject(element: JetBinaryExpression): JetExpression? {
return firstDescendantOfTighterPrecedence(element.getRight(), JetPsiPrecedences.getPrecedence(element), JetBinaryExpression::getLeft)
}
private fun firstDescendantOfTighterPrecedence(expression: JetExpression?, precedence: Int, getChild: JetBinaryExpression.() -> JetExpression?): JetExpression? {
if (expression is JetBinaryExpression) {
val expressionPrecedence = JetPsiPrecedences.getPrecedence(expression)
if (!JetPsiPrecedences.isTighter(expressionPrecedence, precedence)) {
return firstDescendantOfTighterPrecedence(expression.getChild(), precedence, getChild)
}
}
return expression
}
}
@@ -61,4 +61,8 @@ public object JetPsiPrecedences {
else -> PRECEDENCE_OF_ATOMIC_EXPRESSION
}
}
public fun isTighter(subject: Int, tighterThan: Int): Boolean {
return subject < tighterThan
}
}
@@ -0,0 +1,5 @@
// IS_APPLICABLE: false
fun main() {
var mutablevar = 20
mutablevar = <caret>10
}
@@ -1,5 +1,3 @@
// IS_APPLICABLE: false
fun main() {
var mutablevar = 20
mutablevar = <caret>10
fun neq(a: Int, b: Int) {
if (a ==<caret> b) {}
}
@@ -0,0 +1,3 @@
fun neq(a: Int, b: Int) {
if (a ===<caret> b) {}
}
@@ -1,3 +1,3 @@
fun neq(a: Int, b: Int) {
if (a ==<caret> b) {}
if (b === a) {}
}
@@ -0,0 +1,3 @@
fun neq(a: Int, b: Int) {
if (a identityEquals<caret> b) {}
}
@@ -0,0 +1,3 @@
fun neq(a: Int, b: Int) {
if (b identityEquals a) {}
}
@@ -0,0 +1,3 @@
fun neq(a: Int, b: Int) {
if (<caret>b !== a) {}
}
@@ -1,4 +1,3 @@
// IS_APPLICABLE: false
fun neq(a: Int, b: Int) {
if (<caret>a !== b) {}
}
@@ -0,0 +1,3 @@
fun test(): Int {
return 1 <caret>+ 2 - 3
}
@@ -0,0 +1,3 @@
fun test(): Int {
return 2 + 1 - 3
}
@@ -3255,6 +3255,11 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/intentions/swapBinaryExpression"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("assignment.kt")
public void testAssignment() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/assignment.kt");
}
@TestMetadata("compareTo.kt")
public void testCompareTo() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/compareTo.kt");
@@ -3295,11 +3300,6 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/divideLiteral.kt");
}
@TestMetadata("doubleEquals.kt")
public void testDoubleEquals() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/doubleEquals.kt");
}
@TestMetadata("equals.kt")
public void testEquals() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/equals.kt");
@@ -3320,6 +3320,16 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/greaterThanEquals.kt");
}
@TestMetadata("identityEquals.kt")
public void testIdentityEquals() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/identityEquals.kt");
}
@TestMetadata("identityEqualsLiteral.kt")
public void testIdentityEqualsLiteral() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/identityEqualsLiteral.kt");
}
@TestMetadata("in.kt")
public void testIn() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/in.kt");
@@ -3410,16 +3420,16 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/nonBinaryExpr.kt");
}
@TestMetadata("notDoubleEquals.kt")
public void testNotDoubleEquals() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/notDoubleEquals.kt");
}
@TestMetadata("notEquals.kt")
public void testNotEquals() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/notEquals.kt");
}
@TestMetadata("notIdentityEquals.kt")
public void testNotIdentityEquals() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/notIdentityEquals.kt");
}
@TestMetadata("notIn.kt")
public void testNotIn() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/notIn.kt");
@@ -3455,6 +3465,11 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/plusLiteral.kt");
}
@TestMetadata("plusMinus.kt")
public void testPlusMinus() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/plusMinus.kt");
}
@TestMetadata("rangeTo.kt")
public void testRangeTo() throws Exception {
doTestSwapBinaryExpression("idea/testData/intentions/swapBinaryExpression/rangeTo.kt");