From d383e03a8190fbf91853eae78fdf678042529a43 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Thu, 16 Apr 2015 20:18:56 +0300 Subject: [PATCH] Automatic renaming top-level overloads. #KT-4642 in progress --- idea/src/META-INF/plugin.xml | 1 + .../rename/AutomaticOverloadsRenamer.kt | 67 +++++++++++++++++++ .../after/package.kt | 16 +++++ .../after/packageExtra.kt | 4 ++ .../before/package.kt | 16 +++++ .../before/packageExtra.kt | 4 ++ .../automaticRenamerOverloads/package.test | 7 ++ .../refactoring/rename/AbstractRenameTest.kt | 39 ++++++----- .../rename/RenameTestGenerated.java | 6 ++ 9 files changed, 145 insertions(+), 15 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticOverloadsRenamer.kt create mode 100644 idea/testData/refactoring/rename/automaticRenamerOverloads/after/package.kt create mode 100644 idea/testData/refactoring/rename/automaticRenamerOverloads/after/packageExtra.kt create mode 100644 idea/testData/refactoring/rename/automaticRenamerOverloads/before/package.kt create mode 100644 idea/testData/refactoring/rename/automaticRenamerOverloads/before/packageExtra.kt create mode 100644 idea/testData/refactoring/rename/automaticRenamerOverloads/package.test diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 47866545ecb..b470d601b60 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -307,6 +307,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticOverloadsRenamer.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticOverloadsRenamer.kt new file mode 100644 index 00000000000..0abb3648761 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticOverloadsRenamer.kt @@ -0,0 +1,67 @@ +/* + * 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.refactoring.rename + +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.roots.ProjectFileIndex +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.refactoring.JavaRefactoringSettings +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.rename.naming.AutomaticRenamer +import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.stubindex.JetTopLevelFunctionByPackageIndex +import org.jetbrains.kotlin.idea.stubindex.JetTopLevelFunctionFqnNameIndex +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetNamedFunction + +public class AutomaticOverloadsRenamer(function: JetNamedFunction, newName: String) : AutomaticRenamer() { + init { + val project = function.getProject() + val module = ProjectFileIndex.SERVICE.getInstance(project).getModuleForFile(function.getContainingFile().getVirtualFile()) + if (module != null) { + val searchScope = GlobalSearchScope.moduleScope(module) + val overloads = JetTopLevelFunctionFqnNameIndex.getInstance().get(function.getFqName()!!.asString(), project, searchScope) + for (overload in overloads) { + if (overload != function) { + myElements.add(overload) + } + } + } + suggestAllNames(function.getName(), newName) + } + + override fun getDialogTitle() = "Rename Overloads" + override fun getDialogDescription() = "Rename overloads to:" + override fun entityName() = "Overload" + override fun isSelectedByDefault(): Boolean = true +} + + +public class AutomaticOverloadsRenamerFactory : AutomaticRenamerFactory { + override fun isApplicable(element: PsiElement): Boolean { + return element is JetNamedFunction && element.getName() != null && element.getParent() is JetFile + } + + override fun getOptionName() = RefactoringBundle.message("rename.overloads") + override fun isEnabled() = JavaRefactoringSettings.getInstance().isRenameOverloads() + override fun setEnabled(enabled: Boolean) = JavaRefactoringSettings.getInstance().setRenameOverloads(enabled) + override fun createRenamer(element: PsiElement, newName: String, usages: Collection) + = AutomaticOverloadsRenamer(element as JetNamedFunction, newName) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/automaticRenamerOverloads/after/package.kt b/idea/testData/refactoring/rename/automaticRenamerOverloads/after/package.kt new file mode 100644 index 00000000000..2361ed25b9a --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerOverloads/after/package.kt @@ -0,0 +1,16 @@ +package testing + +fun bar() { +} + +fun bar(a: Int) { +} + +fun String.bar() { +} + +fun main(args: Array) { + bar() + bar(1) + "".bar() +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/automaticRenamerOverloads/after/packageExtra.kt b/idea/testData/refactoring/rename/automaticRenamerOverloads/after/packageExtra.kt new file mode 100644 index 00000000000..3c4c0557d34 --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerOverloads/after/packageExtra.kt @@ -0,0 +1,4 @@ +package testing + +fun bar(x: Int, y: Int) { +} diff --git a/idea/testData/refactoring/rename/automaticRenamerOverloads/before/package.kt b/idea/testData/refactoring/rename/automaticRenamerOverloads/before/package.kt new file mode 100644 index 00000000000..7d5847cdae4 --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerOverloads/before/package.kt @@ -0,0 +1,16 @@ +package testing + +fun foo() { +} + +fun foo(a: Int) { +} + +fun String.foo() { +} + +fun main(args: Array) { + foo() + foo(1) + "".foo() +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/automaticRenamerOverloads/before/packageExtra.kt b/idea/testData/refactoring/rename/automaticRenamerOverloads/before/packageExtra.kt new file mode 100644 index 00000000000..e9f673869f9 --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerOverloads/before/packageExtra.kt @@ -0,0 +1,4 @@ +package testing + +fun foo(x: Int, y: Int) { +} diff --git a/idea/testData/refactoring/rename/automaticRenamerOverloads/package.test b/idea/testData/refactoring/rename/automaticRenamerOverloads/package.test new file mode 100644 index 00000000000..889d5c9d1cb --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerOverloads/package.test @@ -0,0 +1,7 @@ +{ + "type": "KOTLIN_FUNCTION", + "packageFqn": "testing", + "oldName": "foo", + "newName": "bar", + "mainFile": "package.kt" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt index 995aa7ee85e..59be89555c9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt @@ -45,10 +45,10 @@ import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.psi.JetFile import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies import org.junit.Assert import java.io.File -import java.util.Collections private enum class RenameType { JAVA_CLASS @@ -137,23 +137,19 @@ public abstract class AbstractRenameTest : KotlinMultiFileTestCase() { private fun renameKotlinFunctionTest(renameParamsObject: JsonObject, context: TestContext) { val oldMethodName = Name.identifier(renameParamsObject.getString("oldName")) - doRenameInKotlinClass(renameParamsObject, context) { classDescriptor -> - val scope = classDescriptor.getMemberScope(Collections.emptyList()) - scope.getFunctions(oldMethodName).first() - } + doRenameInKotlinClassOrPackage(renameParamsObject, context) { it.getFunctions(oldMethodName).first() } } private fun renameKotlinPropertyTest(renameParamsObject: JsonObject, context: TestContext) { val oldPropertyName = Name.identifier(renameParamsObject.getString("oldName")) - doRenameInKotlinClass(renameParamsObject, context) { classDescriptor -> - val scope = classDescriptor.getMemberScope(Collections.emptyList()) - scope.getProperties(oldPropertyName).first() - } + doRenameInKotlinClassOrPackage(renameParamsObject, context) { it.getProperties(oldPropertyName).first() } } private fun renameKotlinClassTest(renameParamsObject: JsonObject, context: TestContext) { - doRenameInKotlinClass(renameParamsObject, context) { classDescriptor -> classDescriptor } + renameParamsObject.getString("classId") //assertion + + doRenameInKotlinClassOrPackage(renameParamsObject, context) { scope -> scope.getContainingDeclaration() as ClassDescriptor } } private fun renameKotlinPackageTest(renameParamsObject: JsonObject, context: TestContext) { @@ -179,10 +175,18 @@ public abstract class AbstractRenameTest : KotlinMultiFileTestCase() { } } - private fun doRenameInKotlinClass( - renameParamsObject: JsonObject, context: TestContext, findDescriptorToRename: (ClassDescriptor) -> DeclarationDescriptor + private fun doRenameInKotlinClassOrPackage( + renameParamsObject: JsonObject, context: TestContext, findDescriptorToRename: (JetScope) -> DeclarationDescriptor ) { - val classId = renameParamsObject.getString("classId").toClassId() + val classIdStr = renameParamsObject.getNullableString("classId") + val packageFqnStr = renameParamsObject.getNullableString("packageFqn") + if (classIdStr != null && packageFqnStr != null) { + throw AssertionError("Both classId and packageFqn are defined. Where should I search: in class or in package?") + } + else if (classIdStr == null && packageFqnStr == null) { + throw AssertionError("Define classId or packageFqn") + } + val newName = renameParamsObject.getString("newName") val mainFilePath = renameParamsObject.getNullableString("mainFile") ?: "${getTestDirName(false)}.kt" @@ -192,9 +196,14 @@ public abstract class AbstractRenameTest : KotlinMultiFileTestCase() { val jetFile = PsiDocumentManager.getInstance(context.project).getPsiFile(document) as JetFile val module = jetFile.analyzeFullyAndGetResult().moduleDescriptor - val classDescriptor = module.findClassAcrossModuleDependencies(classId)!! - val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(findDescriptorToRename(classDescriptor))!! + val scopeToSearch = if (classIdStr != null) { + module.findClassAcrossModuleDependencies(classIdStr.toClassId())!!.getDefaultType().getMemberScope() + } else { + module.getPackage(FqName(packageFqnStr!!))!!.getMemberScope() + } + + val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(findDescriptorToRename(scopeToSearch))!! val substitution = RenamePsiElementProcessor.forElement(psiElement).substituteElementToRename(psiElement, null) diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java index ffaf511cdfa..b9704e8c2f8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -47,6 +47,12 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest(fileName); } + @TestMetadata("automaticRenamerOverloads/package.test") + public void testAutomaticRenamerOverloads_Package() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/automaticRenamerOverloads/package.test"); + doTest(fileName); + } + @TestMetadata("defaultObject/defaultObject.test") public void testDefaultObject_DefaultObject() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/defaultObject/defaultObject.test");