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 d213f657af1..f15de2e5e1a 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 @@ -28,12 +28,10 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextComponentAccessor; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.util.Pass; import com.intellij.psi.*; -import com.intellij.refactoring.JavaRefactoringSettings; -import com.intellij.refactoring.MoveDestination; -import com.intellij.refactoring.PackageWrapper; -import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.*; import com.intellij.refactoring.classMembers.DependencyMemberInfoModel; import com.intellij.refactoring.classMembers.MemberInfoChange; 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.MoveHandler; 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.RefactoringDialog; import com.intellij.refactoring.util.CommonRefactoringUtil; @@ -48,6 +47,7 @@ import com.intellij.ui.ComboboxWithBrowseButton; import com.intellij.ui.RecentsManager; import com.intellij.ui.ReferenceEditorComboWithBrowseButton; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.text.UniqueNameGenerator; import com.intellij.util.ui.UIUtil; import kotlin.Function0; 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.ui.KotlinMemberSelectionPanel; 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.psi.JetFile; 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); } @@ -199,7 +202,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { } private void updateSuggestedFileName() { - tfFileNameInPackage.setText(MovePackage.guessNewFileName(getSelectedElementsToMove())); + tfFileNameInPackage.setText(MovePackage.guessNewFileName(sourceFile, getSelectedElementsToMove())); } private void initPackageChooser(String targetPackageName, PsiDirectory targetDirectory) { @@ -320,6 +323,21 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { 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 private KotlinMoveTarget selectMoveTarget() { String message = verifyBeforeRun(); @@ -331,17 +349,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { setErrorText(null); if (isMoveToPackage()) { - String packageName = getTargetPackage().trim(); - - 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); + final MoveDestination moveDestination = selectPackageBasedMoveDestination(); if (moveDestination == null) return null; final String targetFileName = tfFileNameInPackage.getText(); @@ -359,7 +367,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { return new DeferredJetFileKotlinMoveTarget( myProject, - new FqName(targetPackage.getQualifiedName()), + new FqName(getTargetPackage()), new Function0() { @Override 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 (isMoveToPackage()) { - String name = getTargetPackage().trim(); + String name = getTargetPackage(); if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) { return "\'" + name + "\' is invalid destination package name"; } @@ -451,7 +459,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { } protected final String getTargetPackage() { - return classPackageChooser.getText(); + return classPackageChooser.getText().trim(); } protected final String getTargetFilePath() { @@ -484,6 +492,65 @@ 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) { + //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); + } + finally { + if (moveCallback != null) { + moveCallback.refactoringCompleted(); + } + } + } + }, + EmptyRunnable.INSTANCE + ) + ); + + return; + } + } + MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions( elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, moveCallback ); 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 7880109c8e5..c7eced133a1 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,9 @@ fun createMoveUsageInfoIfPossible( return MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) } -public fun guessNewFileName(declarationsToMove: Collection): String? { +public fun guessNewFileName(sourceFile: JetFile, declarationsToMove: Collection): String? { if (declarationsToMove.isEmpty()) return null + if (sourceFile.getDeclarations() == declarationsToMove) return sourceFile.getName() val representative = declarationsToMove.firstOrNull { it is JetClassOrObject } ?: declarationsToMove.first() return "${representative.getName()}.${JetFileType.EXTENSION}" diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/foo.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/fun.kt similarity index 100% rename from idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/foo.kt rename to idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/fun.kt diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt index bafed43cfec..e02a42dded8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt @@ -246,7 +246,8 @@ enum class MoveAction { val moveTarget = config.getNullableString("targetPackage")?.let { packageName -> DeferredJetFileKotlinMoveTarget(project, FqName(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 -> JetFileKotlinMoveTarget(PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(filePath)!!) as JetFile)