Rename: Rename all overridden members if user chooses to refactor base declaration(s)

#KT-8867 Fixed
This commit is contained in:
Alexey Sedunov
2016-09-02 16:46:27 +03:00
parent a77e1dabce
commit 6e02051be5
6 changed files with 54 additions and 17 deletions
+1
View File
@@ -122,6 +122,7 @@ These artifacts include extensions for the types available in the latter JDKs, s
- [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Report separate conflicts for each property accessor - [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Report separate conflicts for each property accessor
- [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Forbid moving of enum entries - [`KT-13216`](https://youtrack.jetbrains.com/issue/KT-13216) Move: Forbid moving of enum entries
- [`KT-13553`](https://youtrack.jetbrains.com/issue/KT-13553) Move: Do not show directory selection dialog if target directory is already specified by drag-and-drop - [`KT-13553`](https://youtrack.jetbrains.com/issue/KT-13553) Move: Do not show directory selection dialog if target directory is already specified by drag-and-drop
- [`KT-8867`](https://youtrack.jetbrains.com/issue/KT-8867) Rename: Rename all overridden members if user chooses to refactor base declaration(s)
##### New features ##### New features
@@ -190,8 +190,6 @@ there.are.unused.methods.that.override.methods.you.delete=There are unused membe
choose.the.ones.you.want.to.be.deleted=Choose the ones you want to be deleted. choose.the.ones.you.want.to.be.deleted=Choose the ones you want to be deleted.
method.column=Member method.column=Member
delete.param.in.method.hierarchy={0} is a part of method hierarchy. Do you want to delete multiple parameters? delete.param.in.method.hierarchy={0} is a part of method hierarchy. Do you want to delete multiple parameters?
super.methods.delete.with.usage.search=delete (with usage search)
super.methods.action.key.find.usages=find usages of
find.what.implementing.methods.checkbox=&Implementing functions find.what.implementing.methods.checkbox=&Implementing functions
find.what.overriding.methods.checkbox=Over&riding functions find.what.overriding.methods.checkbox=Over&riding functions
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.OverridingMethodsSearch import com.intellij.psi.search.searches.OverridingMethodsSearch
import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.findUsages.handlers.DelegatingFindMemberUsagesHandler import org.jetbrains.kotlin.idea.findUsages.handlers.DelegatingFindMemberUsagesHandler
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindMemberUsagesHandler import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindMemberUsagesHandler
@@ -108,7 +109,7 @@ class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandlerFactor
return KotlinFindMemberUsagesHandler.getInstance(declaration, factory = this) return KotlinFindMemberUsagesHandler.getInstance(declaration, factory = this)
} }
val declarationsToSearch = checkSuperMethods(declaration, null, "super.methods.action.key.find.usages") val declarationsToSearch = checkSuperMethods(declaration, null, "find usages of")
return handlerForMultiple(declaration, declarationsToSearch) return handlerForMultiple(declaration, declarationsToSearch)
} }
@@ -805,7 +805,7 @@ val PsiElement.isInsideInjectedFragment: Boolean
fun checkSuperMethods( fun checkSuperMethods(
declaration: KtDeclaration, declaration: KtDeclaration,
ignore: Collection<PsiElement>?, ignore: Collection<PsiElement>?,
actionStringKey: String actionString: String
): List<PsiElement> { ): List<PsiElement> {
fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> { fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
return overriddenElementsToDescriptor.entries.map { entry -> return overriddenElementsToDescriptor.entries.map { entry ->
@@ -834,7 +834,7 @@ fun checkSuperMethods(
"x.overrides.y.in.class.list", "x.overrides.y.in.class.list",
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor), DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor),
"\n${superClassDescriptions.joinToString(separator = "")}", "\n${superClassDescriptions.joinToString(separator = "")}",
KotlinBundle.message(actionStringKey) actionString
) )
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon()) val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
@@ -17,13 +17,17 @@
package org.jetbrains.kotlin.idea.refactoring.rename package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.listeners.RefactoringElementListener import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenameDialog
import com.intellij.refactoring.rename.RenameJavaMethodProcessor import com.intellij.refactoring.rename.RenameJavaMethodProcessor
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.refactoring.rename.RenameUtil import com.intellij.refactoring.rename.RenameUtil
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.util.SmartList import com.intellij.util.SmartList
@@ -33,18 +37,20 @@ import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.checkSuperMethods
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import java.lang.IllegalStateException
import java.util.* import java.util.*
class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
private val javaMethodProcessorInstance = RenameJavaMethodProcessor() private val javaMethodProcessorInstance = RenameJavaMethodProcessor()
override fun canProcessElement(element: PsiElement): Boolean { override fun canProcessElement(element: PsiElement): Boolean {
return element is KtNamedFunction || (element is KtLightMethod && element.kotlinOrigin is KtNamedFunction) return element is KtNamedFunction || (element is KtLightMethod && element.kotlinOrigin is KtNamedFunction) || element is FunctionWithSupersWrapper
} }
override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD override fun isToSearchInComments(psiElement: PsiElement) = JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD
@@ -87,11 +93,30 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
checkRedeclarations(descriptor, newName, result) checkRedeclarations(descriptor, newName, result)
} }
class FunctionWithSupersWrapper(
val originalDeclaration: KtNamedFunction,
val supers: List<PsiElement>
) : KtLightElement<KtNamedFunction, KtNamedFunction>, PsiNamedElement by originalDeclaration {
override val kotlinOrigin: KtNamedFunction?
get() = originalDeclaration
override val clsDelegate: KtNamedFunction
get() = originalDeclaration
}
override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? { override fun substituteElementToRename(element: PsiElement?, editor: Editor?): PsiElement? {
val wrappedMethod = wrapPsiMethod(element) ?: return element val wrappedMethod = wrapPsiMethod(element) ?: return element
// Use java dialog to ask we should rename function with the base element val deepestSuperMethods = wrappedMethod.findDeepestSuperMethods()
val substitutedJavaElement = javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor) val substitutedJavaElement = when {
deepestSuperMethods.isEmpty() -> return element
wrappedMethod.isConstructor || deepestSuperMethods.size == 1 || element !is KtNamedFunction -> {
javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor)
}
else -> {
val chosenElements = checkSuperMethods(element, null, "rename")
if (chosenElements.size > 1) FunctionWithSupersWrapper(element, chosenElements) else wrappedMethod
}
}
if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) { if (substitutedJavaElement is KtLightMethod && element is KtDeclaration) {
return substitutedJavaElement.kotlinOrigin as? KtNamedFunction return substitutedJavaElement.kotlinOrigin as? KtNamedFunction
@@ -100,14 +125,30 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
return substitutedJavaElement return substitutedJavaElement
} }
override fun createRenameDialog(project: Project, element: PsiElement, nameSuggestionContext: PsiElement?, editor: Editor?): RenameDialog {
val elementForDialog = (element as? FunctionWithSupersWrapper)?.originalDeclaration ?: element
return object : RenameDialog(project, elementForDialog, nameSuggestionContext, editor) {
override fun createRenameProcessor(newName: String) = RenameProcessor(getProject(), element, newName, isSearchInComments, isSearchInNonJavaFiles)
}
}
override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) { override fun prepareRenaming(element: PsiElement, newName: String?, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
super.prepareRenaming(element, newName, allRenames, scope) super.prepareRenaming(element, newName, allRenames, scope)
if (element is KtLightMethod && newName != null && getJvmName(element) == null) {
if (newName == null) return
if (element is KtLightMethod && getJvmName(element) == null) {
(element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName } (element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName }
} }
val psiMethod = wrapPsiMethod(element) if (element is FunctionWithSupersWrapper) {
if (psiMethod?.containingClass != null) { allRenames.remove(element)
javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope) }
for (declaration in (if (element is FunctionWithSupersWrapper) element.supers else listOf(element))) {
val psiMethod = wrapPsiMethod(declaration) ?: continue
allRenames[declaration] = newName
if (psiMethod.containingClass != null) {
javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope)
}
} }
} }
@@ -344,11 +344,7 @@ class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
if (ApplicationManager.getApplication()!!.isUnitTestMode) return Collections.singletonList(element) if (ApplicationManager.getApplication()!!.isUnitTestMode) return Collections.singletonList(element)
return when (element) { return when (element) {
is KtNamedFunction, is KtProperty -> is KtNamedFunction, is KtProperty -> checkSuperMethods(element as KtDeclaration, allElementsToDelete, "delete (with usage search)")
checkSuperMethods(
element as KtDeclaration, allElementsToDelete, "super.methods.delete.with.usage.search"
)
else -> super.getElementsToSearch(element, module, allElementsToDelete) else -> super.getElementsToSearch(element, module, allElementsToDelete)
} }
} }