From a0137aca0c0f0ad141b41d60340c03c0a28bcdfc Mon Sep 17 00:00:00 2001 From: Bart van Helvert Date: Sat, 22 Apr 2023 13:59:07 +0200 Subject: [PATCH] [kotlin] Move `KotlinMoveTarget` to common refactoring module ^KTIJ-23273 Also turns it into a sealed interface and make inheritors nested GitOrigin-RevId: 1e773093d7ead4305763267a024f233e9d523865 --- .../MoveMemberOutOfObjectIntention.kt | 3 +- .../MoveMemberToCompanionObjectIntention.kt | 3 +- .../copy/CopyKotlinDeclarationsHandler.kt | 5 +- .../cutPaste/MoveDeclarationsProcessor.kt | 3 +- .../extractClass/ExtractSuperRefactoring.kt | 35 +++++- .../KotlinChangePackageRefactoring.kt | 14 +-- .../KotlinAwareDelegatingMoveDestination.kt | 4 +- ...ractDeclarationFromCurrentFileIntention.kt | 5 +- .../move/moveDeclarations/KotlinMoveTarget.kt | 103 ------------------ .../MoveDeclarationsDelegate.kt | 8 +- .../MoveKotlinDeclarationsProcessor.kt | 4 +- .../moveDeclarations/moveConflictUtils.kt | 12 +- .../ui/MoveKotlinNestedClassesModel.kt | 3 +- ...oveKotlinNestedClassesToUpperLevelModel.kt | 9 +- .../ui/MoveKotlinTopLevelDeclarationsModel.kt | 13 +-- .../KotlinMoveDirectoryWithClassesHelper.kt | 4 +- .../MoveKotlinFileHandler.kt | 14 +-- .../moveMethod/MoveKotlinMethodProcessor.kt | 8 +- .../idea/refactoring/move/AbstractMoveTest.kt | 13 ++- .../idea/refactoring/move/KotlinMoveTarget.kt | 103 ++++++++++++++++++ 20 files changed, 199 insertions(+), 167 deletions(-) delete mode 100644 plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt create mode 100644 plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMoveTarget.kt diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfObjectIntention.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfObjectIntention.kt index 07b9daa84c8f..ea6b8607df66 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfObjectIntention.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfObjectIntention.kt @@ -9,6 +9,7 @@ import com.intellij.util.containers.MultiMap import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.refactoring.CompositeRefactoringRunner import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement @@ -43,7 +44,7 @@ abstract class MoveMemberOutOfObjectIntention(textGetter: () -> String) : SelfTa val moveDescriptor = MoveDeclarationsDescriptor( project, MoveSource(element), - KotlinMoveTargetForExistingElement(destination), + KotlinMoveTarget.ExistingElement(destination), MoveDeclarationsDelegate.NestedClass() ) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberToCompanionObjectIntention.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberToCompanionObjectIntention.kt index 7123e57f5146..b73ff6713523 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberToCompanionObjectIntention.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberToCompanionObjectIntention.kt @@ -48,6 +48,7 @@ import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.util.runSynchronouslyWithProgress import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.OuterInstanceReferenceUsageInfo import org.jetbrains.kotlin.idea.refactoring.move.collectOuterInstanceReferences import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* @@ -382,7 +383,7 @@ class MoveMemberToCompanionObjectIntention : SelfTargetingRangeIntention()) { (it as? PsiPackage)?.directories?.toList() ?: emptyList() } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ExtractDeclarationFromCurrentFileIntention.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ExtractDeclarationFromCurrentFileIntention.kt index 6216c44b4031..7492fd4e17d0 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ExtractDeclarationFromCurrentFileIntention.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ExtractDeclarationFromCurrentFileIntention.kt @@ -20,9 +20,10 @@ import kotlinx.coroutines.withTimeoutOrNull import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.core.moveCaret import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingRangeIntention +import org.jetbrains.kotlin.idea.core.moveCaret import org.jetbrains.kotlin.idea.refactoring.createKotlinFile +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog import org.jetbrains.kotlin.idea.refactoring.showWithTransaction import org.jetbrains.kotlin.idea.util.application.isUnitTestMode @@ -98,7 +99,7 @@ class ExtractDeclarationFromCurrentFileIntention : SelfTargetingRangeIntentionnull means no additional verification is needed. - */ - @NlsContexts.DialogMessage - fun verify(file: PsiFile): String? = null - -} - -object EmptyKotlinMoveTarget : KotlinMoveTarget { - override val targetContainerFqName: FqName? = null - override val targetFileOrDir: VirtualFile? = null - - override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement = throw UnsupportedOperationException() - override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null -} - -class KotlinMoveTargetForExistingElement(val targetElement: KtElement) : KotlinMoveTarget { - override val targetContainerFqName = targetElement.containingKtFile.packageFqName - - override val targetFileOrDir: VirtualFile = targetElement.containingKtFile.virtualFile - - override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetElement - - override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetElement -} - -class KotlinMoveTargetForCompanion(val targetClass: KtClass) : KotlinMoveTarget { - override val targetContainerFqName = targetClass.companionObjects.firstOrNull()?.fqName - ?: targetClass.fqName!!.child(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) - - override val targetFileOrDir: VirtualFile = targetClass.containingKtFile.virtualFile - - override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetClass.getOrCreateCompanionObject() - - override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetClass.companionObjects.firstOrNull() -} - -/** - * Assumes that a target of move is another file (existent or not). - * @param targetDir directory where a target file exists or to be created, used for various checks - * @param targetFileProvider is called to get a target file (new or existing). Original file is available as an argument. - * Result of the call is cached. - */ -class KotlinMoveTargetForDeferredFile( - targetPackageFqName: FqName, - targetDir: VirtualFile?, - private val targetFileProvider: (KtFile) -> KtFile = { it } -) : KotlinMoveTarget { - override val targetFileOrDir: VirtualFile? = targetDir - override val targetContainerFqName: FqName = targetPackageFqName - - private val createdFiles = HashMap() - - override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement { - val file = originalPsi.containingFile ?: error("PSI element in not contained in any file: $originalPsi") - val originalFile = file as KtFile - return createdFiles.getOrPut(originalFile) { targetFileProvider(originalFile) } - } - - override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null -} - -class KotlinDirectoryMoveTarget( - targetPackageFqName: FqName, - override val targetFileOrDir: VirtualFile -) : KotlinMoveTarget { - - override val targetContainerFqName = targetPackageFqName - - override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtFile { - val file = originalPsi.containingFile ?: error("PSI element in not contained in any file: $originalPsi") - return file as KtFile - } - - override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null -} - -fun KotlinMoveTarget.getTargetModule(project: Project) = targetFileOrDir?.getModule(project) \ No newline at end of file diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt index 9142b5cfe001..3ba82734ed07 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt @@ -55,7 +55,7 @@ sealed class MoveDeclarationsDelegate { ) : MoveDeclarationsDelegate() { override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo { val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!) - val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject + val movingToClass = (moveTarget as? KotlinMoveTarget.ExistingElement)?.targetElement is KtClassOrObject val targetContainerFqName = moveTarget.targetContainerFqName val newInfo = when { targetContainerFqName == null -> ContainerInfo.UnknownPackage @@ -75,8 +75,8 @@ sealed class MoveDeclarationsDelegate { companionDescriptor: ClassDescriptor ): Boolean { return when (val moveTarget = moveDescriptor.moveTarget) { - is KotlinMoveTargetForCompanion -> true - is KotlinMoveTargetForExistingElement -> { + is KotlinMoveTarget.Companion -> true + is KotlinMoveTarget.ExistingElement -> { val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false val targetClassDescriptor = targetClass.unsafeResolveToDescriptor() as ClassDescriptor val companionClassDescriptor = companionDescriptor.containingDeclaration as? ClassDescriptor ?: return false @@ -122,7 +122,7 @@ sealed class MoveDeclarationsDelegate { newClassName?.let { setName(it) } if (this is KtClass) { - if ((descriptor.moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement !is KtClassOrObject) { + if ((descriptor.moveTarget as? KotlinMoveTarget.ExistingElement)?.targetElement !is KtClassOrObject) { if (hasModifier(KtTokens.INNER_KEYWORD)) removeModifier(KtTokens.INNER_KEYWORD) if (hasModifier(KtTokens.PROTECTED_KEYWORD)) removeModifier(KtTokens.PROTECTED_KEYWORD) } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt index b01893652a14..902845653513 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt @@ -186,8 +186,8 @@ class MoveKotlinDeclarationsProcessor( val javaScope = projectScope.restrictByFileType(JavaFileType.INSTANCE) val currentFile = ktDeclaration.containingKtFile val newFile = when (moveTarget) { - is KotlinMoveTargetForExistingElement -> moveTarget.targetElement as? KtFile ?: return null - is KotlinMoveTargetForDeferredFile -> return javaScope + is KotlinMoveTarget.ExistingElement -> moveTarget.targetElement as? KtFile ?: return null + is KotlinMoveTarget.DeferredFile -> return javaScope else -> return null } val currentFacade = currentFile.findFacadeClass() diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/moveConflictUtils.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/moveConflictUtils.kt index 1a685088040e..91d23cd32ab7 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/moveConflictUtils.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/moveConflictUtils.kt @@ -55,7 +55,9 @@ import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.core.util.toPsiDirectory import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.refactoring.getUsageContext +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveUsage +import org.jetbrains.kotlin.idea.refactoring.move.getTargetModule import org.jetbrains.kotlin.idea.refactoring.pullUp.renderForConflicts import org.jetbrains.kotlin.idea.resolve.languageVersionSettings import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor @@ -114,7 +116,7 @@ class MoveConflictChecker( private fun KotlinMoveTarget.getContainerDescriptor(): DeclarationDescriptor? { return when (this) { - is KotlinMoveTargetForExistingElement -> when (val targetElement = targetElement) { + is KotlinMoveTarget.ExistingElement -> when (val targetElement = targetElement) { is KtNamedDeclaration -> resolutionFacade.resolveToDescriptor(targetElement) is KtFile -> { @@ -129,7 +131,7 @@ class MoveConflictChecker( else -> null } - is KotlinDirectoryMoveTarget, is KotlinMoveTargetForDeferredFile -> { + is KotlinMoveTarget.Directory, is KotlinMoveTarget.DeferredFile -> { val packageFqName = targetContainerFqName ?: return null val targetModuleDescriptor = targetFileOrDir?.let { getModuleDescriptor(it) ?: return null } ?: resolutionFacade.moduleDescriptor @@ -399,7 +401,7 @@ class MoveConflictChecker( if (referencedDescriptor is DeclarationDescriptorWithVisibility && referencedDescriptor.visibility == DescriptorVisibilities.PUBLIC - && moveTarget is KotlinMoveTargetForExistingElement + && moveTarget is KotlinMoveTarget.ExistingElement && moveTarget.targetElement.parentsWithSelf.filterIsInstance().all { it.isPublic } ) continue @@ -796,8 +798,8 @@ class MoveConflictChecker( return KotlinJavaPsiFacade.getInstance(project).findPackage(fqName.asString(), GlobalSearchScope.moduleScope(module)) } - return (this as? KotlinDirectoryMoveTarget)?.targetFileOrDir?.toPsiDirectory(project)?.getPackage() - ?: (this as? KotlinMoveTargetForDeferredFile)?.targetFileOrDir?.toPsiDirectory(project)?.getPackage() + return (this as? KotlinMoveTarget.Directory)?.targetFileOrDir?.toPsiDirectory(project)?.getPackage() + ?: (this as? KotlinMoveTarget.DeferredFile)?.targetFileOrDir?.toPsiDirectory(project)?.getPackage() ?: tryGetPackageFromTargetContainer() } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesModel.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesModel.kt index 748b1366494c..79d6ffe6b69b 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesModel.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesModel.kt @@ -9,6 +9,7 @@ import com.intellij.psi.util.PsiTreeUtil import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.move.MoveCallback import org.jetbrains.kotlin.idea.base.resources.KotlinBundle +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* import org.jetbrains.kotlin.idea.statistics.KotlinMoveRefactoringFUSCollector.MoveRefactoringDestination import org.jetbrains.kotlin.idea.statistics.KotlinMoveRefactoringFUSCollector.MovedEntity @@ -52,7 +53,7 @@ internal class MoveKotlinNestedClassesModel( @Throws(ConfigurationException::class) override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData { val elementsToMove = selectedElementsToMove - val target = KotlinMoveTargetForExistingElement(getCheckedTargetClass()) + val target = KotlinMoveTarget.ExistingElement(getCheckedTargetClass()) val delegate = MoveDeclarationsDelegate.NestedClass() val descriptor = MoveDeclarationsDescriptor( project, diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelModel.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelModel.kt index adf1d0bdf875..02639e28b342 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelModel.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelModel.kt @@ -17,12 +17,13 @@ import com.intellij.refactoring.PackageWrapper import com.intellij.refactoring.util.RefactoringMessageUtil import com.intellij.util.CommonJavaRefactoringUtil import com.intellij.util.IncorrectOperationException -import org.jetbrains.kotlin.idea.base.util.module -import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.base.fe10.codeInsight.newDeclaration.Fe10KotlinNameSuggester +import org.jetbrains.kotlin.idea.base.resources.KotlinBundle +import org.jetbrains.kotlin.idea.base.util.module import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.refactoring.createKotlinFile +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.getTargetPackageFqName import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* import org.jetbrains.kotlin.idea.roots.getSuitableDestinationSourceRoots @@ -171,14 +172,14 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel( val targetFileName = suggestedName + "." + KotlinFileType.EXTENSION - val target = KotlinMoveTargetForDeferredFile( + val target = KotlinMoveTarget.DeferredFile( targetPackageFqName, target.virtualFile ) { createKotlinFile(targetFileName, target, targetPackageFqName.asString()) } target to MoveRefactoringDestination.FILE } else { - KotlinMoveTargetForExistingElement(target as KtElement) to MoveRefactoringDestination.DECLARATION + KotlinMoveTarget.ExistingElement(target as KtElement) to MoveRefactoringDestination.DECLARATION } } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsModel.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsModel.kt index bc36065934ff..7f4259f70ac0 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsModel.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsModel.kt @@ -22,11 +22,8 @@ import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.core.util.toPsiDirectory import org.jetbrains.kotlin.idea.core.util.toPsiFile import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile -import org.jetbrains.kotlin.idea.refactoring.move.KotlinAwareMoveFilesOrDirectoriesProcessor -import org.jetbrains.kotlin.idea.refactoring.move.MoveToKotlinFileProcessor -import org.jetbrains.kotlin.idea.refactoring.move.mapWithReadActionInProcess +import org.jetbrains.kotlin.idea.refactoring.move.* import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* -import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective import org.jetbrains.kotlin.idea.statistics.KotlinMoveRefactoringFUSCollector.MoveRefactoringDestination import org.jetbrains.kotlin.idea.statistics.KotlinMoveRefactoringFUSCollector.MovedEntity import org.jetbrains.kotlin.idea.util.collectAllExpectAndActualDeclaration @@ -108,7 +105,7 @@ internal class MoveKotlinTopLevelDeclarationsModel( if (singleSourceFileMode) { val singeTargetFile = filesExistingInTargetDir.single() as? KtFile if (singeTargetFile != null) { - return KotlinMoveTargetForExistingElement(singeTargetFile) + return KotlinMoveTarget.ExistingElement(singeTargetFile) } } else { val filePathsToReport = filesExistingInTargetDir.joinToString( @@ -135,7 +132,7 @@ internal class MoveKotlinTopLevelDeclarationsModel( targetDirectory = null } - return KotlinMoveTargetForDeferredFile( + return KotlinMoveTarget.DeferredFile( FqName(targetPackage), targetDirectory?.virtualFile ) { @@ -159,7 +156,7 @@ internal class MoveKotlinTopLevelDeclarationsModel( if (sourceFiles.singleOrNull() == it) { throw ConfigurationException(KotlinBundle.message("text.cannot.move.to.original.file")) } - return KotlinMoveTargetForExistingElement(it) + return KotlinMoveTarget.ExistingElement(it) } val targetDirectoryPath = targetFile.toPath().parent @@ -183,7 +180,7 @@ internal class MoveKotlinTopLevelDeclarationsModel( KotlinBundle.message("text.cannot.find.package.corresponding.to.0", targetDirectoryPath) ) - return KotlinMoveTargetForDeferredFile(targetPackageFqName, psiDirectory.virtualFile) { + return KotlinMoveTarget.DeferredFile(targetPackageFqName, psiDirectory.virtualFile) { getOrCreateKotlinFile(targetFile.name, psiDirectory, targetPackageFqName.asString()) } } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt index e15ef545ce35..2cf3929df6f1 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.idea.base.util.quoteIfNeeded import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.analyzeConflictsInFile import org.jetbrains.kotlin.name.FqName @@ -78,7 +78,7 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() { conflicts: MultiMap ) { val psiPackage = directory?.getPackage() ?: return - val moveTarget = KotlinDirectoryMoveTarget(FqName(psiPackage.qualifiedName), directory.virtualFile) + val moveTarget = KotlinMoveTarget.Directory(FqName(psiPackage.qualifiedName), directory.virtualFile) for ((index, usageInfo) in infos.withIndex()) { if (usageInfo !is FileUsagesWrapper) continue diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt index f547bc94dc37..3339738f28e6 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt @@ -16,11 +16,11 @@ import org.jetbrains.kotlin.idea.base.util.quoteIfNeeded import org.jetbrains.kotlin.idea.core.getFqNameWithImplicitPrefix import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly -import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo -import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo -import org.jetbrains.kotlin.idea.refactoring.move.allElementsToMove -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* -import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective +import org.jetbrains.kotlin.idea.refactoring.move.* +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationsDelegate +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationsDescriptor +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveSource import org.jetbrains.kotlin.idea.roots.isOutsideKotlinAwareSourceRoot import org.jetbrains.kotlin.psi.KtFile @@ -69,12 +69,12 @@ class MoveKotlinFileHandler : MoveFileHandler() { val project = psiFile.project val moveTarget = when (val newPackage = packageNameInfo.newContainer) { - ContainerInfo.UnknownPackage -> EmptyKotlinMoveTarget + ContainerInfo.UnknownPackage -> KotlinMoveTarget.Empty else -> if (newParent == null) { return null } else { - KotlinMoveTargetForDeferredFile(newPackage.fqName!!, newParent.virtualFile) { + KotlinMoveTarget.DeferredFile(newPackage.fqName!!, newParent.virtualFile) { MoveFilesOrDirectoriesUtil.doMoveFile(psiFile, newParent) val file = newParent.findFile(psiFile.name) ?: error("Lost file after move") file as KtFile diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveMethod/MoveKotlinMethodProcessor.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveMethod/MoveKotlinMethodProcessor.kt index 3d8d6d218e5d..5889264d6bc7 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveMethod/MoveKotlinMethodProcessor.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveMethod/MoveKotlinMethodProcessor.kt @@ -17,13 +17,15 @@ import com.intellij.usageView.UsageViewDescriptor import com.intellij.util.IncorrectOperationException import com.intellij.util.containers.MultiMap import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility +import org.jetbrains.kotlin.descriptors.DescriptorVisibilityUtils import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.base.util.projectScope import org.jetbrains.kotlin.idea.caches.resolve.* import org.jetbrains.kotlin.idea.core.setVisibility import org.jetbrains.kotlin.idea.refactoring.move.* -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinMoveTargetForExistingElement import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveConflictChecker import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.Mover import org.jetbrains.kotlin.idea.references.mainReference @@ -70,7 +72,7 @@ class MoveKotlinMethodProcessor( ContainerInfo.Class(targetClassOrObject.fqName!!) ) val conflictChecker = - MoveConflictChecker(myProject, listOf(method), KotlinMoveTargetForExistingElement(targetClassOrObject), method) + MoveConflictChecker(myProject, listOf(method), KotlinMoveTarget.ExistingElement(targetClassOrObject), method) val searchScope = myProject.projectScope() val internalUsages = mutableSetOf() val methodCallUsages = mutableSetOf() diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt index 310abb26350e..b6eb58f4b911 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt @@ -28,7 +28,10 @@ import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings import org.jetbrains.kotlin.idea.refactoring.createKotlinFile import org.jetbrains.kotlin.idea.refactoring.move.changePackage.KotlinChangePackageRefactoring import org.jetbrains.kotlin.idea.refactoring.move.moveClassesOrPackages.KotlinAwareDelegatingMoveDestination -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationsDelegate +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationsDescriptor +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveSource import org.jetbrains.kotlin.idea.refactoring.move.moveMethod.MoveKotlinMethodProcessor import org.jetbrains.kotlin.idea.refactoring.runRefactoringTest import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex @@ -229,11 +232,11 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction { } else { destDirIfAny?.virtualFile } - KotlinMoveTargetForDeferredFile(FqName(packageName), targetDir) { + KotlinMoveTarget.DeferredFile(FqName(packageName), targetDir) { createKotlinFile(guessNewFileName(elementsToMove)!!, moveDestination.getTargetDirectory(mainFile)) } } ?: config.getString("targetFile").let { filePath -> - KotlinMoveTargetForExistingElement( + KotlinMoveTarget.ExistingElement( PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(filePath)!!) as KtFile ) } @@ -291,12 +294,12 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction { ) val moveTarget = if (targetClass != null) { - KotlinMoveTargetForExistingElement(targetClass) + KotlinMoveTarget.ExistingElement(targetClass) } else { val fileName = (delegate.newClassName ?: elementToMove.name!!) + ".kt" val targetPackageFqName = (mainFile as KtFile).packageFqName val targetDir = mainFile.containingDirectory!! - KotlinMoveTargetForDeferredFile(targetPackageFqName, targetDir.virtualFile) { + KotlinMoveTarget.DeferredFile(targetPackageFqName, targetDir.virtualFile) { createKotlinFile(fileName, targetDir, targetPackageFqName.asString()) } } diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMoveTarget.kt b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMoveTarget.kt new file mode 100644 index 000000000000..9046249d826e --- /dev/null +++ b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMoveTarget.kt @@ -0,0 +1,103 @@ +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package org.jetbrains.kotlin.idea.refactoring.move + +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.NlsContexts +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.idea.base.psi.getOrCreateCompanionObject +import org.jetbrains.kotlin.idea.base.util.getModule +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile + +fun KotlinMoveTarget.getTargetModule(project: Project) = targetFileOrDir?.getModule(project) + +sealed interface KotlinMoveTarget { + val targetContainerFqName: FqName? + + val targetFileOrDir: VirtualFile? + + fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement + + fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? + + /** + * Check possible errors and return corresponding message, or null if no errors are detected. + * null means no additional verification is needed. + */ + @NlsContexts.DialogMessage + fun verify(file: PsiFile): String? = null + + object Empty : KotlinMoveTarget { + override val targetContainerFqName: FqName? = null + + override val targetFileOrDir: VirtualFile? = null + + override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement = throw UnsupportedOperationException() + + override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null + } + + class ExistingElement(val targetElement: KtElement) : KotlinMoveTarget { + override val targetContainerFqName = targetElement.containingKtFile.packageFqName + + override val targetFileOrDir: VirtualFile = targetElement.containingKtFile.virtualFile + + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetElement + + override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetElement + } + + class Companion(val targetClass: KtClass) : KotlinMoveTarget { + override val targetContainerFqName = + targetClass.companionObjects.firstOrNull()?.fqName ?: targetClass.fqName?.child(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) + + override val targetFileOrDir: VirtualFile = targetClass.containingKtFile.virtualFile + + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetClass.getOrCreateCompanionObject() + + override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetClass.companionObjects.firstOrNull() + } + + /** + * Assumes that a target of move is another file (existent or not). + * @param targetDir directory where a target file exists or to be created, used for various checks + * @param targetFileProvider is called to get a target file (new or existing). Original file is available as an argument. + * Result of the call is cached. + */ + class DeferredFile( + targetPackageFqName: FqName, + targetDir: VirtualFile?, + private val targetFileProvider: (KtFile) -> KtFile = { it } + ) : KotlinMoveTarget { + override val targetFileOrDir: VirtualFile? = targetDir + + override val targetContainerFqName: FqName = targetPackageFqName + + private val createdFiles = HashMap() + + override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement { + val file = originalPsi.containingFile ?: error("PSI element in not contained in any file: $originalPsi") + val originalFile = file as KtFile + return createdFiles.getOrPut(originalFile) { targetFileProvider(originalFile) } + } + + override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null + } + + class Directory(targetPackageFqName: FqName, override val targetFileOrDir: VirtualFile) : KotlinMoveTarget { + override val targetContainerFqName = targetPackageFqName + + override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtFile { + val file = originalPsi.containingFile ?: error("PSI element in not contained in any file: $originalPsi") + return file as KtFile + } + + override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null + } +} \ No newline at end of file