diff --git a/plugins/kotlin/base/psi/src/org/jetbrains/kotlin/idea/base/psi/KotlinPsiModificationUtils.kt b/plugins/kotlin/base/psi/src/org/jetbrains/kotlin/idea/base/psi/KotlinPsiModificationUtils.kt index aac3a97b21d5..4d7845b64aad 100644 --- a/plugins/kotlin/base/psi/src/org/jetbrains/kotlin/idea/base/psi/KotlinPsiModificationUtils.kt +++ b/plugins/kotlin/base/psi/src/org/jetbrains/kotlin/idea/base/psi/KotlinPsiModificationUtils.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.idea.base.psi import com.intellij.psi.PsiElement import com.intellij.psi.PsiErrorElement import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.codeStyle.CodeEditUtil import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.annotations.ApiStatus import org.jetbrains.kotlin.lexer.KtTokens @@ -27,6 +28,10 @@ inline fun PsiElement.replaced(newElement: T): T { } } +fun PsiElement.deleteSingle() { + CodeEditUtil.removeChild(parent?.node ?: return, node ?: return) +} + fun KtBlockStringTemplateEntry.dropCurlyBracketsIfPossible(): KtStringTemplateEntryWithExpression { return if (canDropCurlyBrackets()) dropCurlyBrackets() else this } diff --git a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt index 03b8748093dc..e51882eaf78e 100644 --- a/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt +++ b/plugins/kotlin/core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt @@ -307,6 +307,10 @@ private fun deleteElementWithDelimiters(element: PsiElement) { parent.deleteChildRange(from, to) } +@Deprecated( + "Use 'org.jetbrains.kotlin.idea.base.psi.KotlinPsiModificationUtils' instead", + ReplaceWith("this.deleteSingle()", "org.jetbrains.kotlin.idea.base.psi.deleteSingle") +) fun PsiElement.deleteSingle() { CodeEditUtil.removeChild(parent?.node ?: return, node ?: return) } 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 cf8567965fa3..16f0700eefa9 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 @@ -11,10 +11,10 @@ import org.jetbrains.kotlin.idea.refactoring.CompositeRefactoringRunner import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveSource import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMover 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.Mover import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile @@ -71,7 +71,7 @@ abstract class MoveMemberOutOfObjectIntention(textGetter: () -> String) : SelfTa val conflicts = MultiMap().apply { addConflicts(element, this) } project.checkConflictsInteractively(conflicts) { runWriteAction { - Mover.Default(element, destination) + KotlinMover.Default(element, destination) deleteClassOrObjectIfEmpty() } } 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 42e06c88d2b9..880176849e63 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 @@ -52,7 +52,6 @@ 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.Mover import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders @@ -312,7 +311,7 @@ class MoveMemberToCompanionObjectIntention : SelfTargetingRangeIntention= 0) 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 656a7928c2d5..7625d629150b 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 @@ -12,7 +12,6 @@ import com.intellij.psi.PsiElement import com.intellij.psi.PsiNamedElement import com.intellij.psi.PsiReference import com.intellij.psi.search.GlobalSearchScope -import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.refactoring.move.MoveCallback @@ -36,56 +35,26 @@ import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.toLightElements import org.jetbrains.kotlin.idea.base.psi.kotlinFqName import org.jetbrains.kotlin.idea.base.resources.KotlinBundle -import org.jetbrains.kotlin.idea.base.searching.usages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.base.util.module import org.jetbrains.kotlin.idea.base.util.projectScope import org.jetbrains.kotlin.idea.base.util.quoteIfNeeded import org.jetbrains.kotlin.idea.base.util.restrictByFileType import org.jetbrains.kotlin.idea.codeInsight.shorten.addToBeShortenedDescendantsToWaitingSet import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests -import org.jetbrains.kotlin.idea.core.deleteSingle import org.jetbrains.kotlin.idea.refactoring.broadcastRefactoringExit import org.jetbrains.kotlin.idea.refactoring.move.* import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject -import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.utils.ifEmpty import org.jetbrains.kotlin.utils.keysToMap import kotlin.math.max import kotlin.math.min -interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration { - object Default : Mover { - override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration { - return when (targetContainer) { - is KtFile -> { - val declarationContainer: KtElement = - if (targetContainer.isScript()) targetContainer.script!!.blockExpression else targetContainer - declarationContainer.add(originalElement) as KtNamedDeclaration - } - is KtClassOrObject -> targetContainer.addDeclaration(originalElement) - else -> error("Unexpected element: ${targetContainer.getElementTextWithContext()}") - }.apply { - val container = originalElement.containingClassOrObject - if (container is KtObjectDeclaration && - container.isCompanion() && - container.declarations.singleOrNull() == originalElement && - KotlinFindUsagesHandlerFactory(container.project).createFindUsagesHandler(container, false) - .findReferencesToHighlight(container, LocalSearchScope(container.containingFile)).isEmpty() - ) { - container.deleteSingle() - } else { - originalElement.deleteSingle() - } - } - } - } -} - class MoveDeclarationsDescriptor @JvmOverloads constructor( val project: Project, val moveSource: KotlinMoveSource, @@ -122,7 +91,7 @@ private object ElementHashingStrategy : HashingStrategy { class MoveKotlinDeclarationsProcessor( val descriptor: MoveDeclarationsDescriptor, - val mover: Mover = Mover.Default, + val mover: KotlinMover = KotlinMover.Default, private val throwOnConflicts: Boolean = false ) : BaseRefactoringProcessor(descriptor.project) { companion object { 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 8b3b896e2811..779f190cbbd2 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 @@ -11,6 +11,7 @@ import com.intellij.refactoring.move.MoveCallback import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveSource import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMover import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* import org.jetbrains.kotlin.idea.statistics.KotlinMoveRefactoringFUSCollector.MoveRefactoringDestination import org.jetbrains.kotlin.idea.statistics.KotlinMoveRefactoringFUSCollector.MovedEntity @@ -68,7 +69,7 @@ internal class MoveKotlinNestedClassesModel( openInEditor = openInEditorCheckBox ) - val processor = MoveKotlinDeclarationsProcessor(descriptor, Mover.Default, throwOnConflicts) + val processor = MoveKotlinDeclarationsProcessor(descriptor, KotlinMover.Default, throwOnConflicts) return ModelResultWithFUSData( processor, 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 db1ae4bf267c..f5d13a73d695 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 @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.refactoring.createKotlinFile import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveSource import org.jetbrains.kotlin.idea.refactoring.move.KotlinMoveTarget +import org.jetbrains.kotlin.idea.refactoring.move.KotlinMover import org.jetbrains.kotlin.idea.refactoring.move.getTargetPackageFqName import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* import org.jetbrains.kotlin.idea.roots.getSuitableDestinationSourceRoots @@ -206,7 +207,7 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel( openInEditor = isOpenInEditor ) - val processor = MoveKotlinDeclarationsProcessor(moveDescriptor, Mover.Default, throwOnConflicts) + val processor = MoveKotlinDeclarationsProcessor(moveDescriptor, KotlinMover.Default, throwOnConflicts) return ModelResultWithFUSData( processor = processor, 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 cd70020033ed..b272be99b75c 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 @@ -323,6 +323,6 @@ internal class MoveKotlinTopLevelDeclarationsModel( analyzeConflicts = true, searchReferences = isSearchReferences ) - return MoveKotlinDeclarationsProcessor(options, Mover.Default, throwOnConflicts) + return MoveKotlinDeclarationsProcessor(options, KotlinMover.Default, throwOnConflicts) } } \ No newline at end of file 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 8717b1ab9ad7..f7c05994eef5 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 @@ -27,7 +27,6 @@ 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.MoveConflictChecker -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.Mover import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.resolve.languageVersionSettings import org.jetbrains.kotlin.idea.util.getFactoryForImplicitReceiverWithSubtypeOf @@ -268,7 +267,7 @@ class MoveKotlinMethodProcessor( changeMethodSignature() markInternalUsages(oldInternalUsages) - val movedMethod = Mover.Default(method, targetClassOrObject) + val movedMethod = KotlinMover.Default(method, targetClassOrObject) val oldToNewMethodMap = mapOf(method to movedMethod) newInternalUsages += restoreInternalUsages(movedMethod, oldToNewMethodMap) diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMover.kt b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMover.kt new file mode 100644 index 000000000000..67762902c2c2 --- /dev/null +++ b/plugins/kotlin/refactorings/kotlin.refactorings.common/src/org/jetbrains/kotlin/idea/refactoring/move/KotlinMover.kt @@ -0,0 +1,39 @@ +// 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.move + +import com.intellij.psi.search.LocalSearchScope +import org.jetbrains.kotlin.idea.base.psi.deleteSingle +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments + +interface KotlinMover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration { + object Default : KotlinMover { + override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration { + return when (targetContainer) { + is KtFile -> { + val declarationContainer: KtElement = + if (targetContainer.isScript()) targetContainer.script!!.blockExpression else targetContainer + declarationContainer.add(originalElement) as KtNamedDeclaration + } + is KtClassOrObject -> targetContainer.addDeclaration(originalElement) + else -> throw KotlinExceptionWithAttachments("Unexpected element") + .withAttachment("context", targetContainer.getElementTextWithContext()) + }.apply { + val container = originalElement.containingClassOrObject + if (container is KtObjectDeclaration && + container.isCompanion() && + container.declarations.singleOrNull() == originalElement && + KotlinMoveRefactoringSupport.getInstance() + .findReferencesToHighlight(container, LocalSearchScope(container.containingFile)) + .isEmpty() + ) { + container.deleteSingle() + } else { + originalElement.deleteSingle() + } + } + } + } +} \ No newline at end of file