Move: Move entire file if all declarations are selected ("Move to Package" mode)

This commit is contained in:
Alexey Sedunov
2015-05-14 19:57:28 +03:00
parent 7cf7a14d9b
commit 38ae9349fc
4 changed files with 91 additions and 22 deletions
@@ -28,12 +28,10 @@ import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextComponentAccessor; import com.intellij.openapi.ui.TextComponentAccessor;
import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.Pass;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.*;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.PackageWrapper;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.classMembers.DependencyMemberInfoModel; import com.intellij.refactoring.classMembers.DependencyMemberInfoModel;
import com.intellij.refactoring.classMembers.MemberInfoChange; import com.intellij.refactoring.classMembers.MemberInfoChange;
import com.intellij.refactoring.classMembers.MemberInfoChangeListener; import com.intellij.refactoring.classMembers.MemberInfoChangeListener;
@@ -41,6 +39,7 @@ import com.intellij.refactoring.classMembers.UsesMemberDependencyGraph;
import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler; import com.intellij.refactoring.move.MoveHandler;
import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox; import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox;
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor;
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo; import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo;
import com.intellij.refactoring.ui.RefactoringDialog; import com.intellij.refactoring.ui.RefactoringDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.CommonRefactoringUtil;
@@ -48,6 +47,7 @@ import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.RecentsManager; import com.intellij.ui.RecentsManager;
import com.intellij.ui.ReferenceEditorComboWithBrowseButton; import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
import com.intellij.util.IncorrectOperationException; import com.intellij.util.IncorrectOperationException;
import com.intellij.util.text.UniqueNameGenerator;
import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.UIUtil;
import kotlin.Function0; import kotlin.Function0;
import kotlin.Function1; import kotlin.Function1;
@@ -61,6 +61,7 @@ import org.jetbrains.kotlin.idea.refactoring.move.MovePackage;
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*; import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinMemberSelectionPanel; import org.jetbrains.kotlin.idea.refactoring.ui.KotlinMemberSelectionPanel;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinMemberSelectionTable; import org.jetbrains.kotlin.idea.refactoring.ui.KotlinMemberSelectionTable;
import org.jetbrains.kotlin.idea.util.application.ApplicationPackage;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetFile;
import org.jetbrains.kotlin.psi.JetNamedDeclaration; import org.jetbrains.kotlin.psi.JetNamedDeclaration;
@@ -181,7 +182,9 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
) )
); );
String suggestedText = previousDeclarations.isEmpty() ? "" : MovePackage.guessNewFileName(previousDeclarations); String suggestedText = previousDeclarations.isEmpty()
? ""
: MovePackage.guessNewFileName(sourceFile, previousDeclarations);
return tfFileNameInPackage.getText().equals(suggestedText); return tfFileNameInPackage.getText().equals(suggestedText);
} }
@@ -199,7 +202,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
private void updateSuggestedFileName() { private void updateSuggestedFileName() {
tfFileNameInPackage.setText(MovePackage.guessNewFileName(getSelectedElementsToMove())); tfFileNameInPackage.setText(MovePackage.guessNewFileName(sourceFile, getSelectedElementsToMove()));
} }
private void initPackageChooser(String targetPackageName, PsiDirectory targetDirectory) { private void initPackageChooser(String targetPackageName, PsiDirectory targetDirectory) {
@@ -320,6 +323,21 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
refactoringSettings.MOVE_PREVIEW_USAGES = isPreviewUsages(); refactoringSettings.MOVE_PREVIEW_USAGES = isPreviewUsages();
} }
@Nullable
private MoveDestination selectPackageBasedMoveDestination() {
String packageName = getTargetPackage();
RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, packageName);
PackageWrapper targetPackage = new PackageWrapper(PsiManager.getInstance(myProject), packageName);
if (!targetPackage.exists()) {
int ret = Messages.showYesNoDialog(myProject, RefactoringBundle.message("package.does.not.exist", packageName),
RefactoringBundle.message("move.title"), Messages.getQuestionIcon());
if (ret != Messages.YES) return null;
}
return ((DestinationFolderComboBox) destinationFolderCB).selectDirectory(targetPackage, false);
}
@Nullable @Nullable
private KotlinMoveTarget selectMoveTarget() { private KotlinMoveTarget selectMoveTarget() {
String message = verifyBeforeRun(); String message = verifyBeforeRun();
@@ -331,17 +349,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
setErrorText(null); setErrorText(null);
if (isMoveToPackage()) { if (isMoveToPackage()) {
String packageName = getTargetPackage().trim(); final MoveDestination moveDestination = selectPackageBasedMoveDestination();
RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, packageName);
PackageWrapper targetPackage = new PackageWrapper(PsiManager.getInstance(myProject), packageName);
if (!targetPackage.exists()) {
int ret = Messages.showYesNoDialog(myProject, RefactoringBundle.message("package.does.not.exist", packageName),
RefactoringBundle.message("move.title"), Messages.getQuestionIcon());
if (ret != Messages.YES) return null;
}
final MoveDestination moveDestination = ((DestinationFolderComboBox) destinationFolderCB).selectDirectory(targetPackage, false);
if (moveDestination == null) return null; if (moveDestination == null) return null;
final String targetFileName = tfFileNameInPackage.getText(); final String targetFileName = tfFileNameInPackage.getText();
@@ -359,7 +367,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return new DeferredJetFileKotlinMoveTarget( return new DeferredJetFileKotlinMoveTarget(
myProject, myProject,
new FqName(targetPackage.getQualifiedName()), new FqName(getTargetPackage()),
new Function0<JetFile>() { new Function0<JetFile>() {
@Override @Override
public JetFile invoke() { public JetFile invoke() {
@@ -409,7 +417,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
if (memberTable.getSelectedMemberInfos().isEmpty()) return "At least one member must be selected"; if (memberTable.getSelectedMemberInfos().isEmpty()) return "At least one member must be selected";
if (isMoveToPackage()) { if (isMoveToPackage()) {
String name = getTargetPackage().trim(); String name = getTargetPackage();
if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) { if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) {
return "\'" + name + "\' is invalid destination package name"; return "\'" + name + "\' is invalid destination package name";
} }
@@ -451,7 +459,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
protected final String getTargetPackage() { protected final String getTargetPackage() {
return classPackageChooser.getText(); return classPackageChooser.getText().trim();
} }
protected final String getTargetFilePath() { protected final String getTargetFilePath() {
@@ -484,6 +492,65 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
try { 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) {
//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);
}
finally {
if (moveCallback != null) {
moveCallback.refactoringCompleted();
}
}
}
},
EmptyRunnable.INSTANCE
)
);
return;
}
}
MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions( MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions(
elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, moveCallback elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, moveCallback
); );
@@ -156,8 +156,9 @@ fun createMoveUsageInfoIfPossible(
return MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) return MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false)
} }
public fun guessNewFileName(declarationsToMove: Collection<JetNamedDeclaration>): String? { public fun guessNewFileName(sourceFile: JetFile, declarationsToMove: Collection<JetNamedDeclaration>): String? {
if (declarationsToMove.isEmpty()) return null if (declarationsToMove.isEmpty()) return null
if (sourceFile.getDeclarations() == declarationsToMove) 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}"
@@ -246,7 +246,8 @@ enum class MoveAction {
val moveTarget = config.getNullableString("targetPackage")?.let { packageName -> val moveTarget = config.getNullableString("targetPackage")?.let { packageName ->
DeferredJetFileKotlinMoveTarget(project, FqName(packageName)) { DeferredJetFileKotlinMoveTarget(project, FqName(packageName)) {
val moveDestination = MultipleRootsMoveDestination(PackageWrapper(mainFile.getManager(), packageName)) val moveDestination = MultipleRootsMoveDestination(PackageWrapper(mainFile.getManager(), packageName))
createKotlinFile(guessNewFileName(listOf(elementToMove))!!, moveDestination.getTargetDirectory(mainFile)) createKotlinFile(guessNewFileName(mainFile as JetFile, listOf(elementToMove))!!,
moveDestination.getTargetDirectory(mainFile))
} }
} ?: config.getString("targetFile").let { filePath -> } ?: config.getString("targetFile").let { filePath ->
JetFileKotlinMoveTarget(PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(filePath)!!) as JetFile) JetFileKotlinMoveTarget(PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(filePath)!!) as JetFile)