diff --git a/ChangeLog.md b/ChangeLog.md index fe0914d3718..5f7beae93ee 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties index d8cc16a7bd9..820788d1db6 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinBundle.properties @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt index f7576dca874..70ef0514d6f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinFindUsagesHandlerFactory.kt @@ -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) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt index 8d2990b481c..10d182d3bec 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt @@ -805,7 +805,7 @@ val PsiElement.isInsideInjectedFragment: Boolean fun checkSuperMethods( declaration: KtDeclaration, ignore: Collection?, - actionStringKey: String + actionString: String ): List { fun getClassDescriptions(overriddenElementsToDescriptor: Map): List { 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()) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt index 545e29dc5d9..5265157bcac 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt @@ -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 + ) : KtLightElement, 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, 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) + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt index 1cf05775003..4997af7e111 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt @@ -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) } }