Refactor of Move refactoring
1) Expect/actual move checkbox is visible when expect/actual listed in selection list only 2) Fixed invalid temprary renaming (when source file name exists in target directory) 3) Fixed invalid file delition when "delete all files" checkbox is unchecked (KT-36072) 4) Removed UpdatePackageDirective checkbox (now it always updated instead of unpredicted behavior) 5) Removed SpecifyFileNameInPackage checkbox (in case of it redundancy) 6) Wrap resolve operations into the process 7) Minor refactorings 8) Fixed MPP declaration move top level declarations
This commit is contained in:
+1
-9
@@ -64,9 +64,7 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
|
|||||||
"isSearchReferences = $isSearchReferences\n" +
|
"isSearchReferences = $isSearchReferences\n" +
|
||||||
"isSearchInComments = $isSearchInComments\n" +
|
"isSearchInComments = $isSearchInComments\n" +
|
||||||
"isSearchInNonJavaFiles = $isSearchInNonJavaFiles\n" +
|
"isSearchInNonJavaFiles = $isSearchInNonJavaFiles\n" +
|
||||||
"isDeleteEmptyFiles = $isDeleteEmptyFiles\n" +
|
"isDeleteEmptyFiles = $isDeleteEmptyFiles\n"
|
||||||
"isUpdatePackageDirective = $isUpdatePackageDirective\n" +
|
|
||||||
"isFullFileMove = $isFullFileMove"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinAwareMoveFilesOrDirectoriesModel.testDataString(): String {
|
private fun KotlinAwareMoveFilesOrDirectoriesModel.testDataString(): String {
|
||||||
@@ -153,10 +151,6 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
|
|||||||
targetDirectory: PsiDirectory?,
|
targetDirectory: PsiDirectory?,
|
||||||
targetFile: KtFile?,
|
targetFile: KtFile?,
|
||||||
moveToPackage: Boolean,
|
moveToPackage: Boolean,
|
||||||
searchInComments: Boolean,
|
|
||||||
searchForTextOccurrences: Boolean,
|
|
||||||
deleteEmptySourceFiles: Boolean,
|
|
||||||
moveMppDeclarations: Boolean,
|
|
||||||
moveCallback: MoveCallback?
|
moveCallback: MoveCallback?
|
||||||
) {
|
) {
|
||||||
val selectedElementsToMove = mutableSetOf<KtNamedDeclaration>()
|
val selectedElementsToMove = mutableSetOf<KtNamedDeclaration>()
|
||||||
@@ -180,8 +174,6 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
|
|||||||
isSearchInComments = randomBoolean(),
|
isSearchInComments = randomBoolean(),
|
||||||
isSearchInNonJavaFiles = randomBoolean(),
|
isSearchInNonJavaFiles = randomBoolean(),
|
||||||
isDeleteEmptyFiles = randomBoolean(),
|
isDeleteEmptyFiles = randomBoolean(),
|
||||||
isUpdatePackageDirective = randomBoolean(),
|
|
||||||
isFullFileMove = randomBoolean(),
|
|
||||||
applyMPPDeclarations = true,
|
applyMPPDeclarations = true,
|
||||||
moveCallback = null
|
moveCallback = null
|
||||||
)
|
)
|
||||||
|
|||||||
+9
-16
@@ -68,10 +68,6 @@ private val defaultHandlerActions = object : MoveKotlinDeclarationsHandlerAction
|
|||||||
targetDirectory: PsiDirectory?,
|
targetDirectory: PsiDirectory?,
|
||||||
targetFile: KtFile?,
|
targetFile: KtFile?,
|
||||||
moveToPackage: Boolean,
|
moveToPackage: Boolean,
|
||||||
searchInComments: Boolean,
|
|
||||||
searchForTextOccurrences: Boolean,
|
|
||||||
deleteEmptySourceFiles: Boolean,
|
|
||||||
moveMppDeclarations: Boolean,
|
|
||||||
moveCallback: MoveCallback?
|
moveCallback: MoveCallback?
|
||||||
) = MoveKotlinTopLevelDeclarationsDialog(
|
) = MoveKotlinTopLevelDeclarationsDialog(
|
||||||
project,
|
project,
|
||||||
@@ -80,10 +76,6 @@ private val defaultHandlerActions = object : MoveKotlinDeclarationsHandlerAction
|
|||||||
targetDirectory,
|
targetDirectory,
|
||||||
targetFile,
|
targetFile,
|
||||||
moveToPackage,
|
moveToPackage,
|
||||||
searchInComments,
|
|
||||||
searchForTextOccurrences,
|
|
||||||
deleteEmptySourceFiles,
|
|
||||||
moveMppDeclarations,
|
|
||||||
moveCallback
|
moveCallback
|
||||||
).show()
|
).show()
|
||||||
|
|
||||||
@@ -180,6 +172,15 @@ class MoveKotlinDeclarationsHandler internal constructor(private val handlerActi
|
|||||||
}
|
}
|
||||||
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, initialTargetElement)
|
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, initialTargetElement)
|
||||||
|
|
||||||
|
if (!ApplicationManager.getApplication().isUnitTestMode && elementsToSearch.any { it.isExpectDeclaration() || it.isEffectivelyActual() }
|
||||||
|
) {
|
||||||
|
val message = RefactoringBundle.getCannotRefactorMessage(
|
||||||
|
"This refactoring will move selected declaration without it's expect/actual counterparts that may lead to compilation errors.\n\"Do you wish to proceed?\"")
|
||||||
|
val title = RefactoringBundle.getCannotRefactorMessage("MPP declarations does not supported by this refactoring.")
|
||||||
|
val proceedWithIncompleteRefactoring = Messages.showYesNoDialog(project, message, title, Messages.getWarningIcon())
|
||||||
|
if (proceedWithIncompleteRefactoring != Messages.YES) return true
|
||||||
|
}
|
||||||
|
|
||||||
handlerActions.invokeKotlinAwareMoveFilesOrDirectoriesRefactoring(
|
handlerActions.invokeKotlinAwareMoveFilesOrDirectoriesRefactoring(
|
||||||
project, initialTargetDirectory, ktFileElements, callback
|
project, initialTargetDirectory, ktFileElements, callback
|
||||||
)
|
)
|
||||||
@@ -193,10 +194,6 @@ class MoveKotlinDeclarationsHandler internal constructor(private val handlerActi
|
|||||||
val targetDirectory = if (targetContainer != null) {
|
val targetDirectory = if (targetContainer != null) {
|
||||||
MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements)
|
MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements)
|
||||||
} else null
|
} else null
|
||||||
val searchInComments = KotlinRefactoringSettings.instance.MOVE_SEARCH_IN_COMMENTS
|
|
||||||
val searchInText = KotlinRefactoringSettings.instance.MOVE_SEARCH_FOR_TEXT
|
|
||||||
val deleteEmptySourceFiles = KotlinRefactoringSettings.instance.MOVE_DELETE_EMPTY_SOURCE_FILES
|
|
||||||
val moveMppDeclarations = KotlinRefactoringSettings.instance.MOVE_MPP_DECLARATIONS
|
|
||||||
val targetFile = targetContainer as? KtFile
|
val targetFile = targetContainer as? KtFile
|
||||||
val moveToPackage = targetContainer !is KtFile
|
val moveToPackage = targetContainer !is KtFile
|
||||||
|
|
||||||
@@ -207,10 +204,6 @@ class MoveKotlinDeclarationsHandler internal constructor(private val handlerActi
|
|||||||
targetDirectory,
|
targetDirectory,
|
||||||
targetFile,
|
targetFile,
|
||||||
moveToPackage,
|
moveToPackage,
|
||||||
searchInComments,
|
|
||||||
searchInText,
|
|
||||||
deleteEmptySourceFiles,
|
|
||||||
moveMppDeclarations,
|
|
||||||
callback
|
callback
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
-4
@@ -31,10 +31,6 @@ internal interface MoveKotlinDeclarationsHandlerActions {
|
|||||||
targetDirectory: PsiDirectory?,
|
targetDirectory: PsiDirectory?,
|
||||||
targetFile: KtFile?,
|
targetFile: KtFile?,
|
||||||
moveToPackage: Boolean,
|
moveToPackage: Boolean,
|
||||||
searchInComments: Boolean,
|
|
||||||
searchForTextOccurrences: Boolean,
|
|
||||||
deleteEmptySourceFiles: Boolean,
|
|
||||||
moveMppDeclarations: Boolean,
|
|
||||||
moveCallback: MoveCallback?
|
moveCallback: MoveCallback?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -363,7 +363,7 @@ class MoveKotlinDeclarationsProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor.deleteSourceFiles) {
|
if (descriptor.deleteSourceFiles && sourceFile.declarations.isEmpty()) {
|
||||||
sourceFile.delete()
|
sourceFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-9
@@ -66,22 +66,19 @@ class MoveToKotlinFileProcessor @JvmOverloads constructor(
|
|||||||
return super.showConflicts(conflicts, usages)
|
return super.showConflicts(conflicts, usages)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign a temporary name to file-under-move to avoid naming conflict during the refactoring
|
private fun renameFileTemporarilyIfNeeded() {
|
||||||
private fun renameFileTemporarily() {
|
if (targetDirectory.findFile(sourceFile.name) == null) return
|
||||||
if (targetDirectory.findFile(targetFileName) == null) return
|
|
||||||
|
val containingDirectory = sourceFile.containingDirectory ?: return
|
||||||
|
|
||||||
val temporaryName = UniqueNameGenerator.generateUniqueName("temp", "", ".kt") {
|
val temporaryName = UniqueNameGenerator.generateUniqueName("temp", "", ".kt") {
|
||||||
sourceFile.containingDirectory!!.findFile(it) == null
|
containingDirectory.findFile(it) == null
|
||||||
}
|
}
|
||||||
sourceFile.name = temporaryName
|
sourceFile.name = temporaryName
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun performRefactoring(usages: Array<UsageInfo>) {
|
override fun performRefactoring(usages: Array<UsageInfo>) {
|
||||||
val needTemporaryRename = targetDirectory.findFile(sourceFile.name) != null
|
renameFileTemporarilyIfNeeded()
|
||||||
if (needTemporaryRename) {
|
|
||||||
renameFileTemporarily()
|
|
||||||
}
|
|
||||||
|
|
||||||
super.performRefactoring(usages)
|
super.performRefactoring(usages)
|
||||||
sourceFile.name = targetFileName
|
sourceFile.name = targetFileName
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -129,9 +129,11 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
|
|||||||
|
|
||||||
if (targetContainer is KtClassOrObject) {
|
if (targetContainer is KtClassOrObject) {
|
||||||
val targetClass = targetContainer as KtClassOrObject?
|
val targetClass = targetContainer as KtClassOrObject?
|
||||||
for (member in targetClass!!.declarations) {
|
if (targetClass != null) {
|
||||||
if (member is KtClassOrObject && className == member.getName()) {
|
for (member in targetClass.declarations) {
|
||||||
throw ConfigurationException(RefactoringBundle.message("inner.class.exists", className, targetClass.name))
|
if (member is KtClassOrObject && className == member.getName()) {
|
||||||
|
throw ConfigurationException(RefactoringBundle.message("inner.class.exists", className, targetClass.name))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-19
@@ -62,6 +62,15 @@
|
|||||||
<text resource-bundle="messages/KotlinBundle" key="label.text.to.file"/>
|
<text resource-bundle="messages/KotlinBundle" key="label.text.to.file"/>
|
||||||
</properties>
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
|
<component id="4934c" class="javax.swing.JLabel">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="5" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<labelFor value="67853"/>
|
||||||
|
<text resource-bundle="messages/KotlinBundle" key="label.text.file.name"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
<component id="67853" class="javax.swing.JTextField" binding="tfFileNameInPackage" default-binding="true">
|
<component id="67853" class="javax.swing.JTextField" binding="tfFileNameInPackage" default-binding="true">
|
||||||
<constraints>
|
<constraints>
|
||||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
|
||||||
@@ -72,16 +81,6 @@
|
|||||||
<enabled value="false"/>
|
<enabled value="false"/>
|
||||||
</properties>
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
<component id="e1add" class="javax.swing.JCheckBox" binding="cbSpecifyFileNameInPackage">
|
|
||||||
<constraints>
|
|
||||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="0" indent="5" use-parent-layout="false"/>
|
|
||||||
</constraints>
|
|
||||||
<properties>
|
|
||||||
<horizontalAlignment value="2"/>
|
|
||||||
<horizontalTextPosition value="11"/>
|
|
||||||
<text resource-bundle="messages/KotlinBundle" key="label.text.file.name"/>
|
|
||||||
</properties>
|
|
||||||
</component>
|
|
||||||
<grid id="ce694" binding="targetPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
<grid id="ce694" binding="targetPanel" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
<margin top="0" left="0" bottom="0" right="0"/>
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
@@ -107,15 +106,6 @@
|
|||||||
</component>
|
</component>
|
||||||
</children>
|
</children>
|
||||||
</grid>
|
</grid>
|
||||||
<component id="684be" class="javax.swing.JCheckBox" binding="cbUpdatePackageDirective">
|
|
||||||
<constraints>
|
|
||||||
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="5" use-parent-layout="false"/>
|
|
||||||
</constraints>
|
|
||||||
<properties>
|
|
||||||
<selected value="true"/>
|
|
||||||
<text resource-bundle="messages/KotlinBundle" key="label.text.update.package.directive"/>
|
|
||||||
</properties>
|
|
||||||
</component>
|
|
||||||
<grid id="69aba" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
<grid id="69aba" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
<margin top="0" left="0" bottom="0" right="0"/>
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
|
|||||||
+67
-91
@@ -16,7 +16,6 @@ import com.intellij.refactoring.RefactoringBundle;
|
|||||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
|
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
|
||||||
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
||||||
import com.intellij.refactoring.classMembers.MemberInfoChange;
|
import com.intellij.refactoring.classMembers.MemberInfoChange;
|
||||||
import com.intellij.refactoring.classMembers.MemberInfoChangeListener;
|
|
||||||
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.ui.PackageNameReferenceEditorCombo;
|
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo;
|
||||||
@@ -31,7 +30,6 @@ import kotlin.collections.CollectionsKt;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle;
|
import org.jetbrains.kotlin.idea.KotlinBundle;
|
||||||
import org.jetbrains.kotlin.idea.core.PackageUtilsKt;
|
|
||||||
import org.jetbrains.kotlin.idea.core.util.PhysicalFileSystemUtilsKt;
|
import org.jetbrains.kotlin.idea.core.util.PhysicalFileSystemUtilsKt;
|
||||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings;
|
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings;
|
||||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
|
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
|
||||||
@@ -49,8 +47,10 @@ import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.BitSet;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.idea.roots.ProjectRootUtilsKt.getSuitableDestinationSourceRoots;
|
import static org.jetbrains.kotlin.idea.roots.ProjectRootUtilsKt.getSuitableDestinationSourceRoots;
|
||||||
|
|
||||||
@@ -69,44 +69,31 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
private TextFieldWithBrowseButton fileChooser;
|
private TextFieldWithBrowseButton fileChooser;
|
||||||
private JPanel memberInfoPanel;
|
private JPanel memberInfoPanel;
|
||||||
private JTextField tfFileNameInPackage;
|
private JTextField tfFileNameInPackage;
|
||||||
private JCheckBox cbSpecifyFileNameInPackage;
|
|
||||||
private JCheckBox cbUpdatePackageDirective;
|
|
||||||
private JCheckBox cbDeleteEmptySourceFiles;
|
private JCheckBox cbDeleteEmptySourceFiles;
|
||||||
private JCheckBox cbSearchReferences;
|
private JCheckBox cbSearchReferences;
|
||||||
private JCheckBox cbApplyMPPDeclarationsMove;
|
private JCheckBox cbApplyMPPDeclarationsMove;
|
||||||
private KotlinMemberSelectionTable memberTable;
|
private KotlinMemberSelectionTable memberTable;
|
||||||
|
|
||||||
private class MemberSelectionerInfoChangeListener implements MemberInfoChangeListener<KtNamedDeclaration, KotlinMemberInfo> {
|
public MoveKotlinTopLevelDeclarationsDialog(
|
||||||
|
@NotNull Project project,
|
||||||
private final List<KotlinMemberInfo> memberInfos;
|
@NotNull Set<KtNamedDeclaration> elementsToMove,
|
||||||
|
@Nullable String targetPackageName,
|
||||||
public MemberSelectionerInfoChangeListener(List<KotlinMemberInfo> memberInfos) {
|
@Nullable PsiDirectory targetDirectory,
|
||||||
this.memberInfos = memberInfos;
|
@Nullable KtFile targetFile,
|
||||||
}
|
boolean moveToPackage,
|
||||||
|
@Nullable MoveCallback moveCallback
|
||||||
private boolean shouldUpdateFileNameField(Collection<KotlinMemberInfo> changedMembers) {
|
) {
|
||||||
if (!tfFileNameInPackage.isEnabled()) return true;
|
this(project,
|
||||||
|
elementsToMove,
|
||||||
Collection<KtNamedDeclaration> previousDeclarations = CollectionsKt.filterNotNull(
|
targetPackageName,
|
||||||
CollectionsKt.map(
|
targetDirectory,
|
||||||
memberInfos,
|
targetFile,
|
||||||
info -> changedMembers.contains(info) != info.isChecked() ? info.getMember() : null
|
moveToPackage,
|
||||||
)
|
KotlinRefactoringSettings.getInstance().MOVE_SEARCH_IN_COMMENTS,
|
||||||
);
|
KotlinRefactoringSettings.getInstance().MOVE_SEARCH_FOR_TEXT,
|
||||||
String suggestedText = previousDeclarations.isEmpty() ? "" : MoveUtilsKt.guessNewFileName(previousDeclarations);
|
KotlinRefactoringSettings.getInstance().MOVE_DELETE_EMPTY_SOURCE_FILES,
|
||||||
return tfFileNameInPackage.getText().equals(suggestedText);
|
KotlinRefactoringSettings.getInstance().MOVE_MPP_DECLARATIONS,
|
||||||
}
|
moveCallback);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void memberInfoChanged(@NotNull MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo> event) {
|
|
||||||
updateControls();
|
|
||||||
updatePackageDirectiveCheckBox();
|
|
||||||
updateFileNameInPackageField();
|
|
||||||
// Update file name field only if it user hasn't changed it to some non-default value
|
|
||||||
if (shouldUpdateFileNameField(event.getChangedMembers())) {
|
|
||||||
updateSuggestedFileName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MoveKotlinTopLevelDeclarationsDialog(
|
public MoveKotlinTopLevelDeclarationsDialog(
|
||||||
@@ -124,16 +111,19 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
) {
|
) {
|
||||||
super(project, true);
|
super(project, true);
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
List<KtFile> sourceFiles = getSourceFiles(elementsToMove);
|
List<KtFile> sourceFiles = getSourceFiles(elementsToMove);
|
||||||
|
|
||||||
this.moveCallback = moveCallback;
|
this.moveCallback = moveCallback;
|
||||||
this.initialTargetDirectory = targetDirectory;
|
this.initialTargetDirectory = targetDirectory;
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
setTitle(MoveHandler.REFACTORING_NAME);
|
setTitle(MoveHandler.REFACTORING_NAME);
|
||||||
|
|
||||||
initSearchOptions(searchInComments, searchForTextOccurrences, deleteEmptySourceFiles, moveMppDeclarations);
|
List<KtNamedDeclaration> allDeclarations = getAllDeclarations(sourceFiles);
|
||||||
|
|
||||||
|
initSearchOptions(searchInComments, searchForTextOccurrences, deleteEmptySourceFiles, moveMppDeclarations, allDeclarations);
|
||||||
|
|
||||||
initPackageChooser(targetPackageName, targetDirectory, sourceFiles);
|
initPackageChooser(targetPackageName, targetDirectory, sourceFiles);
|
||||||
|
|
||||||
@@ -141,25 +131,28 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
|
|
||||||
initMoveToButtons(moveToPackage);
|
initMoveToButtons(moveToPackage);
|
||||||
|
|
||||||
initMemberInfo(elementsToMove, sourceFiles);
|
initMemberInfo(elementsToMove, allDeclarations);
|
||||||
|
|
||||||
updateControls();
|
updateControls();
|
||||||
|
|
||||||
initializedCheckBoxesState = getCheckboxesState(true);
|
initializedCheckBoxesState = getCheckboxesState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
}
|
||||||
|
|
||||||
private final BitSet initializedCheckBoxesState;
|
private final BitSet initializedCheckBoxesState;
|
||||||
private BitSet getCheckboxesState(boolean applyDefaults) {
|
private BitSet getCheckboxesState(boolean applyDefaults) {
|
||||||
|
|
||||||
BitSet state = new BitSet(7);
|
BitSet state = new BitSet(5);
|
||||||
|
|
||||||
state.set(0, applyDefaults || cbSearchInComments.isSelected()); //cbSearchInComments default is true
|
state.set(0, applyDefaults || cbSearchInComments.isSelected()); //cbSearchInComments default is true
|
||||||
state.set(1, applyDefaults || cbSearchTextOccurrences.isSelected()); //cbSearchTextOccurrences default is true
|
state.set(1, applyDefaults || cbSearchTextOccurrences.isSelected()); //cbSearchTextOccurrences default is true
|
||||||
state.set(2, applyDefaults || cbDeleteEmptySourceFiles.isSelected()); //cbDeleteEmptySourceFiles default is true
|
state.set(2, applyDefaults || cbDeleteEmptySourceFiles.isSelected()); //cbDeleteEmptySourceFiles default is true
|
||||||
state.set(3, applyDefaults || cbApplyMPPDeclarationsMove.isSelected()); //cbApplyMPPDeclarationsMove default is true
|
state.set(3, applyDefaults || cbApplyMPPDeclarationsMove.isSelected()); //cbApplyMPPDeclarationsMove default is true
|
||||||
state.set(4, cbSpecifyFileNameInPackage.isSelected());
|
state.set(4, cbSearchReferences.isSelected());
|
||||||
state.set(5, cbUpdatePackageDirective.isSelected());
|
|
||||||
state.set(6, cbSearchReferences.isSelected());
|
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
@@ -183,32 +176,24 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean arePackagesAndDirectoryMatched(List<KtFile> sourceFiles) {
|
|
||||||
for (KtFile sourceFile : sourceFiles) {
|
|
||||||
if (!PackageUtilsKt.packageMatchesDirectoryOrImplicit(sourceFile)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initMemberInfo(
|
private void initMemberInfo(
|
||||||
@NotNull Set<KtNamedDeclaration> elementsToMove,
|
@NotNull Set<KtNamedDeclaration> elementsToMove,
|
||||||
@NotNull List<KtFile> sourceFiles
|
@NotNull List<KtNamedDeclaration> declarations
|
||||||
) {
|
) {
|
||||||
List<KotlinMemberInfo> memberInfos = CollectionsKt.map(
|
////KotlinMemberInfo run resolve on declaration so it is good to place it to the process
|
||||||
getAllDeclarations(sourceFiles),
|
List<KotlinMemberInfo> memberInfos = MoveUtilsKt.mapWithReadActionInProcess(declarations, myProject, MoveHandler.REFACTORING_NAME, (declaration) -> {
|
||||||
declaration -> {
|
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
|
||||||
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
|
memberInfo.setChecked(elementsToMove.contains(declaration));
|
||||||
memberInfo.setChecked(elementsToMove.contains(declaration));
|
return memberInfo;
|
||||||
return memberInfo;
|
});
|
||||||
}
|
|
||||||
);
|
|
||||||
KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null);
|
KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null);
|
||||||
memberTable = selectionPanel.getTable();
|
memberTable = selectionPanel.getTable();
|
||||||
MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl();
|
MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl();
|
||||||
memberInfoModel.memberInfoChanged(new MemberInfoChange<>(memberInfos));
|
memberInfoModel.memberInfoChanged(new MemberInfoChange<>(memberInfos));
|
||||||
selectionPanel.getTable().setMemberInfoModel(memberInfoModel);
|
selectionPanel.getTable().setMemberInfoModel(memberInfoModel);
|
||||||
selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel);
|
selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel);
|
||||||
selectionPanel.getTable().addMemberInfoChangeListener(new MemberSelectionerInfoChangeListener(memberInfos));
|
selectionPanel.getTable().addMemberInfoChangeListener(listener -> updateControls());
|
||||||
cbApplyMPPDeclarationsMove.addChangeListener(e -> updateControls());
|
cbApplyMPPDeclarationsMove.addChangeListener(e -> updateControls());
|
||||||
memberInfoPanel.add(selectionPanel, BorderLayout.CENTER);
|
memberInfoPanel.add(selectionPanel, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
@@ -219,8 +204,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
|
|
||||||
private void updateFileNameInPackageField() {
|
private void updateFileNameInPackageField() {
|
||||||
boolean movingSingleFileToPackage = rbMoveToPackage.isSelected() && getSourceFiles(getSelectedElementsToMove()).size() == 1;
|
boolean movingSingleFileToPackage = rbMoveToPackage.isSelected() && getSourceFiles(getSelectedElementsToMove()).size() == 1;
|
||||||
cbSpecifyFileNameInPackage.setEnabled(movingSingleFileToPackage);
|
tfFileNameInPackage.setEnabled(movingSingleFileToPackage);
|
||||||
tfFileNameInPackage.setEnabled(movingSingleFileToPackage && cbSpecifyFileNameInPackage.isSelected());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initPackageChooser(
|
private void initPackageChooser(
|
||||||
@@ -243,17 +227,20 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
},
|
},
|
||||||
classPackageChooser.getChildComponent()
|
classPackageChooser.getChildComponent()
|
||||||
);
|
);
|
||||||
|
|
||||||
cbSpecifyFileNameInPackage.addActionListener(e -> updateFileNameInPackageField());
|
|
||||||
|
|
||||||
cbUpdatePackageDirective.setSelected(arePackagesAndDirectoryMatched(sourceFiles));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initSearchOptions(boolean searchInComments, boolean searchForTextOccurences, boolean deleteEmptySourceFiles, boolean moveMppDeclarations) {
|
private void initSearchOptions(
|
||||||
|
boolean searchInComments,
|
||||||
|
boolean searchForTextOccurences,
|
||||||
|
boolean deleteEmptySourceFiles,
|
||||||
|
boolean moveMppDeclarations,
|
||||||
|
List<KtNamedDeclaration> allDeclarations
|
||||||
|
) {
|
||||||
cbSearchInComments.setSelected(searchInComments);
|
cbSearchInComments.setSelected(searchInComments);
|
||||||
cbSearchTextOccurrences.setSelected(searchForTextOccurences);
|
cbSearchTextOccurrences.setSelected(searchForTextOccurences);
|
||||||
cbDeleteEmptySourceFiles.setSelected(deleteEmptySourceFiles);
|
cbDeleteEmptySourceFiles.setSelected(deleteEmptySourceFiles);
|
||||||
cbApplyMPPDeclarationsMove.setSelected(moveMppDeclarations);
|
cbApplyMPPDeclarationsMove.setSelected(moveMppDeclarations);
|
||||||
|
cbApplyMPPDeclarationsMove.setVisible(isMPPDeclarationInList(allDeclarations));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initMoveToButtons(boolean moveToPackage) {
|
private void initMoveToButtons(boolean moveToPackage) {
|
||||||
@@ -348,8 +335,8 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMppDeclarationSelected() {
|
private boolean isMPPDeclarationInList(List<KtNamedDeclaration> declarations) {
|
||||||
for(KtNamedDeclaration element : getSelectedElementsToMove()) {
|
for(KtNamedDeclaration element : declarations) {
|
||||||
if (ExpectActualUtilKt.isEffectivelyActual(element, true) ||
|
if (ExpectActualUtilKt.isEffectivelyActual(element, true) ||
|
||||||
ExpectActualUtilKt.isExpectDeclaration(element)) {
|
ExpectActualUtilKt.isExpectDeclaration(element)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -358,6 +345,10 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isMppDeclarationSelected() {
|
||||||
|
return isMPPDeclarationInList(getSelectedElementsToMove());
|
||||||
|
}
|
||||||
|
|
||||||
private void updateControls() {
|
private void updateControls() {
|
||||||
|
|
||||||
boolean mppDeclarationSelected = isMppDeclarationSelected();
|
boolean mppDeclarationSelected = isMppDeclarationSelected();
|
||||||
@@ -376,27 +367,11 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
classPackageChooser.setEnabled(moveToPackage);
|
classPackageChooser.setEnabled(moveToPackage);
|
||||||
updateFileNameInPackageField();
|
updateFileNameInPackageField();
|
||||||
fileChooser.setEnabled(!moveToPackage);
|
fileChooser.setEnabled(!moveToPackage);
|
||||||
updatePackageDirectiveCheckBox();
|
UIUtil.setEnabled(targetPanel, moveToPackage && !needToMoveMPPDeclarations && hasAnySourceRoots(), true);
|
||||||
UIUtil.setEnabled(targetPanel, moveToPackage && hasAnySourceRoots(), true);
|
|
||||||
updateSuggestedFileName();
|
updateSuggestedFileName();
|
||||||
myHelpAction.setEnabled(false);
|
myHelpAction.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isFullFileMove() {
|
|
||||||
Map<KtFile, List<KtNamedDeclaration>> fileToElements = CollectionsKt.groupBy(
|
|
||||||
getSelectedElementsToMove(),
|
|
||||||
KtPureElement::getContainingKtFile
|
|
||||||
);
|
|
||||||
for (Map.Entry<KtFile, List<KtNamedDeclaration>> entry : fileToElements.entrySet()) {
|
|
||||||
if (KtPsiUtilKt.getFileOrScriptDeclarations(entry.getKey()).size() != entry.getValue().size()) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updatePackageDirectiveCheckBox() {
|
|
||||||
cbUpdatePackageDirective.setEnabled(rbMoveToPackage.isSelected() && isFullFileMove());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasAnySourceRoots() {
|
private boolean hasAnySourceRoots() {
|
||||||
return !getSuitableDestinationSourceRoots(myProject).isEmpty();
|
return !getSuitableDestinationSourceRoots(myProject).isEmpty();
|
||||||
}
|
}
|
||||||
@@ -443,11 +418,14 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
|
|
||||||
private MoveKotlinTopLevelDeclarationsModel getModel() throws ConfigurationException {
|
private MoveKotlinTopLevelDeclarationsModel getModel() throws ConfigurationException {
|
||||||
|
|
||||||
DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper) destinationFolderCB.getComboBox().getSelectedItem();
|
|
||||||
PsiDirectory selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : initialTargetDirectory;
|
|
||||||
|
|
||||||
boolean mppDeclarationSelected = cbApplyMPPDeclarationsMove.isSelected() && isMppDeclarationSelected();
|
boolean mppDeclarationSelected = cbApplyMPPDeclarationsMove.isSelected() && isMppDeclarationSelected();
|
||||||
|
|
||||||
|
PsiDirectory selectedPsiDirectory = null;
|
||||||
|
if (!mppDeclarationSelected) {
|
||||||
|
DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper) destinationFolderCB.getComboBox().getSelectedItem();
|
||||||
|
selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : initialTargetDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
List<KtNamedDeclaration> selectedElements = getSelectedElementsToMoveChecked();
|
List<KtNamedDeclaration> selectedElements = getSelectedElementsToMoveChecked();
|
||||||
|
|
||||||
return new MoveKotlinTopLevelDeclarationsModel(
|
return new MoveKotlinTopLevelDeclarationsModel(
|
||||||
@@ -462,8 +440,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
|||||||
cbSearchInComments.isSelected(),
|
cbSearchInComments.isSelected(),
|
||||||
cbSearchTextOccurrences.isSelected(),
|
cbSearchTextOccurrences.isSelected(),
|
||||||
cbDeleteEmptySourceFiles.isSelected(),
|
cbDeleteEmptySourceFiles.isSelected(),
|
||||||
cbUpdatePackageDirective.isSelected(),
|
|
||||||
isFullFileMove(),
|
|
||||||
mppDeclarationSelected,
|
mppDeclarationSelected,
|
||||||
moveCallback
|
moveCallback
|
||||||
);
|
);
|
||||||
|
|||||||
+65
-51
@@ -16,6 +16,7 @@ import com.intellij.refactoring.BaseRefactoringProcessor
|
|||||||
import com.intellij.refactoring.MoveDestination
|
import com.intellij.refactoring.MoveDestination
|
||||||
import com.intellij.refactoring.PackageWrapper
|
import com.intellij.refactoring.PackageWrapper
|
||||||
import com.intellij.refactoring.move.MoveCallback
|
import com.intellij.refactoring.move.MoveCallback
|
||||||
|
import com.intellij.refactoring.move.MoveHandler
|
||||||
import com.intellij.refactoring.move.moveClassesOrPackages.AutocreatingSingleSourceRootMoveDestination
|
import com.intellij.refactoring.move.moveClassesOrPackages.AutocreatingSingleSourceRootMoveDestination
|
||||||
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination
|
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
@@ -25,18 +26,17 @@ import org.jetbrains.kotlin.idea.core.util.toPsiDirectory
|
|||||||
import org.jetbrains.kotlin.idea.core.util.toPsiFile
|
import org.jetbrains.kotlin.idea.core.util.toPsiFile
|
||||||
import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
|
import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.getOrCreateDirectory
|
import org.jetbrains.kotlin.idea.refactoring.move.getOrCreateDirectory
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.move.mapWithReadActionInProcess
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
|
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
|
import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
|
||||||
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
|
|
||||||
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MoveRefactoringDestination
|
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MoveRefactoringDestination
|
||||||
|
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
|
||||||
import org.jetbrains.kotlin.idea.util.collectAllExpectAndActualDeclaration
|
import org.jetbrains.kotlin.idea.util.collectAllExpectAndActualDeclaration
|
||||||
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
|
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
|
||||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.psiUtil.getFileOrScriptDeclarations
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.InvalidPathException
|
import java.nio.file.InvalidPathException
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
@@ -53,8 +53,6 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
val isSearchInComments: Boolean,
|
val isSearchInComments: Boolean,
|
||||||
val isSearchInNonJavaFiles: Boolean,
|
val isSearchInNonJavaFiles: Boolean,
|
||||||
val isDeleteEmptyFiles: Boolean,
|
val isDeleteEmptyFiles: Boolean,
|
||||||
val isUpdatePackageDirective: Boolean,
|
|
||||||
val isFullFileMove: Boolean,
|
|
||||||
val applyMPPDeclarations: Boolean,
|
val applyMPPDeclarations: Boolean,
|
||||||
val moveCallback: MoveCallback?
|
val moveCallback: MoveCallback?
|
||||||
) : Model {
|
) : Model {
|
||||||
@@ -65,9 +63,13 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
private fun checkedGetSourceDirectory() = sourceFiles.mapToSingleOrNull { it.parent }
|
private fun checkedGetSourceDirectory() = sourceFiles.mapToSingleOrNull { it.parent }
|
||||||
?: throw ConfigurationException(KotlinBundle.message("text.cannot.determine.source.directory"))
|
?: throw ConfigurationException(KotlinBundle.message("text.cannot.determine.source.directory"))
|
||||||
|
|
||||||
private val sourceFiles: Set<KtFile> = elementsToMove.mapTo(mutableSetOf()) { it.containingKtFile }
|
private val sourceFiles: Set<KtFile> by lazy {
|
||||||
|
elementsToMove.mapTo(mutableSetOf()) { it.containingKtFile }
|
||||||
|
}
|
||||||
|
|
||||||
private val elementsToMoveHasMPP = applyMPPDeclarations && elementsToMove.any { it.isEffectivelyActual() || it.isExpectDeclaration() }
|
private val elementsToMoveHasMPP by lazy {
|
||||||
|
applyMPPDeclarations && elementsToMove.any { it.isEffectivelyActual() || it.isExpectDeclaration() }
|
||||||
|
}
|
||||||
|
|
||||||
private val singleSourceFileMode = sourceFiles.size == 1
|
private val singleSourceFileMode = sourceFiles.size == 1
|
||||||
|
|
||||||
@@ -121,15 +123,17 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun selectMoveTargetToPackage(): KotlinMoveTarget {
|
private fun selectMoveTargetToPackage(): KotlinMoveTarget {
|
||||||
require(sourceFiles.isNotEmpty())
|
|
||||||
|
|
||||||
val moveDestination = selectPackageBasedDestination()
|
val moveDestination = selectPackageBasedDestination()
|
||||||
val targetDirectory: PsiDirectory? = moveDestination.getTargetIfExists(checkedGetSourceDirectory())
|
|
||||||
|
|
||||||
val targetFileName = if (singleSourceFileMode) fileNameInPackage.also(::checkTargetFileName) else null
|
val targetDirectory: PsiDirectory?
|
||||||
|
if (!elementsToMoveHasMPP) {
|
||||||
if (targetDirectory != null) {
|
targetDirectory = moveDestination.getTargetIfExists(checkedGetSourceDirectory())
|
||||||
tryMoveToPackageForExistingDirectory(targetFileName, targetDirectory)?.let { return it }
|
val targetFileName = if (singleSourceFileMode) fileNameInPackage.also(::checkTargetFileName) else null
|
||||||
|
if (targetDirectory != null) {
|
||||||
|
tryMoveToPackageForExistingDirectory(targetFileName, targetDirectory)?.let { return it }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
targetDirectory = null
|
||||||
}
|
}
|
||||||
|
|
||||||
return KotlinMoveTargetForDeferredFile(
|
return KotlinMoveTargetForDeferredFile(
|
||||||
@@ -193,9 +197,6 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun selectMoveTarget() =
|
|
||||||
if (isMoveToPackage) selectMoveTargetToPackage() else selectMoveTargetToFile()
|
|
||||||
|
|
||||||
private fun verifyBeforeRun() {
|
private fun verifyBeforeRun() {
|
||||||
if (!isMoveToPackage && elementsToMoveHasMPP)
|
if (!isMoveToPackage && elementsToMoveHasMPP)
|
||||||
throw ConfigurationException(KotlinBundle.message("text.cannot.move.expect.actual.declaration.to.file"))
|
throw ConfigurationException(KotlinBundle.message("text.cannot.move.expect.actual.declaration.to.file"))
|
||||||
@@ -217,14 +218,7 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(ConfigurationException::class)
|
private fun getFUSParameters(): Pair<MovedEntity, MoveRefactoringDestination> {
|
||||||
override fun computeModelResult() = computeModelResult(throwOnConflicts = false)
|
|
||||||
|
|
||||||
@Throws(ConfigurationException::class)
|
|
||||||
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
|
|
||||||
|
|
||||||
verifyBeforeRun()
|
|
||||||
|
|
||||||
val classType = if (elementsToMoveHasMPP) MovedEntity.MPPCLASSES else MovedEntity.CLASSES
|
val classType = if (elementsToMoveHasMPP) MovedEntity.MPPCLASSES else MovedEntity.CLASSES
|
||||||
val functionType = if (elementsToMoveHasMPP) MovedEntity.MPPFUNCTIONS else MovedEntity.FUNCTIONS
|
val functionType = if (elementsToMoveHasMPP) MovedEntity.MPPFUNCTIONS else MovedEntity.FUNCTIONS
|
||||||
val mixedType = if (elementsToMoveHasMPP) MovedEntity.MPPMIXED else MovedEntity.MIXED
|
val mixedType = if (elementsToMoveHasMPP) MovedEntity.MPPMIXED else MovedEntity.MIXED
|
||||||
@@ -236,41 +230,58 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
|
|
||||||
val destination = if (isMoveToPackage) MoveRefactoringDestination.PACKAGE else MoveRefactoringDestination.FILE
|
val destination = if (isMoveToPackage) MoveRefactoringDestination.PACKAGE else MoveRefactoringDestination.FILE
|
||||||
|
|
||||||
if (isFullFileMove && isMoveToPackage) {
|
return entity to destination
|
||||||
tryMoveFile(throwOnConflicts)?.let {
|
}
|
||||||
return ModelResultWithFUSData(it, elementsToMove.size, entity, destination)
|
|
||||||
}
|
|
||||||
}
|
@Throws(ConfigurationException::class)
|
||||||
|
override fun computeModelResult() = computeModelResult(throwOnConflicts = false)
|
||||||
|
|
||||||
|
@Throws(ConfigurationException::class)
|
||||||
|
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
|
||||||
|
|
||||||
|
verifyBeforeRun()
|
||||||
|
|
||||||
|
val (entity, destination) = getFUSParameters()
|
||||||
|
|
||||||
|
val processor = tryMoveEntireFile(throwOnConflicts) ?: moveDeclaration(throwOnConflicts)
|
||||||
|
|
||||||
val processor = moveDeclaration(throwOnConflicts)
|
|
||||||
return ModelResultWithFUSData(processor, elementsToMove.size, entity, destination)
|
return ModelResultWithFUSData(processor, elementsToMove.size, entity, destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryMoveFile(throwOnConflicts: Boolean): BaseRefactoringProcessor? {
|
private fun tryMoveEntireFile(throwOnConflicts: Boolean): BaseRefactoringProcessor? {
|
||||||
|
|
||||||
if (elementsToMoveHasMPP) return null
|
if (!isDeleteEmptyFiles || elementsToMoveHasMPP || !isMoveToPackage) return null
|
||||||
|
|
||||||
val targetFileName = if (sourceFiles.size > 1) null else fileNameInPackage
|
val allDeclarationsMovingOut = elementsToMove
|
||||||
if (targetFileName != null) checkTargetFileName(targetFileName)
|
.groupBy { obj: KtPureElement -> obj.containingKtFile }
|
||||||
|
.all { it.key.getFileOrScriptDeclarations().size == it.value.size }
|
||||||
|
if (!allDeclarationsMovingOut) return null
|
||||||
|
|
||||||
val moveDestination = selectPackageBasedDestination()
|
val targetDirectory = selectPackageBasedDestination()
|
||||||
val targetDirectory = moveDestination.getTargetIfExists(checkedGetSourceDirectory()) ?: return null
|
.getTargetIfExists(checkedGetSourceDirectory())
|
||||||
|
?: return null
|
||||||
|
|
||||||
val filesExistingInTargetDir = getFilesExistingInTargetDirectory(targetFileName, targetDirectory)
|
val targetFileNameAndFile = sourceFiles
|
||||||
|
.singleOrNull()
|
||||||
|
?.let { fileNameInPackage to it }
|
||||||
|
?.also { checkTargetFileName(it.first) }
|
||||||
|
|
||||||
|
val filesExistingInTargetDir = getFilesExistingInTargetDirectory(targetFileNameAndFile?.first, targetDirectory)
|
||||||
|
|
||||||
val moveAsFile = filesExistingInTargetDir.isEmpty() ||
|
val moveAsFile = filesExistingInTargetDir.isEmpty() ||
|
||||||
filesExistingInTargetDir.singleOrNull()?.let { sourceFiles.contains(it) } == true
|
filesExistingInTargetDir.singleOrNull()?.let { sourceFiles.contains(it) } == true
|
||||||
|
|
||||||
if (!moveAsFile) return null
|
if (!moveAsFile) return null
|
||||||
|
|
||||||
sourceFiles.forEach { it.updatePackageDirective = isUpdatePackageDirective }
|
sourceFiles.forEach { it.updatePackageDirective = true }
|
||||||
|
|
||||||
return if (targetFileName != null)
|
return if (targetFileNameAndFile != null)
|
||||||
MoveToKotlinFileProcessor(
|
MoveToKotlinFileProcessor(
|
||||||
project,
|
project,
|
||||||
sourceFiles.first(),
|
targetFileNameAndFile.second,
|
||||||
targetDirectory,
|
targetDirectory,
|
||||||
targetFileName,
|
targetFileNameAndFile.first,
|
||||||
searchInComments = isSearchInComments,
|
searchInComments = isSearchInComments,
|
||||||
searchInNonJavaFiles = isSearchInNonJavaFiles,
|
searchInNonJavaFiles = isSearchInNonJavaFiles,
|
||||||
moveCallback = moveCallback,
|
moveCallback = moveCallback,
|
||||||
@@ -291,13 +302,16 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
|
|
||||||
private fun moveDeclaration(throwOnConflicts: Boolean): BaseRefactoringProcessor {
|
private fun moveDeclaration(throwOnConflicts: Boolean): BaseRefactoringProcessor {
|
||||||
|
|
||||||
val elementsWithMPPIfNeeded =
|
if (elementsToMoveHasMPP) require(isMoveToPackage && selectedPsiDirectory == null)
|
||||||
if (elementsToMoveHasMPP) elementsToMove
|
|
||||||
.flatMap { it.collectAllExpectAndActualDeclaration() }
|
val target = if (isMoveToPackage) selectMoveTargetToPackage() else selectMoveTargetToFile()
|
||||||
.filterIsInstance<KtNamedDeclaration>()
|
|
||||||
else elementsToMove
|
val elementsWithMPPIfNeeded = if (elementsToMoveHasMPP)
|
||||||
|
elementsToMove.mapWithReadActionInProcess(project, MoveHandler.REFACTORING_NAME) {
|
||||||
|
it.collectAllExpectAndActualDeclaration()
|
||||||
|
}.flatten().filterIsInstance<KtNamedDeclaration>()
|
||||||
|
else elementsToMove
|
||||||
|
|
||||||
val target = selectMoveTarget()
|
|
||||||
for (element in elementsWithMPPIfNeeded) {
|
for (element in elementsWithMPPIfNeeded) {
|
||||||
target.verify(element.containingFile)?.let { throw ConfigurationException(it) }
|
target.verify(element.containingFile)?.let { throw ConfigurationException(it) }
|
||||||
}
|
}
|
||||||
@@ -309,7 +323,7 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
|||||||
MoveDeclarationsDelegate.TopLevel,
|
MoveDeclarationsDelegate.TopLevel,
|
||||||
isSearchInComments,
|
isSearchInComments,
|
||||||
isSearchInNonJavaFiles,
|
isSearchInNonJavaFiles,
|
||||||
deleteSourceFiles = isFullFileMove && isDeleteEmptyFiles,
|
deleteSourceFiles = isDeleteEmptyFiles,
|
||||||
moveCallback = moveCallback,
|
moveCallback = moveCallback,
|
||||||
openInEditor = false,
|
openInEditor = false,
|
||||||
allElementsToMove = null,
|
allElementsToMove = null,
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
package org.jetbrains.kotlin.idea.refactoring.move
|
package org.jetbrains.kotlin.idea.refactoring.move
|
||||||
|
|
||||||
import com.intellij.ide.util.DirectoryUtil
|
import com.intellij.ide.util.DirectoryUtil
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.progress.ProgressIndicator
|
||||||
import com.intellij.openapi.progress.ProgressManager
|
import com.intellij.openapi.progress.ProgressManager
|
||||||
|
import com.intellij.openapi.progress.Task
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.Comparing
|
import com.intellij.openapi.util.Comparing
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
@@ -58,7 +61,6 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.System.currentTimeMillis
|
import java.lang.System.currentTimeMillis
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.swing.JCheckBox
|
|
||||||
|
|
||||||
sealed class ContainerInfo {
|
sealed class ContainerInfo {
|
||||||
abstract val fqName: FqName?
|
abstract val fqName: FqName?
|
||||||
@@ -709,3 +711,26 @@ internal fun logFusForMoveRefactoring(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun <T> List<KtNamedDeclaration>.mapWithReadActionInProcess(
|
||||||
|
project: Project,
|
||||||
|
title: String,
|
||||||
|
body: (KtNamedDeclaration) -> T
|
||||||
|
): List<T> = let { declarations ->
|
||||||
|
val result = mutableListOf<T>()
|
||||||
|
val task: Task.Modal = object : Task.Modal(project, title, false) {
|
||||||
|
override fun run(indicator: ProgressIndicator) {
|
||||||
|
val count = 0
|
||||||
|
val fraction: Double = 1.0 / declarations.size
|
||||||
|
indicator.fraction = 0.0
|
||||||
|
ApplicationManager.getApplication().runReadAction(Runnable {
|
||||||
|
for (declaration in declarations) {
|
||||||
|
result.add(body(declaration))
|
||||||
|
indicator.fraction = fraction * count
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProgressManager.getInstance().run(task)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user