Move top-level declarations to separate file: UI

This commit is contained in:
Alexey Sedunov
2014-03-20 14:43:39 +04:00
parent 5aa75ba3e1
commit 46045a5538
5 changed files with 223 additions and 77 deletions
@@ -22,6 +22,8 @@ column.name.val.var=Val/Var
refactoring.move.top.level.declarations=Move top-level declarations
refactoring.move.specifc.element=Move {0} {1}
refactoring.move.selected.elements=Move selected elements
refactoring.move.top.level.declaration.file.title=Choose Destination File
refactoring.move.non.kotlin.file=Target must be a Kotlin file
package.private.0.will.no.longer.be.accessible.from.1=Package-private {0} will no longer be accessible from {1}
0.uses.package.private.1={0} uses package-private {1}
@@ -40,6 +40,12 @@ import com.intellij.refactoring.util.ConflictsUtil
import org.jetbrains.jet.lang.psi.psiUtil.getPackage
import com.intellij.psi.PsiFileFactory
import org.jetbrains.jet.plugin.JetFileType
import com.intellij.openapi.project.Project
import java.io.File
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiFile
import com.intellij.openapi.vfs.VirtualFile
/**
* Replace [[JetSimpleNameExpression]] (and its enclosing qualifier) with qualified element given by FqName
@@ -83,6 +89,12 @@ fun createKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile {
return targetDir.add(file) as JetFile
}
public fun File.toVirtualFile(): VirtualFile? = LocalFileSystem.getInstance()!!.findFileByIoFile(this)
public fun File.toPsiFile(project: Project): PsiFile? {
return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findFile(vfile) }
}
/**
* Returns FqName for given declaration (either Java or Kotlin)
*/
@@ -23,7 +23,6 @@ import com.intellij.psi.PsiElement
import com.intellij.refactoring.move.MoveCallback
import com.intellij.psi.PsiReference
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.plugin.refactoring.isInJavaSourceRoot
import org.jetbrains.jet.lang.psi.JetObjectDeclaration
@@ -74,9 +73,17 @@ public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
val targetDirectory = MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements)
val searchInComments = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_IN_COMMENTS
val searchInText = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_FOR_TEXT
val targetFile: JetFile? = when (targetContainer) {
is JetFile -> targetContainer
else -> {
val files = elements.mapTo(HashSet<JetFile>()) { e -> e.getContainingFile() as JetFile }
if (files.size == 1) files.first() else null
}
}
val moveToPackage = targetContainer !is JetFile
MoveKotlinTopLevelDeclarationsDialog(
project, elementsToSearch, targetPackageName, targetDirectory, searchInComments, searchInText, callback
project, elementsToSearch, targetPackageName, targetDirectory, targetFile, moveToPackage, searchInComments, searchInText, callback
).show()
return true
@@ -93,7 +100,7 @@ public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
}
override fun isValidTarget(psiElement: PsiElement?, sources: Array<out PsiElement>): Boolean {
return psiElement is PsiPackage || (psiElement is PsiDirectory && psiElement.getPackage() != null)
return psiElement is PsiPackage || (psiElement is PsiDirectory && psiElement.getPackage() != null) || psiElement is JetFile
}
override fun doMove(project: Project, elements: Array<out PsiElement>, targetContainer: PsiElement?, callback: MoveCallback?) {
@@ -68,7 +68,7 @@
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="fced3" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="fced3" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="7" left="0" bottom="7" right="0"/>
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -82,7 +82,7 @@
</constraints>
<properties/>
</component>
<component id="f73ba" class="javax.swing.JLabel">
<component id="765d3" class="javax.swing.JRadioButton" binding="rbMoveToPackage">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
@@ -90,14 +90,28 @@
<text value="To package:"/>
</properties>
</component>
<component id="9ea97" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="fileChooser">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="58e61" class="javax.swing.JRadioButton" binding="rbMoveToFile">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="To file:"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
<buttonGroups>
<group name="buttonGroup1">
<member id="63b06"/>
<member id="37e02"/>
<member id="765d3"/>
<member id="58e61"/>
</group>
</buttonGroups>
</form>
@@ -18,15 +18,17 @@ package org.jetbrains.jet.plugin.refactoring.move.moveTopLevelDeclarations.ui;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.JavaProjectRootsUtil;
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.Pass;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiNameHelper;
import com.intellij.psi.*;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.PackageWrapper;
@@ -45,12 +47,16 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamedDeclaration;
import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle;
import org.jetbrains.jet.plugin.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsOptions;
import org.jetbrains.jet.plugin.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor;
import org.jetbrains.jet.plugin.refactoring.RefactoringPackage;
import org.jetbrains.jet.plugin.refactoring.move.moveTopLevelDeclarations.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;
public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
@@ -64,6 +70,9 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private ComboboxWithBrowseButton destinationFolderCB;
private JPanel targetPanel;
private JLabel targetDestinationLabel;
private JRadioButton rbMoveToPackage;
private JRadioButton rbMoveToFile;
private TextFieldWithBrowseButton fileChooser;
private final List<JetNamedDeclaration> elementsToMove;
private final MoveCallback moveCallback;
@@ -73,6 +82,8 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
@NotNull List<JetNamedDeclaration> elementsToMove,
@Nullable String targetPackageName,
@Nullable PsiDirectory targetDirectory,
@Nullable JetFile targetFile,
boolean moveToPackage,
boolean searchInComments,
boolean searchForTextOccurences,
@Nullable MoveCallback moveCallback
@@ -86,6 +97,84 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
setTitle(MoveHandler.REFACTORING_NAME);
initElementDescription(elementsToMove);
initSearchOptions(searchInComments, searchForTextOccurences);
initPackageChooser(targetPackageName, targetDirectory);
initFileChooser(targetFile);
initMoveToButtons(moveToPackage);
updateControls();
}
private void initPackageChooser(String targetPackageName, PsiDirectory targetDirectory) {
if (targetPackageName != null && targetPackageName.length() != 0) {
classPackageChooser.prependItem(targetPackageName);
}
((DestinationFolderComboBox) destinationFolderCB).setData(
myProject,
targetDirectory,
new Pass<String>() {
@Override
public void pass(String s) {
setErrorText(s);
}
},
classPackageChooser.getChildComponent()
);
}
private void initSearchOptions(boolean searchInComments, boolean searchForTextOccurences) {
cbSearchInComments.setSelected(searchInComments);
cbSearchTextOccurences.setSelected(searchForTextOccurences);
}
private void initMoveToButtons(boolean moveToPackage) {
if (moveToPackage) {
rbMoveToPackage.setSelected(true);
}
else {
rbMoveToFile.setSelected(true);
}
rbMoveToPackage.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
classPackageChooser.requestFocus();
updateControls();
}
}
);
rbMoveToFile.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
fileChooser.requestFocus();
updateControls();
}
}
);
}
private void initFileChooser(JetFile targetFile) {
FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor();
descriptor.setRoots(ProjectRootManager.getInstance(myProject).getContentRoots());
descriptor.setIsTreeRootVisible(true);
String title = JetRefactoringBundle.message("refactoring.move.top.level.declaration.file.title");
fileChooser.addBrowseFolderListener(title, null, myProject, descriptor, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT);
if (targetFile != null) {
fileChooser.setText(targetFile.getVirtualFile().getPath());
}
}
private void initElementDescription(List<JetNamedDeclaration> elementsToMove) {
if (elementsToMove.size() == 1) {
PsiElement element = elementsToMove.get(0);
elementDescription.setText(
@@ -99,28 +188,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
else if (elementsToMove.size() > 1) {
elementDescription.setText(JetRefactoringBundle.message("refactoring.move.selected.elements"));
}
if (targetPackageName != null && targetPackageName.length() != 0) {
classPackageChooser.prependItem(targetPackageName);
}
cbSearchInComments.setSelected(searchInComments);
cbSearchTextOccurences.setSelected(searchForTextOccurences);
((DestinationFolderComboBox) destinationFolderCB).setData(
myProject,
targetDirectory,
new Pass<String>() {
@Override
public void pass(String s) {
setErrorText(s);
}
},
classPackageChooser.getChildComponent()
);
UIUtil.setEnabled(targetPanel, hasAnySourceRoots(), true);
validateButtons();
}
private void createUIComponents() {
@@ -148,10 +215,74 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return packageChooser;
}
private void updateControls() {
boolean moveToPackage = isMoveToPackage();
classPackageChooser.setEnabled(moveToPackage);
fileChooser.setEnabled(!moveToPackage);
UIUtil.setEnabled(targetPanel, moveToPackage && hasAnySourceRoots(), true);
validateButtons();
}
private boolean hasAnySourceRoots() {
return !JavaProjectRootsUtil.getSuitableDestinationSourceRoots(myProject).isEmpty();
}
private void saveRefactoringSettings() {
JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance();
refactoringSettings.MOVE_SEARCH_IN_COMMENTS = isSearchInComments();
refactoringSettings.MOVE_SEARCH_FOR_TEXT = isSearchInNonJavaFiles();
refactoringSettings.MOVE_PREVIEW_USAGES = isPreviewUsages();
}
@Nullable
private KotlinMoveTarget selectMoveTarget() {
String message = verifyBeforeRun();
if (message != null) {
setErrorText(message);
return null;
}
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;
}
MoveDestination moveDestination = ((DestinationFolderComboBox) destinationFolderCB).selectDirectory(targetPackage, false);
return moveDestination != null ? new MoveDestinationKotlinMoveTarget(moveDestination) : null;
}
JetFile jetFile = (JetFile) RefactoringPackage.toPsiFile(new File(getTargetFilePath()), myProject);
assert jetFile != null : "Non-Kotlin files must be filtered out before starting the refactoring";
return new JetFileKotlinMoveTarget(jetFile);
}
@Nullable
private String verifyBeforeRun() {
if (isMoveToPackage()) {
String name = getTargetPackage().trim();
if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) {
return "\'" + name + "\' is invalid destination package name";
}
}
else {
PsiFile targetFile = RefactoringPackage.toPsiFile(new File(getTargetFilePath()), myProject);
if (!(targetFile instanceof JetFile)) {
return JetRefactoringBundle.message("refactoring.move.non.kotlin.file");
}
}
return null;
}
@Override
protected JComponent createCenterPanel() {
boolean isDestinationVisible = hasAnySourceRoots();
@@ -170,59 +301,30 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return "#com.intellij.refactoring.move.moveClassesOrPackages.MoveKotlinTopLevelDeclarationsDialog.classes";
}
@Override
public JComponent getPreferredFocusedComponent() {
return classPackageChooser.getChildComponent();
protected final String getTargetPackage() {
return classPackageChooser.getText();
}
private void saveRefactoringSettings() {
JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance();
refactoringSettings.MOVE_SEARCH_IN_COMMENTS = isSearchInComments();
refactoringSettings.MOVE_SEARCH_FOR_TEXT = isSearchInNonJavaFiles();
refactoringSettings.MOVE_PREVIEW_USAGES = isPreviewUsages();
}
@Nullable
private MoveDestination selectDestination() {
String packageName = getTargetPackage().trim();
if (packageName.length() > 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(packageName)) {
Messages.showErrorDialog(myProject, RefactoringBundle.message("please.enter.a.valid.target.package.name"),
RefactoringBundle.message("move.title"));
return null;
}
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);
protected final String getTargetFilePath() {
return fileChooser.getText();
}
@Override
protected void canRun() throws ConfigurationException {
String name = getTargetPackage().trim();
if (name.length() != 0 && !PsiNameHelper.getInstance(myProject).isQualifiedName(name)) {
throw new ConfigurationException("\'" + name + "\' is invalid destination package name");
String message = verifyBeforeRun();
if (message != null) {
throw new ConfigurationException(message);
}
}
protected String getTargetPackage() {
return classPackageChooser.getText();
}
@Override
protected void doAction() {
MoveDestination destination = selectDestination();
if (destination == null) return;
KotlinMoveTarget target = selectMoveTarget();
if (target == null) return;
saveRefactoringSettings();
for (PsiElement element : elementsToMove) {
String message = destination.verify(element.getContainingFile());
String message = target.verify(element.getContainingFile());
if (message != null) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), message, null, myProject);
return;
@@ -231,7 +333,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
try {
MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions(
elementsToMove, destination, isSearchInComments(), isSearchInNonJavaFiles(), moveCallback
elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), moveCallback
);
invokeRefactoring(new MoveKotlinTopLevelDeclarationsProcessor(myProject, options));
}
@@ -240,11 +342,20 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
}
}
protected final boolean isSearchInNonJavaFiles() {
private boolean isSearchInNonJavaFiles() {
return cbSearchTextOccurences.isSelected();
}
protected final boolean isSearchInComments() {
private boolean isSearchInComments() {
return cbSearchInComments.isSelected();
}
private boolean isMoveToPackage() {
return rbMoveToPackage.isSelected();
}
@Override
public JComponent getPreferredFocusedComponent() {
return classPackageChooser.getChildComponent();
}
}