diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index c9e13e9909a..8a2dc760437 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -312,6 +312,9 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java index 0d304958d26..81ed3c045ed 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetChangeSignatureUsageProcessor.java @@ -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); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetMutableMethodDescriptor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetMutableMethodDescriptor.kt index d489c882943..30529227ac2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetMutableMethodDescriptor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/JetMutableMethodDescriptor.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt index 8661ed8f788..cb98aea4bbb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/JetParameterUsage.kt @@ -43,10 +43,10 @@ public abstract class JetExplicitReferenceUsage(element: T) : Jet } public class JetParameterUsage( - element: JetSimpleNameExpression, + element: JetElement, private val parameterInfo: JetParameterInfo, val containingFunction: JetFunctionDefinitionUsage<*> -) : JetExplicitReferenceUsage(element) { +) : JetExplicitReferenceUsage(element) { override fun processReplacedElement(element: JetElement) { val qualifiedExpression = element.getParent() as? JetQualifiedExpression val elementToShorten = if (qualifiedExpression?.getReceiverExpression() == element) qualifiedExpression!! else element diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt new file mode 100644 index 00000000000..97d401e9f8e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinParameterProcessor.kt @@ -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, 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) = true + } + + runChangeSignature(element.getProject(), descriptor, changeSignatureConfiguration, context, element, "Rename parameter") + } +} diff --git a/idea/testData/refactoring/rename/automaticRenamerParameter/after/JavaSub.java b/idea/testData/refactoring/rename/automaticRenamerParameter/after/JavaSub.java new file mode 100644 index 00000000000..de19f2acd7b --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerParameter/after/JavaSub.java @@ -0,0 +1,4 @@ +class JavaSub extends testing.Middle { + void foo(int aa, String b) { + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/automaticRenamerParameter/after/main.kt b/idea/testData/refactoring/rename/automaticRenamerParameter/after/main.kt new file mode 100644 index 00000000000..d74c10cf373 --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerParameter/after/main.kt @@ -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) { + } +} diff --git a/idea/testData/refactoring/rename/automaticRenamerParameter/before/JavaSub.java b/idea/testData/refactoring/rename/automaticRenamerParameter/before/JavaSub.java new file mode 100644 index 00000000000..5d4de8e9d5d --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerParameter/before/JavaSub.java @@ -0,0 +1,4 @@ +class JavaSub extends testing.Middle { + void foo(int a, String b) { + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/automaticRenamerParameter/before/main.kt b/idea/testData/refactoring/rename/automaticRenamerParameter/before/main.kt new file mode 100644 index 00000000000..4bdaae4e277 --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerParameter/before/main.kt @@ -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) { + } +} diff --git a/idea/testData/refactoring/rename/automaticRenamerParameter/parameter.test b/idea/testData/refactoring/rename/automaticRenamerParameter/parameter.test new file mode 100644 index 00000000000..13a6d7a6494 --- /dev/null +++ b/idea/testData/refactoring/rename/automaticRenamerParameter/parameter.test @@ -0,0 +1,5 @@ +{ + "type": "MARKED_ELEMENT", + "newName": "aa", + "mainFile": "main.kt" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java index 00f3363b1cb..a7f62c4e114 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/kdoc/KdocRenameTest.java @@ -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 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 0cf8396a1f3..6a53891063a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt @@ -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()!! + 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") 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 08a592326b9..314efadff37 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -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");