Automatic renaming parameter in hierarchy.

This commit is contained in:
Evgeny Gerashchenko
2015-04-22 18:45:25 +03:00
parent 33f5158170
commit 1e46469bb1
13 changed files with 169 additions and 8 deletions
+3
View File
@@ -312,6 +312,9 @@
<renamePsiElementProcessor implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinPropertyProcessor"
id="KotlinProperty"
order="first"/>
<renamePsiElementProcessor implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinParameterProcessor"
id="KotlinParameter"
order="first"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinImplicitLambdaParameter"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameDynamicMemberHandler"/>
<renameHandler implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameOnSecondaryConstructorHandler"/>
@@ -54,6 +54,7 @@ import org.jetbrains.kotlin.idea.core.refactoring.RefactoringPackage;
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.*;
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference;
import org.jetbrains.kotlin.idea.search.usagesSearch.UsagesSearchPackage;
import org.jetbrains.kotlin.kdoc.psi.impl.KDocName;
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
@@ -73,7 +74,10 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.TypeUtils;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsageProcessor {
@Override
@@ -172,11 +176,11 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
for (PsiReference reference : ReferencesSearch.search(oldParam, oldParam.getUseScope())) {
PsiElement element = reference.getElement();
if (element instanceof JetSimpleNameExpression &&
if ((element instanceof JetSimpleNameExpression || element instanceof KDocName) &&
!(element.getParent() instanceof JetValueArgumentName)) // Usages in named arguments of the calls usage will be changed when the function call is changed
{
JetParameterUsage parameterUsage =
new JetParameterUsage((JetSimpleNameExpression) element, parameterInfo, functionUsageInfo);
new JetParameterUsage((JetElement) element, parameterInfo, functionUsageInfo);
result.add(parameterUsage);
}
}
@@ -446,6 +450,8 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
String newExprText = ((JetParameterUsage) usageInfo).getReplacementText(changeInfo);
if (!newExprText.startsWith("this@")) continue;
if (usageInfo.getElement() instanceof KDocName) continue; // TODO support converting parameter to receiver in KDoc
JetExpression originalExpr = (JetExpression) usageInfo.getElement();
JetScope scope = ResolvePackage.analyze(originalExpr, BodyResolveMode.FULL)
.get(BindingContext.RESOLUTION_SCOPE, originalExpr);
@@ -37,6 +37,10 @@ public class JetMutableMethodDescriptor(val original: JetMethodDescriptor): JetM
parameters.remove(index)
}
public fun renameParameter(index: Int, newName: String) {
parameters[index].setName(newName)
}
public fun clearParameters() {
parameters.clear()
receiver = null
@@ -43,10 +43,10 @@ public abstract class JetExplicitReferenceUsage<T: JetElement>(element: T) : Jet
}
public class JetParameterUsage(
element: JetSimpleNameExpression,
element: JetElement,
private val parameterInfo: JetParameterInfo,
val containingFunction: JetFunctionDefinitionUsage<*>
) : JetExplicitReferenceUsage<JetSimpleNameExpression>(element) {
) : JetExplicitReferenceUsage<JetElement>(element) {
override fun processReplacedElement(element: JetElement) {
val qualifiedExpression = element.getParent() as? JetQualifiedExpression
val elementToShorten = if (qualifiedExpression?.getReceiverExpression() == element) qualifiedExpression!! else element
@@ -0,0 +1,56 @@
/*
* 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.psi.PsiElement
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeSignatureConfiguration
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetMethodDescriptor
import org.jetbrains.kotlin.idea.refactoring.changeSignature.modify
import org.jetbrains.kotlin.idea.refactoring.changeSignature.runChangeSignature
import org.jetbrains.kotlin.psi.JetNamedFunction
import org.jetbrains.kotlin.psi.JetParameter
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.BindingContext
public class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
return element is JetParameter && element.getParent().getParent() is JetNamedFunction
}
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
val function = (element as JetParameter).getParent().getParent() as JetNamedFunction
val paramIndex = function.getValueParameters().indexOf(element)
assert(paramIndex != -1, { "couldn't find parameter in parent ${element.getElementTextWithContext()}" })
val context = function.analyze()
val descriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, function] as? FunctionDescriptor ?: return
val changeSignatureConfiguration = object : JetChangeSignatureConfiguration {
override fun configure(originalDescriptor: JetMethodDescriptor, bindingContext: BindingContext): JetMethodDescriptor {
return originalDescriptor.modify { renameParameter(paramIndex, newName) }
}
override fun performSilently(affectedFunctions: Collection<PsiElement>) = true
}
runChangeSignature(element.getProject(), descriptor, changeSignatureConfiguration, context, element, "Rename parameter")
}
}
@@ -0,0 +1,4 @@
class JavaSub extends testing.Middle {
void foo(int aa, String b) {
}
}
@@ -0,0 +1,21 @@
package testing
trait Trait {
open fun foo(aa: Int, b: String) {
}
}
open class Super {
open fun foo(aa: Int, b: String) {
}
}
open class Middle : Super(), Trait {
override fun foo(/*rename*/aa: Int, b: String) {
}
}
class Sub : Middle() {
override fun foo(aa: Int, b: String) {
}
}
@@ -0,0 +1,4 @@
class JavaSub extends testing.Middle {
void foo(int a, String b) {
}
}
@@ -0,0 +1,21 @@
package testing
trait Trait {
open fun foo(a: Int, b: String) {
}
}
open class Super {
open fun foo(a: Int, b: String) {
}
}
open class Middle : Super(), Trait {
override fun foo(/*rename*/a: Int, b: String) {
}
}
class Sub : Middle() {
override fun foo(a: Int, b: String) {
}
}
@@ -0,0 +1,5 @@
{
"type": "MARKED_ELEMENT",
"newName": "aa",
"mainFile": "main.kt"
}
@@ -17,6 +17,8 @@
package org.jetbrains.kotlin.idea.kdoc;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.ide.startup.impl.StartupManagerImpl;
import com.intellij.openapi.startup.StartupManager;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.rename.RenameProcessor;
import com.intellij.testFramework.LightCodeInsightTestCase;
@@ -38,6 +40,12 @@ public class KdocRenameTest extends LightCodeInsightTestCase {
doTest("xyzzy");
}
@Override
protected void setUp() throws Exception {
super.setUp();
((StartupManagerImpl) StartupManager.getInstance(getProject())).runPostStartupActivities();
}
private void doTest(String newName) throws Exception {
configureByFile(getTestName(false) + ".kt");
PsiElement element = TargetElementUtilBase
@@ -24,10 +24,9 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
import com.intellij.refactoring.rename.RenameProcessor
@@ -44,6 +43,9 @@ import org.jetbrains.kotlin.idea.test.KotlinMultiFileTestCase
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
@@ -57,6 +59,7 @@ private enum class RenameType {
KOTLIN_FUNCTION,
KOTLIN_PROPERTY,
KOTLIN_PACKAGE
MARKED_ELEMENT
}
public abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
@@ -85,6 +88,7 @@ public abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
RenameType.KOTLIN_FUNCTION -> renameKotlinFunctionTest(renameObject, context)
RenameType.KOTLIN_PROPERTY -> renameKotlinPropertyTest(renameObject, context)
RenameType.KOTLIN_PACKAGE -> renameKotlinPackageTest(renameObject, context)
RenameType.MARKED_ELEMENT -> renameMarkedElement(renameObject, context)
}
if (hintDirective != null) {
@@ -104,6 +108,25 @@ public abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
}
}
private fun renameMarkedElement(renameParamsObject: JsonObject, context: TestContext) {
val mainFilePath = renameParamsObject.getString("mainFile")
val newName = renameParamsObject.getString("newName")
doTestCommittingDocuments { rootDir, rootAfter ->
val mainFile = rootDir.findChild(mainFilePath)!!
val psiFile = PsiManager.getInstance(context.project).findFile(mainFile)!!
val MARKER_TEXT = "/*rename*/"
val marker = psiFile.getText().indexOf(MARKER_TEXT)
assert(marker != -1)
val toRename = psiFile.findElementAt(marker + MARKER_TEXT.length())!!.getNonStrictParentOfType<PsiNamedElement>()!!
val substitution = RenamePsiElementProcessor.forElement(toRename).substituteElementToRename(toRename, null)
runRenameProcessor(context, newName, substitution, true, true)
}
}
private fun renameJavaClassTest(renameParamsObject: JsonObject, context: TestContext) {
val classFQN = renameParamsObject.getString("classId").toClassId().asSingleFqName().asString()
val newName = renameParamsObject.getString("newName")
@@ -71,6 +71,12 @@ public class RenameTestGenerated extends AbstractRenameTest {
doTest(fileName);
}
@TestMetadata("automaticRenamerParameter/parameter.test")
public void testAutomaticRenamerParameter_Parameter() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/automaticRenamerParameter/parameter.test");
doTest(fileName);
}
@TestMetadata("defaultObject/defaultObject.test")
public void testDefaultObject_DefaultObject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/rename/defaultObject/defaultObject.test");