From 7bc5ec04c768595c02fbfcf687d27212aef90776 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 15 May 2015 17:10:25 +0300 Subject: [PATCH] Move: Suggest deleting file when all declarations are moved --- .../org/jetbrains/kotlin/psi/JetClass.java | 5 - .../kotlin/psi/JetObjectDeclaration.java | 5 - .../org/jetbrains/kotlin/psi/JetPsiUtil.java | 12 -- ...MoveKotlinTopLevelDeclarationsProcessor.kt | 5 + .../MoveKotlinTopLevelDeclarationsDialog.java | 117 ++++++++++-------- .../kotlin/idea/refactoring/move/moveUtils.kt | 4 +- .../safeDelete/KotlinSafeDeleteProcessor.kt | 39 ++++-- .../misc/internalReferences/after/source/A.kt | 5 + .../moveClassFromDefaultPackage/after/X.kt | 0 9 files changed, 104 insertions(+), 88 deletions(-) create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/A.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveClassFromDefaultPackage/after/X.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClass.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClass.java index 273e202d99c..1b68e83e615 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClass.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetClass.java @@ -173,11 +173,6 @@ public class JetClass extends JetTypeParameterListOwnerStub imp return hasModifier(JetTokens.INNER_KEYWORD); } - @Override - public void delete() throws IncorrectOperationException { - JetPsiUtil.deleteClass(this); - } - @Override public boolean isEquivalentTo(PsiElement another) { if (super.isEquivalentTo(another)) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetObjectDeclaration.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetObjectDeclaration.java index 330eb6645b1..7c3dc41e03f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetObjectDeclaration.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetObjectDeclaration.java @@ -182,11 +182,6 @@ public class JetObjectDeclaration extends JetNamedDeclarationStub 1) { - PsiElement parent = clazz.getParent(); - CodeEditUtil.removeChild(parent.getNode(), clazz.getNode()); - } - else { - file.delete(); - } - } - @Nullable public static Name getAliasName(@NotNull JetImportDirective importDirective) { String aliasName = importDirective.getAliasName(); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt index 2800ebcbb7a..712ebee9f74 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt @@ -86,6 +86,7 @@ public class MoveKotlinTopLevelDeclarationsOptions( val searchInCommentsAndStrings: Boolean = true, val searchInNonCode: Boolean = true, val updateInternalReferences: Boolean = true, + val deleteSourceFile: Boolean = false, val moveCallback: MoveCallback? = null ) @@ -282,6 +283,10 @@ public class MoveKotlinTopLevelDeclarationsProcessor( } } + if (options.deleteSourceFile) { + options.sourceFile.delete() + } + nonCodeUsages = postProcessMoveUsages(usageList, oldToNewElementsMapping).toTypedArray() } catch (e: IncorrectOperationException) { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java index 25ca1bfdee3..cf950cda473 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java @@ -505,67 +505,80 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { } try { - if (isMoveToPackage() && elementsToMove.equals(sourceFile.getDeclarations())) { - final MoveDestination moveDestination = selectPackageBasedMoveDestination(); - //noinspection ConstantConditions - PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceFile); - final String targetFileName = tfFileNameInPackage.getText(); - if (targetDir == null || targetDir.findFile(targetFileName) == null) { + boolean deleteSourceFile = false; + + if (elementsToMove.size() == sourceFile.getDeclarations().size()) { + if (isMoveToPackage()) { + final MoveDestination moveDestination = selectPackageBasedMoveDestination(); //noinspection ConstantConditions - final String temporaryName = UniqueNameGenerator.generateUniqueName( - "temp", - "", - ".kt", - KotlinPackage.map( - sourceFile.getContainingDirectory().getFiles(), - new Function1() { - @Override - public String invoke(PsiFile file) { - return file.getName(); - } - } - ) - ); - PsiDirectory targetDirectory = ApplicationPackage.runWriteAction( - new Function0() { - @Override - public PsiDirectory invoke() { - sourceFile.setName(temporaryName); - return moveDestination.getTargetDirectory(sourceFile); - } - } - ); - invokeRefactoring( - new MoveFilesOrDirectoriesProcessor( - myProject, - new PsiElement[] {sourceFile}, - targetDirectory, - RefactoringSettings.getInstance().MOVE_SEARCH_FOR_REFERENCES_FOR_FILE, - isSearchInComments(), - isSearchInNonJavaFiles(), - new MoveCallback() { - @Override - public void refactoringCompleted() { - try { - sourceFile.setName(targetFileName); + PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceFile); + final String targetFileName = tfFileNameInPackage.getText(); + if (targetDir == null || targetDir.findFile(targetFileName) == null) { + //noinspection ConstantConditions + final String temporaryName = UniqueNameGenerator.generateUniqueName( + "temp", + "", + ".kt", + KotlinPackage.map( + sourceFile.getContainingDirectory().getFiles(), + new Function1() { + @Override + public String invoke(PsiFile file) { + return file.getName(); } - finally { - if (moveCallback != null) { - moveCallback.refactoringCompleted(); + } + ) + ); + PsiDirectory targetDirectory = ApplicationPackage.runWriteAction( + new Function0() { + @Override + public PsiDirectory invoke() { + sourceFile.setName(temporaryName); + return moveDestination.getTargetDirectory(sourceFile); + } + } + ); + invokeRefactoring( + new MoveFilesOrDirectoriesProcessor( + myProject, + new PsiElement[] {sourceFile}, + targetDirectory, + RefactoringSettings.getInstance().MOVE_SEARCH_FOR_REFERENCES_FOR_FILE, + isSearchInComments(), + isSearchInNonJavaFiles(), + new MoveCallback() { + @Override + public void refactoringCompleted() { + try { + sourceFile.setName(targetFileName); + } + finally { + if (moveCallback != null) { + moveCallback.refactoringCompleted(); + } } } - } - }, - EmptyRunnable.INSTANCE - ) + }, + EmptyRunnable.INSTANCE + ) + ); + + return; + } + + int ret = Messages.showYesNoCancelDialog( + myProject, + "You are going to move all declarations out of '" + sourceFile.getVirtualFile().getPath() + "'. Do you want to delete this file?", + RefactoringBundle.message("move.title"), + Messages.getQuestionIcon() ); - - return; + if (ret == Messages.CANCEL) return; + deleteSourceFile = ret == Messages.YES; } } MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions( - sourceFile, elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, moveCallback + sourceFile, elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, deleteSourceFile, moveCallback ); invokeRefactoring(new MoveKotlinTopLevelDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE$)); } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt index 72c49247389..8acea9f02ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt @@ -156,8 +156,10 @@ fun createMoveUsageInfoIfPossible( } public fun guessNewFileName(sourceFile: JetFile, declarationsToMove: Collection): String? { + assert(sourceFile.getDeclarations().containsAll(declarationsToMove)) + if (declarationsToMove.isEmpty()) return null - if (sourceFile.getDeclarations() == declarationsToMove) return sourceFile.getName() + if (sourceFile.getDeclarations().size() == declarationsToMove.size()) return sourceFile.getName() val representative = declarationsToMove.firstOrNull { it is JetClassOrObject } ?: declarationsToMove.first() return "${representative.getName()}.${JetFileType.EXTENSION}" 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 4dd550f1234..4aa85be6583 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/safeDelete/KotlinSafeDeleteProcessor.kt @@ -24,30 +24,29 @@ import com.intellij.openapi.util.Conditions import com.intellij.psi.PsiElement import com.intellij.psi.PsiMethod import com.intellij.psi.PsiParameter -import com.intellij.psi.PsiReference -import com.intellij.psi.impl.search.ConstructorReferencesSearchHelper -import com.intellij.psi.search.SearchRequestCollector -import com.intellij.psi.search.SearchSession import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo -import com.intellij.refactoring.safeDelete.usageInfo.* +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverrideAnnotation +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteOverridingMethodUsageInfo +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo +import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceSimpleDeleteUsageInfo import com.intellij.usageView.UsageInfo -import com.intellij.util.Processor import org.jetbrains.kotlin.asJava.* import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.Modality -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil import org.jetbrains.kotlin.idea.references.JetReference -import java.util.* -import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType -import org.jetbrains.kotlin.psi.psiUtil.deleteElementAndCleanParent -import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.search.usagesSearch.processDelegationCallConstructorUsages +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.deleteElementAndCleanParent +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import java.util.ArrayList +import java.util.Collections public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() { override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement() @@ -327,4 +326,18 @@ public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() { else -> super.getElementsToSearch(element, module, allElementsToDelete) } } + + override fun getAdditionalElementsToDelete( + element: PsiElement, + allElementsToDelete: MutableCollection, + askUser: Boolean + ): Collection { + val filesToDelete = allElementsToDelete + .filter { it is JetNamedDeclaration && it.getContainingJetFile() == it.getParent() } + .groupBy { (it as JetNamedDeclaration).getContainingJetFile() } + .filter { it.getKey().getDeclarations().size() == it.getValue().size() } + .keySet() + return super.getAdditionalElementsToDelete(element, allElementsToDelete, askUser)?.let { it + filesToDelete } + ?: filesToDelete + } } diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/A.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/A.kt new file mode 100644 index 00000000000..2174b98f5e6 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/A.kt @@ -0,0 +1,5 @@ +package source + +import library.B +import library.bar + diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveClassFromDefaultPackage/after/X.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveClassFromDefaultPackage/after/X.kt new file mode 100644 index 00000000000..e69de29bb2d