From e3b2a4775e95d1968e65cdb350c7aa013a5243e3 Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Thu, 26 Oct 2023 18:56:37 +0200 Subject: [PATCH] [kotlin] Added AutomaticFileRenamer #KTIJ-27509 #KTIJ-25076 GitOrigin-RevId: b5cf0ea3a926a049c7db996ccc86775ff6856166 --- .../messages/KotlinBundle.properties | 5 +- .../K1CommonRefactoringSettings.kt | 3 + .../refactoring/KotlinRefactoringSettings.kt | 1 + .../resources/META-INF/refactorings.xml | 1 + .../KotlinCommonRefactoringSettings.kt | 5 ++ .../rename/AutomaticFileRenamer.kt | 60 +++++++++++++++++++ .../rename/RenameKotlinFunctionProcessor.kt | 39 +----------- .../rename/RenameKotlinPropertyProcessor.kt | 4 -- .../K2CommonRefactoringSettings.kt | 3 + .../KotlinFirRefactoringsSettings.kt | 1 + 10 files changed, 79 insertions(+), 43 deletions(-) create mode 100644 plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticFileRenamer.kt diff --git a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties index f336f72c41a8..a1f4bf2de89c 100644 --- a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties +++ b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties @@ -1298,8 +1298,11 @@ rename.base.0=Rename base {0,choice,1#function|2#property|3#member|4#method|11#f rename.declaration.title.0.implements.1.2.of.3={0} {1,choice,1#implements|2#overrides} {2} of {3} rename.searching.for.all.overrides=Searching for all overrides rename.searching.for.super.declaration=Searching for the deepest super declaration -rename.file.name=Rename File Name +title.rename.file.name=Rename File Name +file.entity=File +rename.file.name=Rename file name rename.file.name.0=Rename File Name to ''{0}'' +title.rename.file.name.to=Rename File Name To: wrap.with.coroutine.scope.fix.text=Wrap function body with 'coroutineScope { ... }' wrap.with.coroutine.scope.fix.text2=Wrap call with 'coroutineScope { ... }' wrap.with.coroutine.scope.fix.text3=Remove receiver \\& wrap with 'coroutineScope { ... }' diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/K1CommonRefactoringSettings.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/K1CommonRefactoringSettings.kt index fcaa45125415..d4201815879a 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/K1CommonRefactoringSettings.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/K1CommonRefactoringSettings.kt @@ -48,6 +48,9 @@ internal class K1CommonRefactoringSettings : KotlinCommonRefactoringSettingsBase override var INTRODUCE_SPECIFY_TYPE_EXPLICITLY: Boolean by delegateTo { it::INTRODUCE_SPECIFY_TYPE_EXPLICITLY } + override var renameFileNames: Boolean + by delegateTo { it::renameFileNames } + override var renameVariables: Boolean by delegateTo { it::renameVariables } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSettings.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSettings.kt index 4e45a776b8d7..98a2d1802a9b 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSettings.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/KotlinRefactoringSettings.kt @@ -85,6 +85,7 @@ class KotlinRefactoringSettings : PersistentStateComponent + diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/KotlinCommonRefactoringSettings.kt b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/KotlinCommonRefactoringSettings.kt index cd2bdbb992a7..0693a66d36c9 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/KotlinCommonRefactoringSettings.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/KotlinCommonRefactoringSettings.kt @@ -27,6 +27,11 @@ interface KotlinCommonRefactoringSettings { var INTRODUCE_DECLARE_WITH_VAR: Boolean var INTRODUCE_SPECIFY_TYPE_EXPLICITLY: Boolean + /** + * Indicates whether automatic file rename based on changed type name is suggested + */ + var renameFileNames: Boolean + /** * Indicates whether automatic variable rename based on changed type name is suggested */ diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticFileRenamer.kt b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticFileRenamer.kt new file mode 100644 index 000000000000..3ca227a6c61b --- /dev/null +++ b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/AutomaticFileRenamer.kt @@ -0,0 +1,60 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.refactoring.rename + +import com.intellij.openapi.util.NlsContexts.Button +import com.intellij.openapi.util.NlsContexts.ColumnName +import com.intellij.openapi.util.NlsContexts.DialogTitle +import com.intellij.psi.PsiElement +import com.intellij.refactoring.rename.naming.AutomaticRenamer +import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory +import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.base.resources.KotlinBundle +import org.jetbrains.kotlin.idea.refactoring.KotlinCommonRefactoringSettings +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtVariableDeclaration + +class AutomaticFileRenamer( + file: KtFile, + newFileName: String, +) : AutomaticRenamer() { + + init { + myElements.add(file) + suggestAllNames(file.name, "$newFileName.${file.virtualFile.extension ?: KotlinFileType.EXTENSION}") + } + + override fun allowChangeSuggestedName(): Boolean = false + + override fun getDialogTitle(): @DialogTitle String? = KotlinBundle.message("title.rename.file.name") + + override fun getDialogDescription(): @Button String? = KotlinBundle.message("title.rename.file.name.to") + + override fun entityName(): @ColumnName String? = KotlinBundle.message("file.entity") + + override fun isSelectedByDefault(): Boolean = true +} + +open class AutomaticFileRenamerFactory : AutomaticRenamerFactory { + override fun isApplicable(element: PsiElement): Boolean { + if (!(element is KtNamedFunction || element is KtVariableDeclaration)) return false + val file = element.containingFile as? KtFile ?: return false + + val declaration = file.declarations.singleOrNull() as? KtNamedDeclaration ?: return false + return declaration.name == file.virtualFile.nameWithoutExtension && element == declaration + } + + override fun createRenamer(element: PsiElement, newName: String, usages: Collection): AutomaticFileRenamer { + return AutomaticFileRenamer(element.containingFile as KtFile, newName) + } + + override fun isEnabled() = KotlinCommonRefactoringSettings.getInstance().renameFileNames + + override fun setEnabled(enabled: Boolean) { + KotlinCommonRefactoringSettings.getInstance().renameFileNames = enabled + } + + override fun getOptionName(): String? = KotlinBundle.message("rename.file.name") +} diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt index 697cd18c6ca5..f92329bbbabe 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt @@ -5,9 +5,7 @@ package org.jetbrains.kotlin.idea.refactoring.rename import com.intellij.openapi.application.runReadAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.Pass -import com.intellij.openapi.util.registry.Registry import com.intellij.psi.* import com.intellij.psi.search.SearchScope import com.intellij.refactoring.listeners.RefactoringElementListener @@ -16,7 +14,6 @@ import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.RefactoringUtil import com.intellij.usageView.UsageInfo import com.intellij.util.SmartList -import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.asJava.LightClassUtil import org.jetbrains.kotlin.asJava.elements.KtLightElement import org.jetbrains.kotlin.asJava.elements.KtLightMethod @@ -29,7 +26,6 @@ import org.jetbrains.kotlin.idea.refactoring.conflicts.checkRedeclarationConflic import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.search.KotlinSearchUsagesSupport import org.jetbrains.kotlin.idea.search.declarationsSearch.findDeepestSuperMethodsKotlinAware -import org.jetbrains.kotlin.idea.util.application.isUnitTestMode import org.jetbrains.kotlin.psi.* class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { @@ -235,8 +231,6 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { } } renameRefactoringSupport.prepareForeignUsagesRenaming(element, newName, allRenames, scope) - - element.renameFileIfSingleDeclaration(originalName, newName, allRenames) } override fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { @@ -289,35 +283,4 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() { return processFoundReferences(element, references) } -} - -@ApiStatus.Internal -internal fun PsiElement.renameFileIfSingleDeclaration( - originalName: String, - newName: String, - allRenames: MutableMap -) { - val file = containingFile as? KtFile ?: return - - if (file.declarations.singleOrNull() == this) { - file.virtualFile?.let { virtualFile -> - val nameWithoutExtensions = virtualFile.nameWithoutExtension - if (nameWithoutExtensions == originalName) { - if (!isUnitTestMode() && newName.isNotEmpty() && Messages.showYesNoDialog( - project, - KotlinBundle.message("rename.file.name.0", newName), - KotlinBundle.message("rename.file.name"), - Messages.getYesButton(), - Messages.getCancelButton(), - Messages.getQuestionIcon() - ) == Messages.NO - ) { - return - } - val newFileName = newName + "." + virtualFile.extension - allRenames[file] = newFileName - RenamePsiElementProcessor.forElement(file).prepareRenaming(file, newFileName, allRenames) - } - } - } -} +} \ No newline at end of file diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt index dce1a215816e..bf3fb0437e49 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt @@ -250,10 +250,6 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { } renameRefactoringSupport.prepareForeignUsagesRenaming(element, newName, allRenames, scope) - - originalName?.let { - element.renameFileIfSingleDeclaration(it, newName, allRenames) - } } protected enum class UsageKind { diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/K2CommonRefactoringSettings.kt b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/K2CommonRefactoringSettings.kt index c0cda77a24bc..20fdb9c0ee94 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/K2CommonRefactoringSettings.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/K2CommonRefactoringSettings.kt @@ -50,6 +50,9 @@ internal class K2CommonRefactoringSettings : KotlinCommonRefactoringSettingsBase override var INTRODUCE_SPECIFY_TYPE_EXPLICITLY: Boolean by delegateTo { it::INTRODUCE_SPECIFY_TYPE_EXPLICITLY } + override var renameFileNames: Boolean + by delegateTo { it::renameFileNames } + override var renameVariables: Boolean by delegateTo { it::renameVariables } diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/KotlinFirRefactoringsSettings.kt b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/KotlinFirRefactoringsSettings.kt index 58cc5daa5e8c..ed744bc4264a 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/KotlinFirRefactoringsSettings.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/KotlinFirRefactoringsSettings.kt @@ -17,6 +17,7 @@ class KotlinFirRefactoringsSettings : PersistentStateComponent