ReplaceWithInfixFunctionCallIntention - refactored

This commit is contained in:
Valentin Kipyatkov
2015-04-29 18:15:37 +03:00
parent 9dd67cfc4d
commit 2de466970d
9 changed files with 25 additions and 132 deletions
@@ -290,10 +290,6 @@ add.name.to.argument.single=Add name to argument\: ''{0}''
add.name.to.argument.multiple=Add name to argument...
add.name.to.argument.action=Add name to argument...
add.name.to.parameter.name.chooser.title=Choose parameter name
replace.with.infix.function.call.intention=Replace with infix function call
replace.with.infix.function.call.intention.family=Replace with Infix Function Call
replace.with.infix.function.call.intention.error.resolution.failed=The element cannot be resolved
replace.with.infix.function.call.intention.error.package.call=Cannot be applied with a package as the receiver
split.if=Split into 2 if's
split.if.family=Split If
replace.with.operator.assign.intention=Replace with an operator-assign expression
@@ -16,92 +16,42 @@
package org.jetbrains.kotlin.idea.intentions
import org.jetbrains.kotlin.psi.JetCallExpression
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.psi.JetValueArgument
import org.jetbrains.kotlin.psi.JetPsiUnparsingUtils
import org.jetbrains.kotlin.psi.JetPsiFactory
import com.intellij.codeInsight.hint.HintManager
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.psi.JetBinaryExpression
import org.jetbrains.kotlin.psi.JetCallExpression
import org.jetbrains.kotlin.psi.JetDotQualifiedExpression
import org.jetbrains.kotlin.psi.JetPsiFactory
public open class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<JetCallExpression>("replace.with.infix.function.call.intention", javaClass()) {
public class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<JetCallExpression>(javaClass(), "Replace with infix function call") {
override fun isApplicableTo(element: JetCallExpression, caretOffset: Int): Boolean {
val calleeExpr = element.getCalleeExpression()
if (calleeExpr == null) return false
val calleeExpr = element.getCalleeExpression() ?: return false
if (!calleeExpr.getTextRange().containsOffset(caretOffset)) return false
val textRange = calleeExpr.getTextRange()
if (textRange == null) return false
val dotQualified = element.getParent() as? JetDotQualifiedExpression ?: return false
if (caretOffset !in textRange) return false
if (element.getTypeArgumentList() != null) return false
val parent = element.getParent()
val argument = element.getValueArguments().singleOrNull() ?: return false
if (argument.isNamed()) return false
if (argument.getArgumentExpression() == null) return false
if (parent is JetDotQualifiedExpression) {
val typeArguments = element.getTypeArgumentList()
val valueArguments = element.getValueArgumentList()
val functionLiteralArguments = element.getFunctionLiteralArguments()
val numOfTotalValueArguments = (valueArguments?.getArguments()?.size() ?: 0) + functionLiteralArguments.size()
val receiver = dotQualified.getReceiverExpression()
if (element.analyze().getType(receiver) == null) return false
if (typeArguments?.getArguments()?.size() ?: 0 == 0 &&
numOfTotalValueArguments == 1) {
if (valueArguments?.getArguments()?.size() == 1 && valueArguments?.getArguments()?.first()?.isNamed() ?: false) {
val file = element.getContainingJetFile()
val bindingContext = file.analyzeFully()
val resolvedCall = element.getResolvedCall(bindingContext)
val valueArgumentsMap = resolvedCall?.getValueArguments()
val firstArgument = valueArguments?.getArguments()?.first()
return valueArgumentsMap?.keySet()?.any { it.getName().asString() == firstArgument?.getArgumentName()?.getText() && it.getIndex() == 0 } ?: false
} else {
return true
}
} else {
return false
}
} else {
return false
}
}
open fun intentionFailed(editor: Editor, messageID: String) {
val message = "Intention failed: ${JetBundle.message("replace.with.infix.function.call.intention.error.$messageID")}"
HintManager.getInstance().showErrorHint(editor, message)
return true
}
override fun applyTo(element: JetCallExpression, editor: Editor) {
val parent = element.getParent() as JetDotQualifiedExpression
val receiver = parent.getReceiverExpression()
val leftHandText = parent.getReceiverExpression().getText()
val rightHandTextStringBuilder = StringBuilder()
val dotQualified = element.getParent() as JetDotQualifiedExpression
val receiver = dotQualified.getReceiverExpression()
val operatorText = element.getCalleeExpression()!!.getText()
val valueArguments = element.getValueArgumentList()?.getArguments() ?: listOf<JetValueArgument>()
val functionLiteralArguments = element.getFunctionLiteralArguments()
val bindingContext = parent.analyze()
val receiverType = bindingContext.getType(receiver)
if (receiverType == null) {
if (bindingContext[BindingContext.QUALIFIER, receiver] != null) {
intentionFailed(editor, "package.call")
return
}
intentionFailed(editor, "resolution.failed")
return
}
rightHandTextStringBuilder.append(
if (valueArguments.size() > 0)
JetPsiUnparsingUtils.parenthesizeIfNeeded(valueArguments.first().getArgumentExpression())
else
functionLiteralArguments.first().getText()
)
val newCall = JetPsiFactory(element).createExpression("${receiver.getText()} $operatorText x") as JetBinaryExpression
val argument = element.getValueArguments().single()
newCall.getRight()!!.replace(argument.getArgumentExpression()!!)
val replacement = JetPsiFactory(element).createExpression("$leftHandText $operatorText ${rightHandTextStringBuilder.toString()}")
parent.replace(replacement)
dotQualified.replace(newCall)
}
}
@@ -1 +1 @@
org.jetbrains.kotlin.idea.intentions.TestableReplaceWithInfixFunctionCallIntention
org.jetbrains.kotlin.idea.intentions.ReplaceWithInfixFunctionCallIntention
@@ -1,3 +1,3 @@
fun foo(x: Int) {
x compareTo (1 + 2)
x compareTo 1 + 2
}
@@ -1,11 +0,0 @@
fun doSomething<T>(a: T) {}
class Foo {
fun foo(x: Int) {
doSomething("lol")
}
}
fun bar(baz: Foo) {
baz.<caret>foo(x = 1)
}
@@ -1,11 +0,0 @@
fun doSomething<T>(a: T) {}
class Foo {
fun foo(x: Int) {
doSomething("lol")
}
}
fun bar(baz: Foo) {
baz foo 1
}
@@ -1,5 +1,5 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
// SHOULD_FAIL_WITH: package.call
fun main() {
kotlin.io.<caret>println("")
}
@@ -5393,12 +5393,6 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("firstParameterLabeled.kt")
public void testFirstParameterLabeled() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/replaceWithInfixFunctionCall/firstParameterLabeled.kt");
doTest(fileName);
}
@TestMetadata("functionLiteralArgument.kt")
public void testFunctionLiteralArgument() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/replaceWithInfixFunctionCall/functionLiteralArgument.kt");
@@ -1,25 +0,0 @@
/*
* Copyright 2010-2015 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.openapi.editor.Editor
public class TestableReplaceWithInfixFunctionCallIntention : ReplaceWithInfixFunctionCallIntention() {
override fun intentionFailed(editor: Editor, messageID: String) {
throw IntentionTestException(messageID)
}
}