diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt index 5df28d8b6f6..0bb229d2bae 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.idea.intentions.conventionNameCalls import com.intellij.codeInsight.intention.HighPriorityAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.util.TextRange +import com.intellij.psi.tree.IElementType import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.intentions.* import org.jetbrains.kotlin.lexer.KtSingleValueToken @@ -36,6 +37,17 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention KtTokens.GT + KtTokens.GT -> KtTokens.LT + + KtTokens.GTEQ -> KtTokens.LTEQ + KtTokens.LTEQ -> KtTokens.GTEQ + + else -> null + } + override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? { val calleeExpression = element.callExpression?.calleeExpression as? KtSimpleNameExpression ?: return null val operation = operation(calleeExpression) ?: return null @@ -58,15 +70,24 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention { + val prefixExpression = element.getWrappingPrefixExpressionIfAny() ?: return + val newExpression = factory.createExpressionByPattern("$0 != $1", receiver, argument) + prefixExpression.replace(newExpression) + } + in OperatorConventions.COMPARISON_OPERATIONS -> { + val binaryParent = element.parent as? KtBinaryExpression ?: return + val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument) + binaryParent.replace(newExpression) + } + else -> { + val newExpression = factory.createExpressionByPattern("$0 ${operation.value} $1", receiver, argument) + element.replace(newExpression) + } } + } private fun PsiElement.getWrappingPrefixExpressionIfAny() = @@ -74,11 +95,31 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention { + val prefixExpression = calleeExpression.parent?.parent?.getWrappingPrefixExpressionIfAny() + if (prefixExpression != null && prefixExpression.operationToken == KtTokens.EXCL) KtTokens.EXCLEQ + else KtTokens.EQEQ + } + OperatorNameConventions.COMPARE_TO -> { + // callee -> call -> DotQualified -> Binary + val dotQualified = calleeExpression.parent?.parent + val binaryParent = dotQualified?.parent as? KtBinaryExpression ?: return null + val notZero = when { + binaryParent.right?.text == "0" -> binaryParent.left + binaryParent.left?.text == "0" -> binaryParent.right + else -> return null + } + if (notZero != dotQualified) return null + val token = binaryParent.operationToken as? KtSingleValueToken ?: return null + if (token in OperatorConventions.COMPARISON_OPERATIONS) { + if (notZero == binaryParent.left) token else token.inverted() + } + else { + null + } + } + else -> OperatorConventions.BINARY_OPERATION_NAMES.inverse()[identifier] } - return OperatorConventions.BINARY_OPERATION_NAMES.inverse()[identifier] } } diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsCompareTo.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsCompareTo.kt new file mode 100644 index 00000000000..bba372a41f0 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsCompareTo.kt @@ -0,0 +1,3 @@ +// IS_APPLICABLE: false + +val x = 2.compareTo(2) == 0 \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt new file mode 100644 index 00000000000..512be417371 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Replace with '>' operator + +val x = 3.compareTo(2) > 0 \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt.after b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt.after new file mode 100644 index 00000000000..d1b34eee6e1 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt.after @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Replace with '>' operator + +val x = 3 > 2 \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt new file mode 100644 index 00000000000..674875a0a13 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Replace with '<=' operator + +val x = 0 >= 4.compareTo(5) \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt.after b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt.after new file mode 100644 index 00000000000..eef5c45d02f --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt.after @@ -0,0 +1,3 @@ +// INTENTION_TEXT: Replace with '<=' operator + +val x = 4 <= 5 \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/safeCompareTo.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/safeCompareTo.kt new file mode 100644 index 00000000000..41591deefa7 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/safeCompareTo.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false +// ERROR: Infix call corresponds to a dot-qualified call 'nullable?.compareTo(1).compareTo(0)' which is not allowed on a nullable receiver 'nullable?.compareTo(1)'. Use '?.'-qualified call instead + +val nullable: Int? = null +val x = nullable?.compareTo(1) >= 0 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 1d82d627d75..ce8a636d04f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -2744,6 +2744,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("equalsCompareTo.kt") + public void testEqualsCompareTo() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsCompareTo.kt"); + doTest(fileName); + } + @TestMetadata("extensionFunction.kt") public void testExtensionFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/extensionFunction.kt"); @@ -2756,6 +2762,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("greater.kt") + public void testGreater() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/greater.kt"); + doTest(fileName); + } + + @TestMetadata("lessEquals.kt") + public void testLessEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/lessEquals.kt"); + doTest(fileName); + } + @TestMetadata("minusSanityTest.kt") public void testMinusSanityTest() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/minusSanityTest.kt"); @@ -2816,6 +2834,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("safeCompareTo.kt") + public void testSafeCompareTo() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/safeCompareTo.kt"); + doTest(fileName); + } + @TestMetadata("super.kt") public void testSuper() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/super.kt");