diff --git a/annotations/com/intellij/refactoring/rename/annotations.xml b/annotations/com/intellij/refactoring/rename/annotations.xml new file mode 100644 index 00000000000..7c596a8c959 --- /dev/null +++ b/annotations/com/intellij/refactoring/rename/annotations.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 3a7073cdcc5..3350695ee65 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -216,7 +216,9 @@ - + diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.kt index 9db91bf4d33..8cf2f29b700 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/rename/RenameKotlinFunctionProcessor.kt @@ -16,34 +16,60 @@ package org.jetbrains.jet.plugin.refactoring.rename; -import com.intellij.openapi.editor.Editor; -import com.intellij.psi.PsiCompiledElement; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.search.SearchScope; -import com.intellij.refactoring.rename.RenamePsiElementProcessor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.asJava.KotlinLightClass; -import org.jetbrains.jet.lang.psi.JetFunction; +import com.intellij.openapi.editor.Editor +import com.intellij.psi.PsiCompiledElement +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.refactoring.rename.RenamePsiElementProcessor +import com.intellij.psi.search.SearchScope +import com.intellij.psi.search.searches.OverridingMethodsSearch +import com.intellij.openapi.application.ApplicationManager +import org.jetbrains.jet.asJava.LightClassUtil +import org.jetbrains.jet.lang.psi.JetNamedFunction +import com.intellij.openapi.util.Computable +import com.intellij.psi.PsiMirrorElement +import com.intellij.psi.SyntheticElement +import com.intellij.refactoring.util.RefactoringUtil +import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.refactoring.rename.RenameJavaMethodProcessor +import com.intellij.psi.PsiNamedElement +import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod +import kotlin.properties.Delegates -import java.util.Map; +public class RenameKotlinFunctionProcessor : RenamePsiElementProcessor() { + private val javaMethodProcessorInstance by Delegates.lazy { + // KT-4250 + // RenamePsiElementProcessor.EP_NAME.findExtension(javaClass())!! -public class RenameKotlinFunctionProcessor extends RenamePsiElementProcessor { - @Override - public boolean canProcessElement(@NotNull PsiElement element) { - if (element instanceof PsiMethod && ((PsiMethod) element).getContainingClass() instanceof KotlinLightClass) { - return true; - } - return element instanceof JetFunction; + RenameJavaMethodProcessor() } - @Override - public PsiElement substituteElementToRename(PsiElement element, @Nullable Editor editor) { - if (element instanceof PsiMethod && element instanceof PsiCompiledElement && - ((PsiMethod) element).getContainingClass() instanceof KotlinLightClass) { - return ((PsiCompiledElement) element).getMirror(); + override fun canProcessElement(element: PsiElement): Boolean { + return element is JetNamedFunction || element is KotlinLightMethod + } + + override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? { + val wrappedMethod = wrapPsiMethod(element) + if (wrappedMethod == null) { + // Cancel rename + return null } - return super.substituteElementToRename(element, editor); + + // Use java dialog to ask we should rename function with the base element + return unwrapPsiMethod(javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor)) + } + + override fun prepareRenaming(element: PsiElement?, newName: String?, allRenames: MutableMap, scope: SearchScope) { + javaMethodProcessorInstance.prepareRenaming(wrapPsiMethod(element), newName, allRenames, scope) + } + + private fun wrapPsiMethod(element: PsiElement?): PsiMethod? = when (element) { + is KotlinLightMethod -> element + is JetNamedFunction -> ApplicationManager.getApplication()!!.runReadAction(Computable { LightClassUtil.getLightClassMethod(element) }) + else -> throw IllegalStateException("Can't be for element $element there because of canProcessElement()") + } + + private fun unwrapPsiMethod(element: PsiElement?): JetNamedFunction? { + return (element as? KotlinLightMethod)?.getOrigin() as? JetNamedFunction } } diff --git a/idea/testData/refactoring/rename/renameKotlinBaseMethod/after/RenameKotlinBaseMethod.kt b/idea/testData/refactoring/rename/renameKotlinBaseMethod/after/RenameKotlinBaseMethod.kt new file mode 100644 index 00000000000..0523cfb4116 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinBaseMethod/after/RenameKotlinBaseMethod.kt @@ -0,0 +1,27 @@ +package testing.rename + +trait A { + fun second() : Int +} + +public open class B: A { + override fun second() = 1 + + fun first(a: Int) = 12 +} + +class C: B() { + override fun second() = 2 +} + +fun usages() { + val b = B() + val a: A = b + val c = C() + + a.second() + b.second() + c.second() +} + + diff --git a/idea/testData/refactoring/rename/renameKotlinBaseMethod/after/testing/JavaClient.java b/idea/testData/refactoring/rename/renameKotlinBaseMethod/after/testing/JavaClient.java new file mode 100644 index 00000000000..4908268f958 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinBaseMethod/after/testing/JavaClient.java @@ -0,0 +1,26 @@ +package testing; + +import testing.rename.*; + +class JavaClient { + public void foo(A a) { + a.second(); + new B().second(); + new C().second(); + new D().second(); + } + + public static class D implements A { + @Override + public int second() { + return 3; + } + } + + public static class E extends D { + @Override + public int second() { + return 4; + } + } +} diff --git a/idea/testData/refactoring/rename/renameKotlinBaseMethod/before/RenameKotlinBaseMethod.kt b/idea/testData/refactoring/rename/renameKotlinBaseMethod/before/RenameKotlinBaseMethod.kt new file mode 100644 index 00000000000..7e5dcc2a4cc --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinBaseMethod/before/RenameKotlinBaseMethod.kt @@ -0,0 +1,27 @@ +package testing.rename + +trait A { + fun first() : Int +} + +public open class B: A { + override fun first() = 1 + + fun first(a: Int) = 12 +} + +class C: B() { + override fun first() = 2 +} + +fun usages() { + val b = B() + val a: A = b + val c = C() + + a.first() + b.first() + c.first() +} + + diff --git a/idea/testData/refactoring/rename/renameKotlinBaseMethod/before/testing/JavaClient.java b/idea/testData/refactoring/rename/renameKotlinBaseMethod/before/testing/JavaClient.java new file mode 100644 index 00000000000..b10d6a38285 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinBaseMethod/before/testing/JavaClient.java @@ -0,0 +1,26 @@ +package testing; + +import testing.rename.*; + +class JavaClient { + public void foo(A a) { + a.first(); + new B().first(); + new C().first(); + new D().first(); + } + + public static class D implements A { + @Override + public int first() { + return 3; + } + } + + public static class E extends D { + @Override + public int first() { + return 4; + } + } +} diff --git a/idea/testData/refactoring/rename/renameKotlinBaseMethod/javaWrapperForBaseFunction.test b/idea/testData/refactoring/rename/renameKotlinBaseMethod/javaWrapperForBaseFunction.test new file mode 100644 index 00000000000..d9c8385f1b4 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinBaseMethod/javaWrapperForBaseFunction.test @@ -0,0 +1 @@ +// RENAME: JAVA_METHOD->testing.rename.A->void first()->second \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameKotlinBaseMethod/kotlinBaseFunction.test b/idea/testData/refactoring/rename/renameKotlinBaseMethod/kotlinBaseFunction.test new file mode 100644 index 00000000000..6b1ffbd52d5 --- /dev/null +++ b/idea/testData/refactoring/rename/renameKotlinBaseMethod/kotlinBaseFunction.test @@ -0,0 +1,3 @@ +// RENAME: KOTLIN_FUNCTION->testing.rename.A.first->second + +// KT-2836 Rename method with all its implementations \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameTestGenerated.java index fadc48f51f2..a498481f707 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/rename/RenameTestGenerated.java @@ -41,6 +41,16 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest("idea/testData/refactoring/rename/renameJavaClass/renameJavaClass.test"); } + @TestMetadata("renameKotlinBaseMethod/javaWrapperForBaseFunction.test") + public void testRenameKotlinBaseMethod_JavaWrapperForBaseFunction() throws Exception { + doTest("idea/testData/refactoring/rename/renameKotlinBaseMethod/javaWrapperForBaseFunction.test"); + } + + @TestMetadata("renameKotlinBaseMethod/kotlinBaseFunction.test") + public void testRenameKotlinBaseMethod_KotlinBaseFunction() throws Exception { + doTest("idea/testData/refactoring/rename/renameKotlinBaseMethod/kotlinBaseFunction.test"); + } + @TestMetadata("renameKotlinClass/javaWrapperForKotlinClass.test") public void testRenameKotlinClass_JavaWrapperForKotlinClass() throws Exception { doTest("idea/testData/refactoring/rename/renameKotlinClass/javaWrapperForKotlinClass.test");