diff --git a/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/after.kt.template b/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/after.kt.template new file mode 100644 index 00000000000..33a1d1a60a3 --- /dev/null +++ b/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/after.kt.template @@ -0,0 +1 @@ +"ABC".equals("abc", true) \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/before.kt.template b/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/before.kt.template new file mode 100644 index 00000000000..c488bdf449e --- /dev/null +++ b/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/before.kt.template @@ -0,0 +1 @@ +"abc".equals("ABC", true) \ No newline at end of file diff --git a/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/description.html b/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/description.html new file mode 100644 index 00000000000..cdf3ba7f902 --- /dev/null +++ b/idea/resources/intentionDescriptions/SwapStringEqualsIgnoreCaseIntention/description.html @@ -0,0 +1,5 @@ + + +This intention replaces string1.equals(string2, ignoreCase) with string2.equals(string1, ignoreCase). + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 0f73159e30c..1059ccb1d84 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1151,6 +1151,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.SwapStringEqualsIgnoreCaseIntention + Kotlin + + org.jetbrains.kotlin.idea.intentions.SplitIfIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SwapStringEqualsIgnoreCaseIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SwapStringEqualsIgnoreCaseIntention.kt new file mode 100644 index 00000000000..37a93e74fcc --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SwapStringEqualsIgnoreCaseIntention.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.intentions + +import com.intellij.codeInsight.intention.LowPriorityAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.idea.core.moveCaret +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.createExpressionByPattern +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull + +class SwapStringEqualsIgnoreCaseIntention : SelfTargetingRangeIntention(KtDotQualifiedExpression::class.java, "Flip 'equals'"), LowPriorityAction { + + override fun applicabilityRange(element: KtDotQualifiedExpression): TextRange? { + val descriptor = element.getCallableDescriptor() ?: return null + + val fqName: FqName = descriptor.fqNameOrNull() ?: return null + if (fqName.asString() != "kotlin.text.equals") return null + + val valueParameters = descriptor.valueParameters.takeIf { it.size == 2 } ?: return null + if (!KotlinBuiltIns.isStringOrNullableString(valueParameters[0].type)) return null + if (!KotlinBuiltIns.isBoolean(valueParameters[1].type)) return null + + return element.callExpression?.calleeExpression?.textRange + } + + override fun applyTo(element: KtDotQualifiedExpression, editor: Editor?) { + val callExpression = element.callExpression ?: return + val offset = (editor?.caretModel?.offset ?: 0) - (callExpression.calleeExpression?.startOffset ?: 0) + val receiverExpression = element.receiverExpression + val valueArguments = callExpression.valueArguments.takeIf { it.size == 2 } ?: return + val newElement = KtPsiFactory(element).createExpressionByPattern( + "$0.equals($1, $2)", + valueArguments[0].getArgumentExpression()!!, + receiverExpression, + valueArguments[1].text + ) + val replacedElement = element.replaced(newElement) as? KtDotQualifiedExpression + replacedElement?.callExpression?.calleeExpression?.startOffset?.let { + editor?.moveCaret(it + offset) + } + } + +} \ No newline at end of file diff --git a/idea/testData/intentions/swapStringEqualsIgnoreCase/.intention b/idea/testData/intentions/swapStringEqualsIgnoreCase/.intention new file mode 100644 index 00000000000..9970e45f69e --- /dev/null +++ b/idea/testData/intentions/swapStringEqualsIgnoreCase/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.SwapStringEqualsIgnoreCaseIntention diff --git a/idea/testData/intentions/swapStringEqualsIgnoreCase/equals.kt b/idea/testData/intentions/swapStringEqualsIgnoreCase/equals.kt new file mode 100644 index 00000000000..37a29d5d2af --- /dev/null +++ b/idea/testData/intentions/swapStringEqualsIgnoreCase/equals.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +fun test() { + "ABC".equals("abc") +} diff --git a/idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt b/idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt new file mode 100644 index 00000000000..15a1b20f65f --- /dev/null +++ b/idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +fun test() { + "ABC".equals(Foo().bar(), ignoreCase = true) +} + +class Foo { + fun bar(): String = "" +} diff --git a/idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt.after b/idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt.after new file mode 100644 index 00000000000..6815bfae893 --- /dev/null +++ b/idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt.after @@ -0,0 +1,8 @@ +// WITH_RUNTIME +fun test() { + Foo().bar().equals("ABC", ignoreCase = true) +} + +class Foo { + fun bar(): String = "" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 90dd43222fd..cd8a8c985ab 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -15348,6 +15348,27 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/swapStringEqualsIgnoreCase") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SwapStringEqualsIgnoreCase extends AbstractIntentionTest { + public void testAllFilesPresentInSwapStringEqualsIgnoreCase() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/swapStringEqualsIgnoreCase"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("equals.kt") + public void testEquals() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/swapStringEqualsIgnoreCase/equals.kt"); + doTest(fileName); + } + + @TestMetadata("equalsIgnoreCase.kt") + public void testEqualsIgnoreCase() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/swapStringEqualsIgnoreCase/equalsIgnoreCase.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/toInfixCall") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)