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;
|
package org.jetbrains.kotlin.psi;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.search.LocalSearchScope;
|
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.psiUtil.KtPsiUtilKt;
|
||||||
import org.jetbrains.kotlin.psi.stubs.KotlinStubWithFqName;
|
import org.jetbrains.kotlin.psi.stubs.KotlinStubWithFqName;
|
||||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions;
|
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;
|
import static org.jetbrains.kotlin.psi.KtPsiFactoryKt.KtPsiFactory;
|
||||||
|
|
||||||
@@ -78,16 +82,26 @@ abstract class KtNamedDeclarationStub<T extends KotlinStubWithFqName<?>> extends
|
|||||||
return findChildByType(KtTokens.IDENTIFIER);
|
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
|
@Override
|
||||||
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
|
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
|
||||||
PsiElement identifier = getNameIdentifier();
|
PsiElement identifier = getNameIdentifier();
|
||||||
if (identifier == null) return null;
|
if (identifier == null) return null;
|
||||||
|
|
||||||
KtModifierList modifierList = getModifierList();
|
KtModifierList modifierList = getModifierList();
|
||||||
if (modifierList != null &&
|
if (modifierList != null && modifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) {
|
||||||
modifierList.hasModifier(KtTokens.OPERATOR_KEYWORD) &&
|
if (shouldDropOperatorKeyword(getName(), name)) {
|
||||||
!OperatorConventions.isConventionName(Name.identifier(name))) {
|
removeModifier(KtTokens.OPERATOR_KEYWORD);
|
||||||
removeModifier(KtTokens.OPERATOR_KEYWORD);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PsiElement newIdentifier = KtPsiFactory(this).createNameIdentifierIfPossible(name);
|
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.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens;
|
import org.jetbrains.kotlin.lexer.KtTokens;
|
||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression;
|
import org.jetbrains.kotlin.psi.*;
|
||||||
import org.jetbrains.kotlin.psi.KtContainerNode;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||||
@@ -98,6 +97,22 @@ public class KtArrayAccessReference extends KtSimpleReference<KtArrayAccessExpre
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public PsiElement handleElementRename(@Nullable String newElementName) {
|
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);
|
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.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.Call
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
|
||||||
import org.jetbrains.kotlin.psi.unpackFunctionLiteral
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
@@ -95,7 +93,20 @@ class KtInvokeFunctionReference(expression: KtCallExpression) : KtSimpleReferenc
|
|||||||
|
|
||||||
override fun canRename(): Boolean = true
|
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 {
|
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() {
|
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) {
|
private fun doTestImplicitLambdaParameter(newName: String) {
|
||||||
@@ -136,6 +160,10 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
|
|||||||
checkResultByFile(getTestName(false) + ".kt.after")
|
checkResultByFile(getTestName(false) + ".kt.after")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun doTestMemberInplaceRename(newName: String?) {
|
||||||
|
doTestInplaceRename(newName, MemberInplaceRenameHandler())
|
||||||
|
}
|
||||||
|
|
||||||
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = VariableInplaceRenameHandler()) {
|
private fun doTestInplaceRename(newName: String?, handler: VariableInplaceRenameHandler = VariableInplaceRenameHandler()) {
|
||||||
configureByFile(getTestName(false) + ".kt")
|
configureByFile(getTestName(false) + ".kt")
|
||||||
val element = TargetElementUtil.findTargetElement(
|
val element = TargetElementUtil.findTargetElement(
|
||||||
|
|||||||
Reference in New Issue
Block a user