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: 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-8867`](https://youtrack.jetbrains.com/issue/KT-8867) Rename: Rename all overridden members if user chooses to refactor base declaration(s)
##### 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.
method.column=Member
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.overriding.methods.checkbox=Over&riding functions
@@ -29,6 +29,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.OverridingMethodsSearch
import org.jetbrains.kotlin.asJava.toLightMethods
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.KotlinFindClassUsagesHandler
import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindMemberUsagesHandler
@@ -108,7 +109,7 @@ class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandlerFactor
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)
}
@@ -805,7 +805,7 @@ val PsiElement.isInsideInjectedFragment: Boolean
fun checkSuperMethods(
declaration: KtDeclaration,
ignore: Collection<PsiElement>?,
actionStringKey: String
actionString: String
): List<PsiElement> {
fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
return overriddenElementsToDescriptor.entries.map { entry ->
@@ -834,7 +834,7 @@ fun checkSuperMethods(
"x.overrides.y.in.class.list",
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor),
"\n${superClassDescriptions.joinToString(separator = "")}",
KotlinBundle.message(actionStringKey)
actionString
)
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
@@ -17,13 +17,17 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchScope
import com.intellij.refactoring.JavaRefactoringSettings
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenameDialog
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.usageView.UsageInfo
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.descriptors.FunctionDescriptor
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.references.KtReference
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.DescriptorUtils
import java.lang.IllegalStateException
import java.util.*
class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
private val javaMethodProcessorInstance = RenameJavaMethodProcessor()
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
@@ -87,11 +93,30 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
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? {
val wrappedMethod = wrapPsiMethod(element) ?: return element
// Use java dialog to ask we should rename function with the base element
val substitutedJavaElement = javaMethodProcessorInstance.substituteElementToRename(wrappedMethod, editor)
val deepestSuperMethods = wrappedMethod.findDeepestSuperMethods()
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) {
return substitutedJavaElement.kotlinOrigin as? KtNamedFunction
@@ -100,14 +125,30 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
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) {
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 }
}
val psiMethod = wrapPsiMethod(element)
if (psiMethod?.containingClass != null) {
javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope)
if (element is FunctionWithSupersWrapper) {
allRenames.remove(element)
}
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)
return when (element) {
is KtNamedFunction, is KtProperty ->
checkSuperMethods(
element as KtDeclaration, allElementsToDelete, "super.methods.delete.with.usage.search"
)
is KtNamedFunction, is KtProperty -> checkSuperMethods(element as KtDeclaration, allElementsToDelete, "delete (with usage search)")
else -> super.getElementsToSearch(element, module, allElementsToDelete)
}
}