Automatic renaming top-level overloads.

#KT-4642 in progress
This commit is contained in:
Evgeny Gerashchenko
2015-04-16 20:18:56 +03:00
parent 8fe3a1d366
commit d383e03a81
9 changed files with 145 additions and 15 deletions
+1
View File
@@ -307,6 +307,7 @@
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameOnSecondaryConstructorHandler"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticVariableRenamerFactory"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticInheritorRenamerFactory"/>
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.AutomaticOverloadsRenamerFactory"/>
<spellchecker.support implementationClass="org.jetbrains.kotlin.idea.KotlinSpellcheckingStrategy" language="jet"/>
@@ -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<UsageInfo>)
= AutomaticOverloadsRenamer(element as JetNamedFunction, newName)
}
@@ -0,0 +1,16 @@
package testing
fun bar() {
}
fun bar(a: Int) {
}
fun String.bar() {
}
fun main(args: Array<String>) {
bar()
bar(1)
"".bar()
}
@@ -0,0 +1,4 @@
package testing
fun bar(x: Int, y: Int) {
}
@@ -0,0 +1,16 @@
package testing
fun foo() {
}
fun foo(a: Int) {
}
fun String.foo() {
}
fun main(args: Array<String>) {
foo()
foo(1)
"".foo()
}
@@ -0,0 +1,4 @@
package testing
fun foo(x: Int, y: Int) {
}
@@ -0,0 +1,7 @@
{
"type": "KOTLIN_FUNCTION",
"packageFqn": "testing",
"oldName": "foo",
"newName": "bar",
"mainFile": "package.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)
@@ -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");