Rename: Make improvements for "by-convention" calls
- Convert between conventions call for 'get' and 'invoke' - Drop 'operator' when converting 'get'/'invoke' to something other than 'invoke'/'get' respectively #KT-12365 Fixed
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.psi;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
@@ -32,6 +33,9 @@ import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinStubWithFqName;
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.kotlin.psi.KtPsiFactoryKt.KtPsiFactory;
|
||||
|
||||
@@ -78,16 +82,26 @@ abstract class KtNamedDeclarationStub<T extends KotlinStubWithFqName<?>> extends
|
||||
return findChildByType(KtTokens.IDENTIFIER);
|
||||
}
|
||||
|
||||
private static final Set<String> FUNCTIONLIKE_CONVENTIONS = ImmutableSet.of(
|
||||
OperatorNameConventions.INVOKE.asString(),
|
||||
OperatorNameConventions.GET.asString()
|
||||
);
|
||||
|
||||
private static boolean shouldDropOperatorKeyword(String oldName, String newName) {
|
||||
return !OperatorConventions.isConventionName(Name.identifier(newName)) ||
|
||||
FUNCTIONLIKE_CONVENTIONS.contains(oldName) != FUNCTIONLIKE_CONVENTIONS.contains(newName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
|
||||
PsiElement identifier = getNameIdentifier();
|
||||
if (identifier == null) return null;
|
||||
|
||||
KtModifierList modifierList = getModifierList();
|
||||
if (modifierList != null &&
|
||||
modifierList.hasModifier(KtTokens.OPERATOR_KEYWORD) &&
|
||||
!OperatorConventions.isConventionName(Name.identifier(name))) {
|
||||
removeModifier(KtTokens.OPERATOR_KEYWORD);
|
||||
if (modifierList != null && modifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) {
|
||||
if (shouldDropOperatorKeyword(getName(), name)) {
|
||||
removeModifier(KtTokens.OPERATOR_KEYWORD);
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement newIdentifier = KtPsiFactory(this).createNameIdentifierIfPossible(name);
|
||||
|
||||
+17
-2
@@ -27,8 +27,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
|
||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression;
|
||||
import org.jetbrains.kotlin.psi.KtContainerNode;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
@@ -98,6 +97,22 @@ public class KtArrayAccessReference extends KtSimpleReference<KtArrayAccessExpre
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement handleElementRename(@Nullable String newElementName) {
|
||||
KtArrayAccessExpression arrayAccessExpression = getExpression();
|
||||
if (OperatorNameConventions.INVOKE.asString().equals(newElementName)) {
|
||||
KtExpression callExpression = CreateByPatternKt.buildExpression(
|
||||
new KtPsiFactory(arrayAccessExpression.getProject()),
|
||||
true,
|
||||
pattern -> {
|
||||
pattern.appendExpression(arrayAccessExpression.getArrayExpression());
|
||||
pattern.appendFixedText("(");
|
||||
pattern.appendExpressions(arrayAccessExpression.getIndexExpressions(), ",");
|
||||
pattern.appendFixedText(")");
|
||||
return null;
|
||||
}
|
||||
);
|
||||
return arrayAccessExpression.replace(callExpression);
|
||||
}
|
||||
|
||||
return ReferenceUtilKt.renameImplicitConventionalCall(this, newElementName);
|
||||
}
|
||||
|
||||
|
||||
+15
-4
@@ -23,9 +23,7 @@ import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.Call
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.unpackFunctionLiteral
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
@@ -95,7 +93,20 @@ class KtInvokeFunctionReference(expression: KtCallExpression) : KtSimpleReferenc
|
||||
|
||||
override fun canRename(): Boolean = true
|
||||
|
||||
override fun handleElementRename(newElementName: String?): PsiElement? = renameImplicitConventionalCall(newElementName)
|
||||
override fun handleElementRename(newElementName: String?): PsiElement? {
|
||||
val callExpression = expression
|
||||
if (newElementName == OperatorNameConventions.GET.asString() && callExpression.typeArguments.isEmpty()) {
|
||||
val arrayAccessExpression = KtPsiFactory(callExpression).buildExpression {
|
||||
appendExpression(callExpression.calleeExpression)
|
||||
appendFixedText("[")
|
||||
appendExpressions(callExpression.valueArguments.map { it.getArgumentExpression() })
|
||||
appendFixedText("]")
|
||||
}
|
||||
return callExpression.replace(arrayAccessExpression)
|
||||
}
|
||||
|
||||
return renameImplicitConventionalCall(newElementName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun <caret>get(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()[1, "2"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
fun foo(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A().foo(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun <caret>get(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()[1, "2"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun invoke(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun <caret>get(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()[1, "2"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
fun plus(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A().plus(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun <caret>invoke(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
fun foo(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A().foo(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun <caret>invoke(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun get(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()[1, "2"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
operator fun <caret>invoke(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A()(1, "2")
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
fun plus(n: Int, s: String) = 1
|
||||
}
|
||||
|
||||
fun test() {
|
||||
A().plus(1, "2")
|
||||
}
|
||||
@@ -83,7 +83,31 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
|
||||
}
|
||||
|
||||
fun testNoReformat() {
|
||||
doTestInplaceRename("subject2", MemberInplaceRenameHandler())
|
||||
doTestMemberInplaceRename("subject2")
|
||||
}
|
||||
|
||||
fun testInvokeToFoo() {
|
||||
doTestMemberInplaceRename("foo")
|
||||
}
|
||||
|
||||
fun testInvokeToGet() {
|
||||
doTestMemberInplaceRename("get")
|
||||
}
|
||||
|
||||
fun testInvokeToPlus() {
|
||||
doTestMemberInplaceRename("plus")
|
||||
}
|
||||
|
||||
fun testGetToFoo() {
|
||||
doTestMemberInplaceRename("foo")
|
||||
}
|
||||
|
||||
fun testGetToInvoke() {
|
||||
doTestMemberInplaceRename("invoke")
|
||||
}
|
||||
|
||||
fun testGetToPlus() {
|
||||
doTestMemberInplaceRename("plus")
|
||||
}
|
||||
|
||||
private fun doTestImplicitLambdaParameter(newName: String) {
|
||||
@@ -136,6 +160,10 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
|
||||
checkResultByFile(getTestName(false) + ".kt.after")
|
||||
}
|
||||
|
||||
private fun doTestMemberInplaceRename(newName: String?) {
|
||||
doTestInplaceRename(newName, MemberInplaceRenameHandler())
|
||||
}
|
||||
|
||||
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = VariableInplaceRenameHandler()) {
|
||||
configureByFile(getTestName(false) + ".kt")
|
||||
val element = TargetElementUtil.findTargetElement(
|
||||
|
||||
Reference in New Issue
Block a user