Automatic renaming top-level overloads.
#KT-4642 in progress
This commit is contained in:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user