diff --git a/annotations/com/intellij/refactoring/move/annotations.xml b/annotations/com/intellij/refactoring/move/annotations.xml
new file mode 100644
index 00000000000..cedcd6882be
--- /dev/null
+++ b/annotations/com/intellij/refactoring/move/annotations.xml
@@ -0,0 +1,33 @@
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 4237ad49b71..91551ddd72f 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -170,7 +170,13 @@
-
+
+
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties
index 4da8c73edea..e7618766d8e 100644
--- a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties
@@ -19,5 +19,9 @@ parameter.name.is.invalid=Parameter name ''{0}'' is invalid
parameter.type.is.invalid=Parameter type ''{0}'' is invalid
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
+
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}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/jetRefactoringUtil.kt
index 5a710edf8c6..f9945bd3ef5 100644
--- a/idea/src/org/jetbrains/jet/plugin/refactoring/jetRefactoringUtil.kt
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/jetRefactoringUtil.kt
@@ -28,15 +28,18 @@ import com.intellij.psi.PsiElement
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiDirectory
import org.jetbrains.jet.lang.psi.JetFile
-import com.intellij.psi.PsiFileFactory
-import org.jetbrains.jet.lang.psi.psiUtil.getPackage
-import org.jetbrains.jet.plugin.JetFileType
+import com.intellij.openapi.roots.JavaProjectRootsUtil
import com.intellij.psi.PsiPackage
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiMember
-import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import org.jetbrains.jet.lang.psi.JetPsiUtil
import org.jetbrains.jet.asJava.namedUnwrappedElement
+import org.jetbrains.jet.lang.psi.JetNamedDeclaration
+import com.intellij.psi.util.PsiTreeUtil
+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
/**
* Replace [[JetSimpleNameExpression]] (and its enclosing qualifier) with qualified element given by FqName
@@ -102,4 +105,7 @@ public fun PsiElement.getUsageContext(): PsiElement {
is JetElement -> PsiTreeUtil.getParentOfType(this, javaClass(), javaClass())!!
else -> ConflictsUtil.getContainer(this)
}
-}
\ No newline at end of file
+}
+
+public fun PsiElement.isInJavaSourceRoot(): Boolean =
+ !JavaProjectRootsUtil.isOutsideJavaSourceRoot(getContainingFile())
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
new file mode 100644
index 00000000000..839e732c41a
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2010-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.jet.plugin.refactoring.move.moveTopLevelDeclarations
+
+import com.intellij.refactoring.move.MoveHandlerDelegate
+import com.intellij.openapi.actionSystem.DataContext
+import com.intellij.openapi.project.Project
+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
+import org.jetbrains.jet.lang.psi.JetClass
+import com.intellij.psi.PsiPackage
+import com.intellij.psi.PsiDirectory
+import org.jetbrains.jet.lang.psi.psiUtil.getPackage
+import org.jetbrains.jet.plugin.refactoring.move.getFileNameAfterMove
+import java.util.HashSet
+import com.intellij.refactoring.RefactoringBundle
+import com.intellij.refactoring.util.CommonRefactoringUtil
+import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl
+import org.jetbrains.jet.plugin.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
+import com.intellij.refactoring.JavaRefactoringSettings
+import com.intellij.openapi.actionSystem.LangDataKeys
+import org.jetbrains.jet.lang.psi.JetNamedFunction
+import org.jetbrains.jet.lang.psi.JetProperty
+import org.jetbrains.jet.lang.psi.JetNamedDeclaration
+
+public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
+ private fun doMoveWithCheck(
+ project: Project, elements: Array, targetContainer: PsiElement?, callback: MoveCallback?
+ ): Boolean {
+ fun checkNameConflicts(): Boolean {
+ val fileNames = HashSet()
+ for (element in elements) {
+ if (element !is JetNamedDeclaration) continue
+
+ val fileName = element.getFileNameAfterMove()
+ if (fileName != null && !fileNames.add(fileName)) {
+ val message = RefactoringBundle.getCannotRefactorMessage(
+ RefactoringBundle.message("there.are.going.to.be.multiple.destination.files.with.the.same.name")
+ )
+ CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("move.title"), message, null, project)
+ return false
+ }
+ }
+
+ return true
+ }
+
+ if (!checkNameConflicts()) return false
+ if (!CommonRefactoringUtil.checkReadOnlyStatusRecursively(project, elements.toList(), true)) return false
+
+ [suppress("UNCHECKED_CAST")]
+ val elementsToSearch = elements.toList() as List
+ val targetPackageName = MoveClassesOrPackagesImpl.getInitialTargetPackageName(targetContainer, elements)
+ val targetDirectory = MoveClassesOrPackagesImpl.getInitialTargetDirectory(targetContainer, elements)
+ val searchInComments = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_IN_COMMENTS
+ val searchInText = JavaRefactoringSettings.getInstance()!!.MOVE_SEARCH_FOR_TEXT
+
+ MoveKotlinTopLevelDeclarationsDialog(
+ project, elementsToSearch, targetPackageName, targetDirectory, searchInComments, searchInText, callback
+ ).show()
+
+ return true
+ }
+
+ override fun canMove(elements: Array, targetContainer: PsiElement?): Boolean {
+ return super.canMove(elements, targetContainer) && elements.all { e ->
+ if (e is JetClass || (e is JetObjectDeclaration && !e.isObjectLiteral()) || e is JetNamedFunction || e is JetProperty) {
+ val parent = e.getParent()
+ parent is JetFile && parent.isInJavaSourceRoot()
+ }
+ else false
+ }
+ }
+
+ override fun isValidTarget(psiElement: PsiElement?, sources: Array): Boolean {
+ return psiElement is PsiPackage || (psiElement is PsiDirectory && psiElement.getPackage() != null)
+ }
+
+ override fun doMove(project: Project, elements: Array, targetContainer: PsiElement?, callback: MoveCallback?) {
+ doMoveWithCheck(project, elements, targetContainer, callback)
+ }
+
+ override fun tryToMove(
+ element: PsiElement, project: Project, dataContext: DataContext?, reference: PsiReference?, editor: Editor?
+ ): Boolean {
+ val elementsToMove = array(element)
+ val targetContainer = dataContext?.let { dataContext -> LangDataKeys.TARGET_PSI_ELEMENT.getData(dataContext) }
+
+ return canMove(elementsToMove, targetContainer) && doMoveWithCheck(project, elementsToMove, targetContainer, null)
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
new file mode 100644
index 00000000000..ed7458f63e9
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
@@ -0,0 +1,103 @@
+
+
diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java
new file mode 100644
index 00000000000..4347fd21a68
--- /dev/null
+++ b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2010-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+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.options.ConfigurationException;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.roots.JavaProjectRootsUtil;
+import com.intellij.openapi.ui.Messages;
+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.refactoring.JavaRefactoringSettings;
+import com.intellij.refactoring.MoveDestination;
+import com.intellij.refactoring.PackageWrapper;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.refactoring.move.MoveCallback;
+import com.intellij.refactoring.move.MoveHandler;
+import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox;
+import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo;
+import com.intellij.refactoring.ui.RefactoringDialog;
+import com.intellij.refactoring.util.CommonRefactoringUtil;
+import com.intellij.ui.ComboboxWithBrowseButton;
+import com.intellij.ui.RecentsManager;
+import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
+import com.intellij.usageView.UsageViewUtil;
+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.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 javax.swing.*;
+import java.util.List;
+
+public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
+ private static final String RECENTS_KEY = "MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY";
+
+ private JLabel elementDescription;
+ private JCheckBox cbSearchInComments;
+ private JCheckBox cbSearchTextOccurences;
+ private JPanel mainPanel;
+ private ReferenceEditorComboWithBrowseButton classPackageChooser;
+ private ComboboxWithBrowseButton destinationFolderCB;
+ private JPanel targetPanel;
+ private JLabel targetDestinationLabel;
+
+ private final List elementsToMove;
+ private final MoveCallback moveCallback;
+
+ public MoveKotlinTopLevelDeclarationsDialog(
+ @NotNull Project project,
+ @NotNull List elementsToMove,
+ @Nullable String targetPackageName,
+ @Nullable PsiDirectory targetDirectory,
+ boolean searchInComments,
+ boolean searchForTextOccurences,
+ @Nullable MoveCallback moveCallback
+ ) {
+ super(project, true);
+
+ this.elementsToMove = elementsToMove;
+ this.moveCallback = moveCallback;
+
+ init();
+
+ setTitle(MoveHandler.REFACTORING_NAME);
+
+ if (elementsToMove.size() == 1) {
+ PsiElement element = elementsToMove.get(0);
+ elementDescription.setText(
+ JetRefactoringBundle.message(
+ "refactoring.move.specifc.element",
+ UsageViewUtil.getType(element),
+ UsageViewUtil.getLongName(element)
+ )
+ );
+ }
+ 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() {
+ @Override
+ public void pass(String s) {
+ setErrorText(s);
+ }
+ },
+ classPackageChooser.getChildComponent()
+ );
+
+ UIUtil.setEnabled(targetPanel, hasAnySourceRoots(), true);
+ validateButtons();
+ }
+
+ private void createUIComponents() {
+ classPackageChooser = createPackageChooser();
+
+ destinationFolderCB = new DestinationFolderComboBox() {
+ @Override
+ public String getTargetPackage() {
+ return MoveKotlinTopLevelDeclarationsDialog.this.getTargetPackage();
+ }
+ };
+ }
+
+ private ReferenceEditorComboWithBrowseButton createPackageChooser() {
+ ReferenceEditorComboWithBrowseButton packageChooser =
+ new PackageNameReferenceEditorCombo("", myProject, RECENTS_KEY, RefactoringBundle.message("choose.destination.package"));
+ Document document = packageChooser.getChildComponent().getDocument();
+ document.addDocumentListener(new DocumentAdapter() {
+ @Override
+ public void documentChanged(DocumentEvent e) {
+ validateButtons();
+ }
+ });
+
+ return packageChooser;
+ }
+
+ private boolean hasAnySourceRoots() {
+ return !JavaProjectRootsUtil.getSuitableDestinationSourceRoots(myProject).isEmpty();
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ boolean isDestinationVisible = hasAnySourceRoots();
+ destinationFolderCB.setVisible(isDestinationVisible);
+ targetDestinationLabel.setVisible(isDestinationVisible);
+ return null;
+ }
+
+ @Override
+ protected JComponent createNorthPanel() {
+ return mainPanel;
+ }
+
+ @Override
+ protected String getDimensionServiceKey() {
+ return "#com.intellij.refactoring.move.moveClassesOrPackages.MoveKotlinTopLevelDeclarationsDialog.classes";
+ }
+
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return classPackageChooser.getChildComponent();
+ }
+
+ 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);
+ }
+
+ @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");
+ }
+ }
+
+ protected String getTargetPackage() {
+ return classPackageChooser.getText();
+ }
+
+ @Override
+ protected void doAction() {
+ MoveDestination destination = selectDestination();
+ if (destination == null) return;
+
+ saveRefactoringSettings();
+ for (PsiElement element : elementsToMove) {
+ String message = destination.verify(element.getContainingFile());
+ if (message != null) {
+ CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), message, null, myProject);
+ return;
+ }
+ }
+
+ try {
+ MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions(
+ elementsToMove, destination, isSearchInComments(), isSearchInNonJavaFiles(), moveCallback
+ );
+ invokeRefactoring(new MoveKotlinTopLevelDeclarationsProcessor(myProject, options));
+ }
+ catch (IncorrectOperationException e) {
+ CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject);
+ }
+ }
+
+ protected final boolean isSearchInNonJavaFiles() {
+ return cbSearchTextOccurences.isSelected();
+ }
+
+ protected final boolean isSearchInComments() {
+ return cbSearchInComments.isSelected();
+ }
+}
\ No newline at end of file