Move: Suggest deleting file when all declarations are moved

This commit is contained in:
Alexey Sedunov
2015-05-15 17:10:25 +03:00
parent 6c4b895f72
commit 7bc5ec04c7
9 changed files with 104 additions and 88 deletions
@@ -173,11 +173,6 @@ public class JetClass extends JetTypeParameterListOwnerStub<KotlinClassStub> 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)) {
@@ -182,11 +182,6 @@ public class JetObjectDeclaration extends JetNamedDeclarationStub<KotlinObjectSt
return findChildByType(JetTokens.OBJECT_KEYWORD);
}
@Override
public void delete() throws IncorrectOperationException {
JetPsiUtil.deleteClass(this);
}
@Override
public ItemPresentation getPresentation() {
return ItemPresentationProviders.getItemPresentation(this);
@@ -297,18 +297,6 @@ public class JetPsiUtil {
return null;
}
public static void deleteClass(@NotNull JetClassOrObject clazz) {
CheckUtil.checkWritable(clazz);
JetFile file = clazz.getContainingJetFile();
if (!clazz.isTopLevel() || file.getDeclarations().size() > 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();
@@ -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) {
@@ -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<PsiFile, String>() {
@Override
public String invoke(PsiFile file) {
return file.getName();
}
}
)
);
PsiDirectory targetDirectory = ApplicationPackage.runWriteAction(
new Function0<PsiDirectory>() {
@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<PsiFile, String>() {
@Override
public String invoke(PsiFile file) {
return file.getName();
}
finally {
if (moveCallback != null) {
moveCallback.refactoringCompleted();
}
)
);
PsiDirectory targetDirectory = ApplicationPackage.runWriteAction(
new Function0<PsiDirectory>() {
@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$));
}
@@ -156,8 +156,10 @@ fun createMoveUsageInfoIfPossible(
}
public fun guessNewFileName(sourceFile: JetFile, declarationsToMove: Collection<JetNamedDeclaration>): 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}"
@@ -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<PsiElement>,
askUser: Boolean
): Collection<PsiElement> {
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
}
}
@@ -0,0 +1,5 @@
package source
import library.B
import library.bar