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" +
|
||||
"isSearchInComments = $isSearchInComments\n" +
|
||||
"isSearchInNonJavaFiles = $isSearchInNonJavaFiles\n" +
|
||||
"isDeleteEmptyFiles = $isDeleteEmptyFiles\n" +
|
||||
"isUpdatePackageDirective = $isUpdatePackageDirective\n" +
|
||||
"isFullFileMove = $isFullFileMove"
|
||||
"isDeleteEmptyFiles = $isDeleteEmptyFiles\n"
|
||||
}
|
||||
|
||||
private fun KotlinAwareMoveFilesOrDirectoriesModel.testDataString(): String {
|
||||
@@ -153,10 +151,6 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
|
||||
targetDirectory: PsiDirectory?,
|
||||
targetFile: KtFile?,
|
||||
moveToPackage: Boolean,
|
||||
searchInComments: Boolean,
|
||||
searchForTextOccurrences: Boolean,
|
||||
deleteEmptySourceFiles: Boolean,
|
||||
moveMppDeclarations: Boolean,
|
||||
moveCallback: MoveCallback?
|
||||
) {
|
||||
val selectedElementsToMove = mutableSetOf<KtNamedDeclaration>()
|
||||
@@ -180,8 +174,6 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
|
||||
isSearchInComments = randomBoolean(),
|
||||
isSearchInNonJavaFiles = randomBoolean(),
|
||||
isDeleteEmptyFiles = randomBoolean(),
|
||||
isUpdatePackageDirective = randomBoolean(),
|
||||
isFullFileMove = randomBoolean(),
|
||||
applyMPPDeclarations = true,
|
||||
moveCallback = null
|
||||
)
|
||||
|
||||
+9
-16
@@ -68,10 +68,6 @@ private val defaultHandlerActions = object : MoveKotlinDeclarationsHandlerAction
|
||||
targetDirectory: PsiDirectory?,
|
||||
targetFile: KtFile?,
|
||||
moveToPackage: Boolean,
|
||||
searchInComments: Boolean,
|
||||
searchForTextOccurrences: Boolean,
|
||||
deleteEmptySourceFiles: Boolean,
|
||||
moveMppDeclarations: Boolean,
|
||||
moveCallback: MoveCallback?
|
||||
) = MoveKotlinTopLevelDeclarationsDialog(
|
||||
project,
|
||||
@@ -80,10 +76,6 @@ private val defaultHandlerActions = object : MoveKotlinDeclarationsHandlerAction
|
||||
targetDirectory,
|
||||
targetFile,
|
||||
moveToPackage,
|
||||
searchInComments,
|
||||
searchForTextOccurrences,
|
||||
deleteEmptySourceFiles,
|
||||
moveMppDeclarations,
|
||||
moveCallback
|
||||
).show()
|
||||
|
||||
@@ -180,6 +172,15 @@ class MoveKotlinDeclarationsHandler internal constructor(private val handlerActi
|
||||
}
|
||||
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(
|
||||
project, initialTargetDirectory, ktFileElements, callback
|
||||
)
|
||||
@@ -193,10 +194,6 @@ class MoveKotlinDeclarationsHandler internal constructor(private val handlerActi
|
||||
val targetDirectory = if (targetContainer != null) {
|
||||
MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements)
|
||||
} 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 moveToPackage = targetContainer !is KtFile
|
||||
|
||||
@@ -207,10 +204,6 @@ class MoveKotlinDeclarationsHandler internal constructor(private val handlerActi
|
||||
targetDirectory,
|
||||
targetFile,
|
||||
moveToPackage,
|
||||
searchInComments,
|
||||
searchInText,
|
||||
deleteEmptySourceFiles,
|
||||
moveMppDeclarations,
|
||||
callback
|
||||
)
|
||||
}
|
||||
|
||||
-4
@@ -31,10 +31,6 @@ internal interface MoveKotlinDeclarationsHandlerActions {
|
||||
targetDirectory: PsiDirectory?,
|
||||
targetFile: KtFile?,
|
||||
moveToPackage: Boolean,
|
||||
searchInComments: Boolean,
|
||||
searchForTextOccurrences: Boolean,
|
||||
deleteEmptySourceFiles: Boolean,
|
||||
moveMppDeclarations: Boolean,
|
||||
moveCallback: MoveCallback?
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -363,7 +363,7 @@ class MoveKotlinDeclarationsProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor.deleteSourceFiles) {
|
||||
if (descriptor.deleteSourceFiles && sourceFile.declarations.isEmpty()) {
|
||||
sourceFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
+6
-9
@@ -66,22 +66,19 @@ class MoveToKotlinFileProcessor @JvmOverloads constructor(
|
||||
return super.showConflicts(conflicts, usages)
|
||||
}
|
||||
|
||||
// Assign a temporary name to file-under-move to avoid naming conflict during the refactoring
|
||||
private fun renameFileTemporarily() {
|
||||
if (targetDirectory.findFile(targetFileName) == null) return
|
||||
private fun renameFileTemporarilyIfNeeded() {
|
||||
if (targetDirectory.findFile(sourceFile.name) == null) return
|
||||
|
||||
val containingDirectory = sourceFile.containingDirectory ?: return
|
||||
|
||||
val temporaryName = UniqueNameGenerator.generateUniqueName("temp", "", ".kt") {
|
||||
sourceFile.containingDirectory!!.findFile(it) == null
|
||||
containingDirectory.findFile(it) == null
|
||||
}
|
||||
sourceFile.name = temporaryName
|
||||
}
|
||||
|
||||
override fun performRefactoring(usages: Array<UsageInfo>) {
|
||||
val needTemporaryRename = targetDirectory.findFile(sourceFile.name) != null
|
||||
if (needTemporaryRename) {
|
||||
renameFileTemporarily()
|
||||
}
|
||||
|
||||
renameFileTemporarilyIfNeeded()
|
||||
super.performRefactoring(usages)
|
||||
sourceFile.name = targetFileName
|
||||
}
|
||||
|
||||
+5
-3
@@ -129,9 +129,11 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
|
||||
|
||||
if (targetContainer is KtClassOrObject) {
|
||||
val targetClass = targetContainer as KtClassOrObject?
|
||||
for (member in targetClass!!.declarations) {
|
||||
if (member is KtClassOrObject && className == member.getName()) {
|
||||
throw ConfigurationException(RefactoringBundle.message("inner.class.exists", className, targetClass.name))
|
||||
if (targetClass != null) {
|
||||
for (member in targetClass.declarations) {
|
||||
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"/>
|
||||
</properties>
|
||||
</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">
|
||||
<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">
|
||||
@@ -72,16 +81,6 @@
|
||||
<enabled value="false"/>
|
||||
</properties>
|
||||
</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">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
@@ -107,15 +106,6 @@
|
||||
</component>
|
||||
</children>
|
||||
</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">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
|
||||
+67
-91
@@ -16,7 +16,6 @@ import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoChange;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoChangeListener;
|
||||
import com.intellij.refactoring.move.MoveCallback;
|
||||
import com.intellij.refactoring.move.MoveHandler;
|
||||
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo;
|
||||
@@ -31,7 +30,6 @@ import kotlin.collections.CollectionsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.refactoring.KotlinRefactoringSettings;
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
|
||||
@@ -49,8 +47,10 @@ import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.kotlin.idea.roots.ProjectRootUtilsKt.getSuitableDestinationSourceRoots;
|
||||
|
||||
@@ -69,44 +69,31 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
private TextFieldWithBrowseButton fileChooser;
|
||||
private JPanel memberInfoPanel;
|
||||
private JTextField tfFileNameInPackage;
|
||||
private JCheckBox cbSpecifyFileNameInPackage;
|
||||
private JCheckBox cbUpdatePackageDirective;
|
||||
private JCheckBox cbDeleteEmptySourceFiles;
|
||||
private JCheckBox cbSearchReferences;
|
||||
private JCheckBox cbApplyMPPDeclarationsMove;
|
||||
private KotlinMemberSelectionTable memberTable;
|
||||
|
||||
private class MemberSelectionerInfoChangeListener implements MemberInfoChangeListener<KtNamedDeclaration, KotlinMemberInfo> {
|
||||
|
||||
private final List<KotlinMemberInfo> memberInfos;
|
||||
|
||||
public MemberSelectionerInfoChangeListener(List<KotlinMemberInfo> memberInfos) {
|
||||
this.memberInfos = memberInfos;
|
||||
}
|
||||
|
||||
private boolean shouldUpdateFileNameField(Collection<KotlinMemberInfo> changedMembers) {
|
||||
if (!tfFileNameInPackage.isEnabled()) return true;
|
||||
|
||||
Collection<KtNamedDeclaration> previousDeclarations = CollectionsKt.filterNotNull(
|
||||
CollectionsKt.map(
|
||||
memberInfos,
|
||||
info -> changedMembers.contains(info) != info.isChecked() ? info.getMember() : null
|
||||
)
|
||||
);
|
||||
String suggestedText = previousDeclarations.isEmpty() ? "" : MoveUtilsKt.guessNewFileName(previousDeclarations);
|
||||
return tfFileNameInPackage.getText().equals(suggestedText);
|
||||
}
|
||||
|
||||
@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(
|
||||
@NotNull Project project,
|
||||
@NotNull Set<KtNamedDeclaration> elementsToMove,
|
||||
@Nullable String targetPackageName,
|
||||
@Nullable PsiDirectory targetDirectory,
|
||||
@Nullable KtFile targetFile,
|
||||
boolean moveToPackage,
|
||||
@Nullable MoveCallback moveCallback
|
||||
) {
|
||||
this(project,
|
||||
elementsToMove,
|
||||
targetPackageName,
|
||||
targetDirectory,
|
||||
targetFile,
|
||||
moveToPackage,
|
||||
KotlinRefactoringSettings.getInstance().MOVE_SEARCH_IN_COMMENTS,
|
||||
KotlinRefactoringSettings.getInstance().MOVE_SEARCH_FOR_TEXT,
|
||||
KotlinRefactoringSettings.getInstance().MOVE_DELETE_EMPTY_SOURCE_FILES,
|
||||
KotlinRefactoringSettings.getInstance().MOVE_MPP_DECLARATIONS,
|
||||
moveCallback);
|
||||
}
|
||||
|
||||
public MoveKotlinTopLevelDeclarationsDialog(
|
||||
@@ -124,16 +111,19 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
) {
|
||||
super(project, true);
|
||||
|
||||
init();
|
||||
|
||||
List<KtFile> sourceFiles = getSourceFiles(elementsToMove);
|
||||
|
||||
this.moveCallback = moveCallback;
|
||||
this.initialTargetDirectory = targetDirectory;
|
||||
|
||||
init();
|
||||
|
||||
setTitle(MoveHandler.REFACTORING_NAME);
|
||||
|
||||
initSearchOptions(searchInComments, searchForTextOccurrences, deleteEmptySourceFiles, moveMppDeclarations);
|
||||
List<KtNamedDeclaration> allDeclarations = getAllDeclarations(sourceFiles);
|
||||
|
||||
initSearchOptions(searchInComments, searchForTextOccurrences, deleteEmptySourceFiles, moveMppDeclarations, allDeclarations);
|
||||
|
||||
initPackageChooser(targetPackageName, targetDirectory, sourceFiles);
|
||||
|
||||
@@ -141,25 +131,28 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
|
||||
initMoveToButtons(moveToPackage);
|
||||
|
||||
initMemberInfo(elementsToMove, sourceFiles);
|
||||
initMemberInfo(elementsToMove, allDeclarations);
|
||||
|
||||
updateControls();
|
||||
|
||||
initializedCheckBoxesState = getCheckboxesState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
}
|
||||
|
||||
private final BitSet initializedCheckBoxesState;
|
||||
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(1, applyDefaults || cbSearchTextOccurrences.isSelected()); //cbSearchTextOccurrences 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(4, cbSpecifyFileNameInPackage.isSelected());
|
||||
state.set(5, cbUpdatePackageDirective.isSelected());
|
||||
state.set(6, cbSearchReferences.isSelected());
|
||||
state.set(4, cbSearchReferences.isSelected());
|
||||
|
||||
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(
|
||||
@NotNull Set<KtNamedDeclaration> elementsToMove,
|
||||
@NotNull List<KtFile> sourceFiles
|
||||
@NotNull List<KtNamedDeclaration> declarations
|
||||
) {
|
||||
List<KotlinMemberInfo> memberInfos = CollectionsKt.map(
|
||||
getAllDeclarations(sourceFiles),
|
||||
declaration -> {
|
||||
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
|
||||
memberInfo.setChecked(elementsToMove.contains(declaration));
|
||||
return memberInfo;
|
||||
}
|
||||
);
|
||||
////KotlinMemberInfo run resolve on declaration so it is good to place it to the process
|
||||
List<KotlinMemberInfo> memberInfos = MoveUtilsKt.mapWithReadActionInProcess(declarations, myProject, MoveHandler.REFACTORING_NAME, (declaration) -> {
|
||||
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
|
||||
memberInfo.setChecked(elementsToMove.contains(declaration));
|
||||
return memberInfo;
|
||||
});
|
||||
|
||||
KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null);
|
||||
memberTable = selectionPanel.getTable();
|
||||
MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl();
|
||||
memberInfoModel.memberInfoChanged(new MemberInfoChange<>(memberInfos));
|
||||
selectionPanel.getTable().setMemberInfoModel(memberInfoModel);
|
||||
selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel);
|
||||
selectionPanel.getTable().addMemberInfoChangeListener(new MemberSelectionerInfoChangeListener(memberInfos));
|
||||
selectionPanel.getTable().addMemberInfoChangeListener(listener -> updateControls());
|
||||
cbApplyMPPDeclarationsMove.addChangeListener(e -> updateControls());
|
||||
memberInfoPanel.add(selectionPanel, BorderLayout.CENTER);
|
||||
}
|
||||
@@ -219,8 +204,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
|
||||
private void updateFileNameInPackageField() {
|
||||
boolean movingSingleFileToPackage = rbMoveToPackage.isSelected() && getSourceFiles(getSelectedElementsToMove()).size() == 1;
|
||||
cbSpecifyFileNameInPackage.setEnabled(movingSingleFileToPackage);
|
||||
tfFileNameInPackage.setEnabled(movingSingleFileToPackage && cbSpecifyFileNameInPackage.isSelected());
|
||||
tfFileNameInPackage.setEnabled(movingSingleFileToPackage);
|
||||
}
|
||||
|
||||
private void initPackageChooser(
|
||||
@@ -243,17 +227,20 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
},
|
||||
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);
|
||||
cbSearchTextOccurrences.setSelected(searchForTextOccurences);
|
||||
cbDeleteEmptySourceFiles.setSelected(deleteEmptySourceFiles);
|
||||
cbApplyMPPDeclarationsMove.setSelected(moveMppDeclarations);
|
||||
cbApplyMPPDeclarationsMove.setVisible(isMPPDeclarationInList(allDeclarations));
|
||||
}
|
||||
|
||||
private void initMoveToButtons(boolean moveToPackage) {
|
||||
@@ -348,8 +335,8 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isMppDeclarationSelected() {
|
||||
for(KtNamedDeclaration element : getSelectedElementsToMove()) {
|
||||
private boolean isMPPDeclarationInList(List<KtNamedDeclaration> declarations) {
|
||||
for(KtNamedDeclaration element : declarations) {
|
||||
if (ExpectActualUtilKt.isEffectivelyActual(element, true) ||
|
||||
ExpectActualUtilKt.isExpectDeclaration(element)) {
|
||||
return true;
|
||||
@@ -358,6 +345,10 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isMppDeclarationSelected() {
|
||||
return isMPPDeclarationInList(getSelectedElementsToMove());
|
||||
}
|
||||
|
||||
private void updateControls() {
|
||||
|
||||
boolean mppDeclarationSelected = isMppDeclarationSelected();
|
||||
@@ -376,27 +367,11 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
classPackageChooser.setEnabled(moveToPackage);
|
||||
updateFileNameInPackageField();
|
||||
fileChooser.setEnabled(!moveToPackage);
|
||||
updatePackageDirectiveCheckBox();
|
||||
UIUtil.setEnabled(targetPanel, moveToPackage && hasAnySourceRoots(), true);
|
||||
UIUtil.setEnabled(targetPanel, moveToPackage && !needToMoveMPPDeclarations && hasAnySourceRoots(), true);
|
||||
updateSuggestedFileName();
|
||||
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() {
|
||||
return !getSuitableDestinationSourceRoots(myProject).isEmpty();
|
||||
}
|
||||
@@ -443,11 +418,14 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
|
||||
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();
|
||||
|
||||
PsiDirectory selectedPsiDirectory = null;
|
||||
if (!mppDeclarationSelected) {
|
||||
DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper) destinationFolderCB.getComboBox().getSelectedItem();
|
||||
selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : initialTargetDirectory;
|
||||
}
|
||||
|
||||
List<KtNamedDeclaration> selectedElements = getSelectedElementsToMoveChecked();
|
||||
|
||||
return new MoveKotlinTopLevelDeclarationsModel(
|
||||
@@ -462,8 +440,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
cbSearchInComments.isSelected(),
|
||||
cbSearchTextOccurrences.isSelected(),
|
||||
cbDeleteEmptySourceFiles.isSelected(),
|
||||
cbUpdatePackageDirective.isSelected(),
|
||||
isFullFileMove(),
|
||||
mppDeclarationSelected,
|
||||
moveCallback
|
||||
);
|
||||
|
||||
+65
-51
@@ -16,6 +16,7 @@ import com.intellij.refactoring.BaseRefactoringProcessor
|
||||
import com.intellij.refactoring.MoveDestination
|
||||
import com.intellij.refactoring.PackageWrapper
|
||||
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.MultipleRootsMoveDestination
|
||||
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.refactoring.getOrCreateKotlinFile
|
||||
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.updatePackageDirective
|
||||
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
|
||||
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.isEffectivelyActual
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtFunction
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getFileOrScriptDeclarations
|
||||
import java.io.File
|
||||
import java.nio.file.InvalidPathException
|
||||
import java.nio.file.Paths
|
||||
@@ -53,8 +53,6 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
val isSearchInComments: Boolean,
|
||||
val isSearchInNonJavaFiles: Boolean,
|
||||
val isDeleteEmptyFiles: Boolean,
|
||||
val isUpdatePackageDirective: Boolean,
|
||||
val isFullFileMove: Boolean,
|
||||
val applyMPPDeclarations: Boolean,
|
||||
val moveCallback: MoveCallback?
|
||||
) : Model {
|
||||
@@ -65,9 +63,13 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
private fun checkedGetSourceDirectory() = sourceFiles.mapToSingleOrNull { it.parent }
|
||||
?: 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
|
||||
|
||||
@@ -121,15 +123,17 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
}
|
||||
|
||||
private fun selectMoveTargetToPackage(): KotlinMoveTarget {
|
||||
require(sourceFiles.isNotEmpty())
|
||||
|
||||
val moveDestination = selectPackageBasedDestination()
|
||||
val targetDirectory: PsiDirectory? = moveDestination.getTargetIfExists(checkedGetSourceDirectory())
|
||||
|
||||
val targetFileName = if (singleSourceFileMode) fileNameInPackage.also(::checkTargetFileName) else null
|
||||
|
||||
if (targetDirectory != null) {
|
||||
tryMoveToPackageForExistingDirectory(targetFileName, targetDirectory)?.let { return it }
|
||||
val targetDirectory: PsiDirectory?
|
||||
if (!elementsToMoveHasMPP) {
|
||||
targetDirectory = moveDestination.getTargetIfExists(checkedGetSourceDirectory())
|
||||
val targetFileName = if (singleSourceFileMode) fileNameInPackage.also(::checkTargetFileName) else null
|
||||
if (targetDirectory != null) {
|
||||
tryMoveToPackageForExistingDirectory(targetFileName, targetDirectory)?.let { return it }
|
||||
}
|
||||
} else {
|
||||
targetDirectory = null
|
||||
}
|
||||
|
||||
return KotlinMoveTargetForDeferredFile(
|
||||
@@ -193,9 +197,6 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectMoveTarget() =
|
||||
if (isMoveToPackage) selectMoveTargetToPackage() else selectMoveTargetToFile()
|
||||
|
||||
private fun verifyBeforeRun() {
|
||||
if (!isMoveToPackage && elementsToMoveHasMPP)
|
||||
throw ConfigurationException(KotlinBundle.message("text.cannot.move.expect.actual.declaration.to.file"))
|
||||
@@ -217,14 +218,7 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ConfigurationException::class)
|
||||
override fun computeModelResult() = computeModelResult(throwOnConflicts = false)
|
||||
|
||||
@Throws(ConfigurationException::class)
|
||||
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
|
||||
|
||||
verifyBeforeRun()
|
||||
|
||||
private fun getFUSParameters(): Pair<MovedEntity, MoveRefactoringDestination> {
|
||||
val classType = if (elementsToMoveHasMPP) MovedEntity.MPPCLASSES else MovedEntity.CLASSES
|
||||
val functionType = if (elementsToMoveHasMPP) MovedEntity.MPPFUNCTIONS else MovedEntity.FUNCTIONS
|
||||
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
|
||||
|
||||
if (isFullFileMove && isMoveToPackage) {
|
||||
tryMoveFile(throwOnConflicts)?.let {
|
||||
return ModelResultWithFUSData(it, elementsToMove.size, entity, destination)
|
||||
}
|
||||
}
|
||||
return entity to 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)
|
||||
}
|
||||
|
||||
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
|
||||
if (targetFileName != null) checkTargetFileName(targetFileName)
|
||||
val allDeclarationsMovingOut = elementsToMove
|
||||
.groupBy { obj: KtPureElement -> obj.containingKtFile }
|
||||
.all { it.key.getFileOrScriptDeclarations().size == it.value.size }
|
||||
if (!allDeclarationsMovingOut) return null
|
||||
|
||||
val moveDestination = selectPackageBasedDestination()
|
||||
val targetDirectory = moveDestination.getTargetIfExists(checkedGetSourceDirectory()) ?: return null
|
||||
val targetDirectory = selectPackageBasedDestination()
|
||||
.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() ||
|
||||
filesExistingInTargetDir.singleOrNull()?.let { sourceFiles.contains(it) } == true
|
||||
|
||||
if (!moveAsFile) return null
|
||||
|
||||
sourceFiles.forEach { it.updatePackageDirective = isUpdatePackageDirective }
|
||||
sourceFiles.forEach { it.updatePackageDirective = true }
|
||||
|
||||
return if (targetFileName != null)
|
||||
return if (targetFileNameAndFile != null)
|
||||
MoveToKotlinFileProcessor(
|
||||
project,
|
||||
sourceFiles.first(),
|
||||
targetFileNameAndFile.second,
|
||||
targetDirectory,
|
||||
targetFileName,
|
||||
targetFileNameAndFile.first,
|
||||
searchInComments = isSearchInComments,
|
||||
searchInNonJavaFiles = isSearchInNonJavaFiles,
|
||||
moveCallback = moveCallback,
|
||||
@@ -291,13 +302,16 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
|
||||
private fun moveDeclaration(throwOnConflicts: Boolean): BaseRefactoringProcessor {
|
||||
|
||||
val elementsWithMPPIfNeeded =
|
||||
if (elementsToMoveHasMPP) elementsToMove
|
||||
.flatMap { it.collectAllExpectAndActualDeclaration() }
|
||||
.filterIsInstance<KtNamedDeclaration>()
|
||||
else elementsToMove
|
||||
if (elementsToMoveHasMPP) require(isMoveToPackage && selectedPsiDirectory == null)
|
||||
|
||||
val target = if (isMoveToPackage) selectMoveTargetToPackage() else selectMoveTargetToFile()
|
||||
|
||||
val elementsWithMPPIfNeeded = if (elementsToMoveHasMPP)
|
||||
elementsToMove.mapWithReadActionInProcess(project, MoveHandler.REFACTORING_NAME) {
|
||||
it.collectAllExpectAndActualDeclaration()
|
||||
}.flatten().filterIsInstance<KtNamedDeclaration>()
|
||||
else elementsToMove
|
||||
|
||||
val target = selectMoveTarget()
|
||||
for (element in elementsWithMPPIfNeeded) {
|
||||
target.verify(element.containingFile)?.let { throw ConfigurationException(it) }
|
||||
}
|
||||
@@ -309,7 +323,7 @@ internal class MoveKotlinTopLevelDeclarationsModel(
|
||||
MoveDeclarationsDelegate.TopLevel,
|
||||
isSearchInComments,
|
||||
isSearchInNonJavaFiles,
|
||||
deleteSourceFiles = isFullFileMove && isDeleteEmptyFiles,
|
||||
deleteSourceFiles = isDeleteEmptyFiles,
|
||||
moveCallback = moveCallback,
|
||||
openInEditor = false,
|
||||
allElementsToMove = null,
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
package org.jetbrains.kotlin.idea.refactoring.move
|
||||
|
||||
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.Task
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.util.Key
|
||||
@@ -58,7 +61,6 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.io.File
|
||||
import java.lang.System.currentTimeMillis
|
||||
import java.util.*
|
||||
import javax.swing.JCheckBox
|
||||
|
||||
sealed class ContainerInfo {
|
||||
abstract val fqName: FqName?
|
||||
@@ -708,4 +710,27 @@ internal fun logFusForMoveRefactoring(
|
||||
isSucceeded = succeeded,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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