Rename kotlin properties with all usages

#KT-4317 Fixed
 #KT-4316 Fixed
This commit is contained in:
Nikolay Krasko
2014-03-28 14:05:45 +04:00
parent b779141845
commit 7c94e8a9af
24 changed files with 569 additions and 21 deletions
@@ -14,4 +14,8 @@
name='com.intellij.refactoring.rename.RenamePsiElementProcessor void prepareRenaming(com.intellij.psi.PsiElement, java.lang.String, java.util.Map<com.intellij.psi.PsiElement,java.lang.String>, com.intellij.psi.search.SearchScope) 3'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.refactoring.rename.RenamePsiElementProcessor void renameElement(com.intellij.psi.PsiElement, java.lang.String, com.intellij.usageView.UsageInfo[], com.intellij.refactoring.listeners.RefactoringElementListener) 2'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -0,0 +1,10 @@
<root>
<item
name='com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler boolean isAvailable(com.intellij.psi.PsiElement, com.intellij.openapi.editor.Editor, com.intellij.psi.PsiFile) 1'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item
name='com.intellij.refactoring.rename.inplace.VariableInplaceRenameHandler boolean isAvailable(com.intellij.psi.PsiElement, com.intellij.openapi.editor.Editor, com.intellij.psi.PsiFile) 2'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
</root>
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
* Copyright 2010-2014 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.
@@ -387,10 +387,12 @@ public class PropertyCodegen extends GenerationStateAware {
}
}
@NotNull
public static String getterName(Name propertyName) {
return JvmAbi.GETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
}
@NotNull
public static String setterName(Name propertyName) {
return JvmAbi.SETTER_PREFIX + StringUtil.capitalizeWithJavaBeanConvention(propertyName.asString());
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
* Copyright 2010-2014 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.
@@ -22,6 +22,7 @@ import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.FunctionDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.PropertyAccessorDescriptorImpl;
@@ -748,4 +749,15 @@ public class OverridingUtil {
}
return result;
}
@NotNull
@ReadOnly
public static <T extends CallableMemberDescriptor> Set<T> getDeepestSuperDeclarations(T functionDescriptor) {
Set<T> overriddenDeclarations = getAllOverriddenDeclarations(functionDescriptor);
if (overriddenDeclarations.isEmpty()) {
return Collections.singleton(functionDescriptor);
}
return filterOutOverriding(overriddenDeclarations);
}
}
+3
View File
@@ -240,6 +240,9 @@
<renamePsiElementProcessor implementation="org.jetbrains.jet.plugin.refactoring.rename.RenameKotlinFunctionProcessor"
id="KotlinFunction"
order="first"/>
<renamePsiElementProcessor implementation="org.jetbrains.jet.plugin.refactoring.rename.RenameKotlinPropertyProcessor"
id="KotlinProperty"
order="first"/>
<spellchecker.support implementationClass="org.jetbrains.jet.plugin.KotlinSpellcheckingStrategy" language="jet"/>
@@ -70,7 +70,7 @@ public class JetChangeSignature(val project: Project,
val closestModifiableDescriptors = getClosestModifiableDescriptors()
assert(!closestModifiableDescriptors.isEmpty(), "Should contain functionDescriptor itself or some of its super declarations")
val deepestSuperDeclarations = getDeepestSuperDeclarations()
val deepestSuperDeclarations = OverridingUtil.getDeepestSuperDeclarations(functionDescriptor)
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
showChangeSignatureDialog(deepestSuperDeclarations)
return
@@ -112,15 +112,6 @@ public class JetChangeSignature(val project: Project,
}
}
fun getDeepestSuperDeclarations(): Set<FunctionDescriptor> {
val overriddenDeclarations = OverridingUtil.getAllOverriddenDeclarations(functionDescriptor)
if (overriddenDeclarations.isEmpty()) {
return Collections.singleton(functionDescriptor)
}
return OverridingUtil.filterOutOverriding(overriddenDeclarations)
}
private fun showChangeSignatureDialog(descriptorsForSignatureChange: Collection<FunctionDescriptor>) {
val dialog = createChangeSignatureDialog(descriptorsForSignatureChange)
if (dialog == null) {
@@ -238,5 +229,5 @@ TestOnly public fun getChangeSignatureDialog(project: Project,
bindingContext: BindingContext,
defaultValueContext: PsiElement): JetChangeSignatureDialog? {
val jetChangeSignature = JetChangeSignature(project, functionDescriptor, configuration, bindingContext, defaultValueContext, null)
return jetChangeSignature.createChangeSignatureDialog(jetChangeSignature.getDeepestSuperDeclarations())
return jetChangeSignature.createChangeSignatureDialog(OverridingUtil.getDeepestSuperDeclarations(functionDescriptor))
}
@@ -0,0 +1,195 @@
/*
* Copyright 2010-2014 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.jet.plugin.refactoring.rename
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import com.intellij.psi.PsiElement
import org.jetbrains.jet.lang.psi.JetProperty
import com.intellij.psi.search.SearchScope
import com.intellij.openapi.application.ApplicationManager
import org.jetbrains.jet.asJava.LightClassUtil
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.openapi.util.Computable
import org.jetbrains.jet.asJava.LightClassUtil.PropertyAccessorsPsiMethods
import com.intellij.psi.PsiMethod
import com.intellij.psi.SyntheticElement
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.refactoring.rename.RenameProcessor
import org.jetbrains.jet.codegen.PropertyCodegen
import org.jetbrains.jet.lang.resolve.name.Name
import com.intellij.usageView.UsageInfo
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetClassOrObject
import com.intellij.openapi.ui.Messages
import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor
import org.jetbrains.jet.asJava.namedUnwrappedElement
public class RenameKotlinPropertyProcessor : RenamePsiElementProcessor() {
override fun canProcessElement(element: PsiElement): Boolean = element.namedUnwrappedElement is JetProperty
/* Can't properly update getters and setters in Java */
override fun isInplaceRenameSupported() = false
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
val jetProperty = element?.namedUnwrappedElement as? JetProperty
if (jetProperty == null) throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
val deepestSuperProperty = findDeepestOverriddenProperty(jetProperty)
if (deepestSuperProperty == null || deepestSuperProperty == jetProperty) {
return jetProperty
}
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
return deepestSuperProperty
}
val containsText: String? =
deepestSuperProperty.getFqName()?.parent()?.asString() ?:
(deepestSuperProperty.getParent() as? JetClassOrObject)?.getName()
val result = Messages.showYesNoCancelDialog(
deepestSuperProperty.getProject(),
if (containsText != null) "Do you want to rename base property from \n$containsText" else "Do you want to rename base property",
"Rename warning",
Messages.getQuestionIcon())
return when (result) {
Messages.YES -> deepestSuperProperty
Messages.NO -> jetProperty
else -> /* Cancel rename */ null
}
}
override fun prepareRenaming(element: PsiElement?, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
val jetProperty = element?.namedUnwrappedElement as? JetProperty
if (jetProperty == null) throw IllegalStateException("Can't be for element $element there because of canProcessElement()")
val propertyMethods = ApplicationManager.getApplication()!!.runReadAction(Computable<PropertyAccessorsPsiMethods> {
LightClassUtil.getLightClassPropertyMethods(jetProperty)
})!!
for (propertyMethod in propertyMethods) {
addRenameElements(propertyMethod, jetProperty.getName(), newName, allRenames, scope)
}
}
override fun renameElement(element: PsiElement?, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
if (element !is JetProperty) {
super.renameElement(element, newName, usages, listener)
return
}
enum class UsageKind {
SIMPLE_PROPERTY_USAGE
GETTER_USAGE
SETTER_USAGE
}
val oldGetterName = PropertyCodegen.getterName(element.getNameAsName())
val oldSetterName = PropertyCodegen.setterName(element.getNameAsName())
val refKindUsages = usages.toList().groupBy { (usage: UsageInfo): UsageKind ->
val refElement = usage.getReference()!!.resolve()
if (refElement is PsiMethod) {
when (refElement.getName()) {
oldGetterName -> UsageKind.GETTER_USAGE
oldSetterName -> UsageKind.SETTER_USAGE
else -> UsageKind.SIMPLE_PROPERTY_USAGE
}
}
else {
UsageKind.SIMPLE_PROPERTY_USAGE
}
}
super.renameElement(element, PropertyCodegen.setterName(Name.identifier(newName!!)),
refKindUsages[UsageKind.SETTER_USAGE]?.copyToArray() ?: array<UsageInfo>(),
null)
super.renameElement(element, PropertyCodegen.getterName(Name.identifier(newName)),
refKindUsages[UsageKind.GETTER_USAGE]?.copyToArray() ?: array<UsageInfo>(),
null)
super.renameElement(element, newName,
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.copyToArray() ?: array<UsageInfo>(),
null)
listener?.elementRenamed(element)
}
private fun addRenameElements(psiMethod: PsiMethod?,
oldName: String?, newName: String?,
allRenames: MutableMap<PsiElement, String>,
scope: SearchScope) {
if (psiMethod == null) return
OverridingMethodsSearch.search(psiMethod, scope, true).forEach { overrider ->
val overriderElement = overrider.namedUnwrappedElement
if (overriderElement != null && !(overriderElement is SyntheticElement)) {
RenameProcessor.assertNonCompileElement(overriderElement)
val overriderName = overriderElement.getName()
if (overriderElement is PsiMethod) {
if (newName != null && Name.isValidIdentifier(newName)) {
val isGetter = overriderElement.getParameterList().getParametersCount() == 0
val name = Name.identifier(newName)
allRenames[overriderElement] = if (isGetter) PropertyCodegen.getterName(name) else PropertyCodegen.setterName(name)
}
}
else {
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, newName)
if (newOverriderName != null) {
allRenames[overriderElement] = newOverriderName
}
}
}
true
}
}
private fun findDeepestOverriddenProperty(jetProperty: JetProperty): JetProperty? {
if (jetProperty.getModifierList()?.hasModifier(JetTokens.OVERRIDE_KEYWORD) == true) {
val bindingContext = AnalyzerFacadeWithCache.getContextForElement(jetProperty)
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, jetProperty]
if (descriptor != null) {
assert(descriptor is PropertyDescriptor, "Property descriptor is expected")
val supers = OverridingUtil.getDeepestSuperDeclarations(descriptor as PropertyDescriptor)
// Take one of supers for now - API doesn't support substitute to several elements (IDEA-48796)
val deepest = supers.first()
if (deepest != descriptor) {
val superPsiElement = BindingContextUtils.descriptorToDeclaration(bindingContext, deepest)
return superPsiElement as? JetProperty
}
}
}
return null
}
}
@@ -0,0 +1,29 @@
package testing.rename
trait AP {
val second: Int // <--- Rename base here
}
public open class BP: AP {
override val second: Int get() = 12 // <-- Rename with Java getter here
}
class CP: BP() {
override val second = 2 // <--- Rename overriden here
}
class CPOther {
val first: Int = 111
}
fun usagesProp() {
val b = BP()
val a: AP = b
val c = CP()
a.second
b.second
c.second
CPOther().first
}
@@ -0,0 +1,32 @@
package testing;
import testing.rename.*;
class JavaClient {
public void foo(AP ap, DP dp) {
ap.getSecond();
new BP().getSecond();
new CP().getSecond();
dp.getSecond();
new EP().getSecond();
new FP().getSecond();
}
public interface DP extends AP {
}
public static class EP implements DP {
@Override
public int getSecond() {
return 3;
}
}
public static class FP extends EP {
@Override
public int getSecond() {
return 4;
}
}
}
@@ -0,0 +1,29 @@
package testing.rename
trait AP {
val first: Int // <--- Rename base here
}
public open class BP: AP {
override val first: Int get() = 12 // <-- Rename with Java getter here
}
class CP: BP() {
override val first = 2 // <--- Rename overriden here
}
class CPOther {
val first: Int = 111
}
fun usagesProp() {
val b = BP()
val a: AP = b
val c = CP()
a.first
b.first
c.first
CPOther().first
}
@@ -0,0 +1,32 @@
package testing;
import testing.rename.*;
class JavaClient {
public void foo(AP ap, DP dp) {
ap.getFirst();
new BP().getFirst();
new CP().getFirst();
dp.getFirst();
new EP().getFirst();
new FP().getFirst();
}
public interface DP extends AP {
}
public static class EP implements DP {
@Override
public int getFirst() {
return 3;
}
}
public static class FP extends EP {
@Override
public int getFirst() {
return 4;
}
}
}
@@ -0,0 +1,6 @@
{
"type": "JAVA_METHOD",
"classFQN": "testing.rename.BP",
"methodSignature": "int getFirst()",
"newName": "second"
}
@@ -0,0 +1,6 @@
{
"type": "KOTLIN_PROPERTY",
"classFQN": "testing.rename.AP",
"oldName": "first",
"newName": "second"
}
@@ -0,0 +1,6 @@
{
"type": "KOTLIN_PROPERTY",
"classFQN": "testing.rename.CP",
"oldName": "first",
"newName": "second"
}
@@ -0,0 +1,33 @@
package testing.rename
trait AP {
var second: Int // <--- Rename base here, rename as Java getter and setter here
}
public open class BP: AP {
override var second = 1 // <--- Rename overriden here
}
class CP: BP() {
override var second = 2
}
class CPOther {
var first: Int = 111
}
fun usagesProp() {
val b = BP()
val a: AP = b
val c = CP()
a.second
b.second
c.second
a.second = 1
b.second = 2
c.second = 3
CPOther().first
}
@@ -0,0 +1,36 @@
package testing;
import testing.rename.*;
class JavaClient {
public void foo(AP ap, DP dp) {
ap.getSecond();
new BP().getSecond();
new CP().getSecond();
dp.getSecond();
new EP().getSecond();
new FP().getSecond();
}
public interface DP extends AP {
}
public static class EP implements DP {
@Override
public int getSecond() {
return 3;
}
}
public static class FP extends EP {
@Override
public int getSecond() {
return 4;
}
@Override
public void setSecond(int value) {
}
}
}
@@ -0,0 +1,33 @@
package testing.rename
trait AP {
var first: Int // <--- Rename base here, rename as Java getter and setter here
}
public open class BP: AP {
override var first = 1 // <--- Rename overriden here
}
class CP: BP() {
override var first = 2
}
class CPOther {
var first: Int = 111
}
fun usagesProp() {
val b = BP()
val a: AP = b
val c = CP()
a.first
b.first
c.first
a.first = 1
b.first = 2
c.first = 3
CPOther().first
}
@@ -0,0 +1,36 @@
package testing;
import testing.rename.*;
class JavaClient {
public void foo(AP ap, DP dp) {
ap.getFirst();
new BP().getFirst();
new CP().getFirst();
dp.getFirst();
new EP().getFirst();
new FP().getFirst();
}
public interface DP extends AP {
}
public static class EP implements DP {
@Override
public int getFirst() {
return 3;
}
}
public static class FP extends EP {
@Override
public int getFirst() {
return 4;
}
@Override
public void setFirst(int value) {
}
}
}
@@ -0,0 +1,6 @@
{
"type": "JAVA_METHOD",
"classFQN": "testing.rename.AP",
"methodSignature": "int getFirst()",
"newName": "second"
}
@@ -0,0 +1,6 @@
{
"type": "JAVA_METHOD",
"classFQN": "testing.rename.AP",
"methodSignature": "void setFirst(int value)",
"newName": "second"
}
@@ -0,0 +1,6 @@
{
"type": "KOTLIN_PROPERTY",
"classFQN": "testing.rename.AP",
"oldName": "first",
"newName": "second"
}
@@ -0,0 +1,6 @@
{
"type": "KOTLIN_PROPERTY",
"classFQN": "testing.rename.BP",
"oldName": "first",
"newName": "second"
}
@@ -16,17 +16,11 @@
package org.jetbrains.jet.plugin.refactoring.rename;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.TestMetadata;
import java.io.File;
import java.util.regex.Pattern;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.plugin.refactoring.rename.AbstractRenameTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@@ -126,4 +120,39 @@ public class RenameTestGenerated extends AbstractRenameTest {
doTest("idea/testData/refactoring/rename/renameKotlinPackageFunctionFromJava/renameKotlinPackageFunctionFromJava.test");
}
@TestMetadata("renameKotlinValProperty/renameAsJavaGetterForExplicitGetter.test")
public void testRenameKotlinValProperty_RenameAsJavaGetterForExplicitGetter() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinValProperty/renameAsJavaGetterForExplicitGetter.test");
}
@TestMetadata("renameKotlinValProperty/renameBase.test")
public void testRenameKotlinValProperty_RenameBase() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinValProperty/renameBase.test");
}
@TestMetadata("renameKotlinValProperty/renameOverriden.test")
public void testRenameKotlinValProperty_RenameOverriden() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinValProperty/renameOverriden.test");
}
@TestMetadata("renameKotlinVarProperty/renameAsJavaGetter.test")
public void testRenameKotlinVarProperty_RenameAsJavaGetter() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinVarProperty/renameAsJavaGetter.test");
}
@TestMetadata("renameKotlinVarProperty/renameAsJavaSetter.test")
public void testRenameKotlinVarProperty_RenameAsJavaSetter() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinVarProperty/renameAsJavaSetter.test");
}
@TestMetadata("renameKotlinVarProperty/renameBase.test")
public void testRenameKotlinVarProperty_RenameBase() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinVarProperty/renameBase.test");
}
@TestMetadata("renameKotlinVarProperty/renameOverriden.test")
public void testRenameKotlinVarProperty_RenameOverriden() throws Exception {
doTest("idea/testData/refactoring/rename/renameKotlinVarProperty/renameOverriden.test");
}
}