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); return hasModifier(JetTokens.INNER_KEYWORD);
} }
@Override
public void delete() throws IncorrectOperationException {
JetPsiUtil.deleteClass(this);
}
@Override @Override
public boolean isEquivalentTo(PsiElement another) { public boolean isEquivalentTo(PsiElement another) {
if (super.isEquivalentTo(another)) { if (super.isEquivalentTo(another)) {
@@ -182,11 +182,6 @@ public class JetObjectDeclaration extends JetNamedDeclarationStub<KotlinObjectSt
return findChildByType(JetTokens.OBJECT_KEYWORD); return findChildByType(JetTokens.OBJECT_KEYWORD);
} }
@Override
public void delete() throws IncorrectOperationException {
JetPsiUtil.deleteClass(this);
}
@Override @Override
public ItemPresentation getPresentation() { public ItemPresentation getPresentation() {
return ItemPresentationProviders.getItemPresentation(this); return ItemPresentationProviders.getItemPresentation(this);
@@ -297,18 +297,6 @@ public class JetPsiUtil {
return null; 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 @Nullable
public static Name getAliasName(@NotNull JetImportDirective importDirective) { public static Name getAliasName(@NotNull JetImportDirective importDirective) {
String aliasName = importDirective.getAliasName(); String aliasName = importDirective.getAliasName();
@@ -86,6 +86,7 @@ public class MoveKotlinTopLevelDeclarationsOptions(
val searchInCommentsAndStrings: Boolean = true, val searchInCommentsAndStrings: Boolean = true,
val searchInNonCode: Boolean = true, val searchInNonCode: Boolean = true,
val updateInternalReferences: Boolean = true, val updateInternalReferences: Boolean = true,
val deleteSourceFile: Boolean = false,
val moveCallback: MoveCallback? = null val moveCallback: MoveCallback? = null
) )
@@ -282,6 +283,10 @@ public class MoveKotlinTopLevelDeclarationsProcessor(
} }
} }
if (options.deleteSourceFile) {
options.sourceFile.delete()
}
nonCodeUsages = postProcessMoveUsages(usageList, oldToNewElementsMapping).toTypedArray() nonCodeUsages = postProcessMoveUsages(usageList, oldToNewElementsMapping).toTypedArray()
} }
catch (e: IncorrectOperationException) { catch (e: IncorrectOperationException) {
@@ -505,67 +505,80 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
try { try {
if (isMoveToPackage() && elementsToMove.equals(sourceFile.getDeclarations())) { boolean deleteSourceFile = false;
final MoveDestination moveDestination = selectPackageBasedMoveDestination();
//noinspection ConstantConditions if (elementsToMove.size() == sourceFile.getDeclarations().size()) {
PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceFile); if (isMoveToPackage()) {
final String targetFileName = tfFileNameInPackage.getText(); final MoveDestination moveDestination = selectPackageBasedMoveDestination();
if (targetDir == null || targetDir.findFile(targetFileName) == null) {
//noinspection ConstantConditions //noinspection ConstantConditions
final String temporaryName = UniqueNameGenerator.generateUniqueName( PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceFile);
"temp", final String targetFileName = tfFileNameInPackage.getText();
"", if (targetDir == null || targetDir.findFile(targetFileName) == null) {
".kt", //noinspection ConstantConditions
KotlinPackage.map( final String temporaryName = UniqueNameGenerator.generateUniqueName(
sourceFile.getContainingDirectory().getFiles(), "temp",
new Function1<PsiFile, String>() { "",
@Override ".kt",
public String invoke(PsiFile file) { KotlinPackage.map(
return file.getName(); 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);
} }
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()
); );
if (ret == Messages.CANCEL) return;
return; deleteSourceFile = ret == Messages.YES;
} }
} }
MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions( 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$)); invokeRefactoring(new MoveKotlinTopLevelDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE$));
} }
@@ -156,8 +156,10 @@ fun createMoveUsageInfoIfPossible(
} }
public fun guessNewFileName(sourceFile: JetFile, declarationsToMove: Collection<JetNamedDeclaration>): String? { public fun guessNewFileName(sourceFile: JetFile, declarationsToMove: Collection<JetNamedDeclaration>): String? {
assert(sourceFile.getDeclarations().containsAll(declarationsToMove))
if (declarationsToMove.isEmpty()) return null 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() val representative = declarationsToMove.firstOrNull { it is JetClassOrObject } ?: declarationsToMove.first()
return "${representative.getName()}.${JetFileType.EXTENSION}" return "${representative.getName()}.${JetFileType.EXTENSION}"
@@ -24,30 +24,29 @@ import com.intellij.openapi.util.Conditions
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiParameter 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.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor import com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor
import com.intellij.refactoring.safeDelete.NonCodeUsageSearchInfo 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.usageView.UsageInfo
import com.intellij.util.Processor
import org.jetbrains.kotlin.asJava.* import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality 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.JetBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil import org.jetbrains.kotlin.idea.refactoring.JetRefactoringUtil
import org.jetbrains.kotlin.idea.references.JetReference 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.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() { public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement() override fun handlesElement(element: PsiElement): Boolean = element.canDeleteElement()
@@ -327,4 +326,18 @@ public class KotlinSafeDeleteProcessor : JavaSafeDeleteProcessor() {
else -> super.getElementsToSearch(element, module, allElementsToDelete) 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