diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 7f870aa28e0..673f6ef984a 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -251,7 +251,8 @@
+ implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.KotlinMoveFilesOrDirectoriesHandler"
+ order="before moveJavaFileOrDir"/>
, targetElement: PsiElement?): Array {
+ override fun adjustForMove(project: Project, sourceElements: Array, targetElement: PsiElement?): Array {
return sourceElements.map { if (it is JetClassOrObject) it.getContainingFile() else it }.toTypedArray()
}
+ override fun doMove(project: Project, elements: Array, targetContainer: PsiElement?, callback: MoveCallback?) {
+ if (!(targetContainer == null || targetContainer is PsiDirectory || targetContainer is PsiDirectoryContainer)) return
+
+ moveFilesOrDirectories(project, adjustForMove(project, elements, targetContainer), targetContainer) {
+ callback?.refactoringCompleted()
+ }
+ }
+
companion object {
public fun isMovableClass(clazz: JetClassOrObject): Boolean {
return clazz.isTopLevel() && clazz.getContainingJetFile().getDeclarations().all { it !is JetClassOrObject || it == clazz }
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt
index 42ee40a5fcf..18aa9ac3a75 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveK
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.Mover
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
+import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetNamedDeclaration
@@ -60,11 +61,12 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
): List? {
clearState()
- if (psiFile !is JetFile || !psiFile.packageMatchesDirectory()) return null
+ if (psiFile !is JetFile || !(psiFile.updatePackageDirective ?: psiFile.packageMatchesDirectory())) return null
val newPackage = newParent.getPackage() ?: return null
val packageNameInfo = PackageNameInfo(psiFile.getPackageFqName(), FqName(newPackage.getQualifiedName()))
+ if (packageNameInfo.oldPackageName == packageNameInfo.newPackageName) return null
val project = psiFile.getProject()
val declarationMoveProcessor = MoveKotlinTopLevelDeclarationsProcessor(
@@ -93,6 +95,8 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
override fun updateMovedFile(file: PsiFile) {
if (file !is JetFile) return
+
+ file.updatePackageDirective = null
val packageNameInfo = packageNameInfo ?: return
val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
index cb0dcf64356..5c2b340b608 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
@@ -32,6 +32,7 @@ import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackages
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.core.refactoring.isInJavaSourceRoot
+import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getPackage
@@ -51,11 +52,7 @@ public class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
val sourceFile = sourceFiles.singleOrNull()
if (sourceFile == null) {
if (sourceFiles.all { it.getDeclarations().size() == 1 }) {
- MoveFilesOrDirectoriesUtil.doMove(project,
- sourceFiles.toTypedArray(),
- arrayOf(targetContainer),
- null)
-
+ moveFilesOrDirectories(project, sourceFiles.toTypedArray(), targetContainer)
return true
}
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveFilesOrDirectoriesDialogWithKotlinOptions.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveFilesOrDirectoriesDialogWithKotlinOptions.kt
new file mode 100644
index 00000000000..8999fd30902
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveFilesOrDirectoriesDialogWithKotlinOptions.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010-2015 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.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui
+
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiDirectory
+import com.intellij.psi.PsiElement
+import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesDialog
+import com.intellij.ui.NonFocusableCheckBox
+import com.intellij.util.ui.UIUtil
+import org.jetbrains.kotlin.psi.JetFile
+import org.jetbrains.kotlin.psi.psiUtil.packageMatchesDirectory
+import java.awt.GridBagConstraints
+import java.awt.Insets
+import javax.swing.JCheckBox
+import javax.swing.JComponent
+import javax.swing.JLabel
+import kotlin.properties.Delegates
+
+public class MoveFilesOrDirectoriesDialogWithKotlinOptions(
+ project: Project,
+ callback: (MoveFilesOrDirectoriesDialog?) -> Unit): MoveFilesOrDirectoriesDialog(project, callback) {
+ private var cbUpdatePackageDirective: JCheckBox? = null
+
+ public val updatePackageDirective: Boolean
+ get() = cbUpdatePackageDirective!!.isSelected()
+
+ override fun createNorthPanel(): JComponent {
+ val panel = super.createNorthPanel()
+
+ val gbc = GridBagConstraints()
+
+ gbc.gridwidth = 1
+ gbc.gridx = 0
+ gbc.gridy = GridBagConstraints.RELATIVE
+ gbc.weightx = 0.0
+ gbc.weighty = 0.0
+ gbc.fill = GridBagConstraints.NONE
+ gbc.anchor = GridBagConstraints.WEST
+ gbc.insets = Insets(UIUtil.LARGE_VGAP, 0, 0, UIUtil.DEFAULT_HGAP)
+ panel.add(JLabel(), gbc)
+
+ cbUpdatePackageDirective = NonFocusableCheckBox()
+ gbc.gridx = 1
+ gbc.weightx = 1.0
+ gbc.weighty = 0.0
+ gbc.fill = GridBagConstraints.HORIZONTAL
+ gbc.anchor = GridBagConstraints.WEST
+ gbc.insets = Insets(UIUtil.LARGE_VGAP, 0, 0, 0)
+ panel.add(cbUpdatePackageDirective, gbc)
+
+ return panel
+ }
+
+ override fun setData(psiElements: Array, initialTargetDirectory: PsiDirectory?, helpID: String) {
+ super.setData(psiElements, initialTargetDirectory, helpID)
+
+ with (cbUpdatePackageDirective!!) {
+ val jetFiles = psiElements.filterIsInstance()
+ if (jetFiles.isEmpty()) {
+ getParent().remove(cbUpdatePackageDirective)
+ return
+ }
+
+ val singleFile = jetFiles.singleOrNull()
+ if (singleFile != null) {
+ setSelected(singleFile.packageMatchesDirectory())
+ setText("Update package directive")
+ }
+ else {
+ setSelected(true)
+ setText("Update package directive for Kotlin files")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt
index 8acea9f02ce..9d108e8b931 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt
@@ -16,15 +16,31 @@
package org.jetbrains.kotlin.idea.refactoring.move
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.command.CommandProcessor
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.Comparing
+import com.intellij.openapi.util.Computable
+import com.intellij.openapi.util.Key
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.*
+import com.intellij.refactoring.RefactoringBundle
+import com.intellij.refactoring.RefactoringSettings
+import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
+import com.intellij.refactoring.move.MoveCallback
+import com.intellij.refactoring.move.MoveHandler
+import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesDialog
+import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor
+import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions
import com.intellij.refactoring.move.moveMembers.MoveMemberHandler
import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor
+import com.intellij.refactoring.util.CommonRefactoringUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.usageView.UsageInfo
+import com.intellij.util.Function
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped
@@ -35,10 +51,13 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.codeInsight.JetFileReferencesResolver
import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
+import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveFilesOrDirectoriesDialogWithKotlinOptions
import org.jetbrains.kotlin.idea.references.JetReference
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
+import org.jetbrains.kotlin.idea.util.application.executeCommand
+import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
@@ -261,3 +280,73 @@ fun postProcessMoveUsages(usages: List,
return nonCodeUsages
}
+
+var JetFile.updatePackageDirective: Boolean? by UserDataProperty(Key.create("UPDATE_PACKAGE_DIRECTIVE"))
+
+// Mostly copied from MoveFilesOrDirectoriesUtil.doMove()
+public fun moveFilesOrDirectories(
+ project: Project,
+ elements: Array,
+ targetElement: PsiElement?,
+ moveCallback: (() -> Unit)? = null
+) {
+ elements.forEach { if (it !is PsiFile && it !is PsiDirectory) throw IllegalArgumentException("unexpected element type: " + it) }
+
+ val targetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, targetElement)
+ if (targetElement != null && targetDirectory == null) return
+
+ val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements)
+
+ fun doRun(moveDialog: MoveFilesOrDirectoriesDialog?) {
+ fun closeDialog() {
+ moveDialog?.close(DialogWrapper.CANCEL_EXIT_CODE)
+ }
+
+ project.executeCommand(MoveHandler.REFACTORING_NAME) {
+ val selectedDir = if (moveDialog != null) moveDialog.getTargetDirectory() else initialTargetDirectory
+ val updatePackageDirective = (moveDialog as? MoveFilesOrDirectoriesDialogWithKotlinOptions)?.updatePackageDirective
+
+ try {
+ val choice = if (elements.size() > 1 || elements[0] is PsiDirectory) intArrayOf(-1) else null
+ val elementsToMove = elements.filterNot {
+ it is PsiFile
+ && runWriteAction { CopyFilesOrDirectoriesHandler.checkFileExist(selectedDir, choice, it, it.getName(), "Move") }
+ }
+
+ elementsToMove.forEach {
+ MoveFilesOrDirectoriesUtil.checkMove(it, selectedDir)
+ (it as? JetFile)?.let { it.updatePackageDirective = updatePackageDirective }
+ }
+
+ if (elementsToMove.isNotEmpty()) {
+ MoveFilesOrDirectoriesProcessor(
+ project,
+ elementsToMove.toTypedArray(),
+ selectedDir,
+ RefactoringSettings.getInstance().MOVE_SEARCH_FOR_REFERENCES_FOR_FILE,
+ false,
+ false,
+ moveCallback,
+ ::closeDialog
+ ).run()
+ }
+ else {
+ closeDialog()
+ }
+ }
+ catch (e: IncorrectOperationException) {
+ CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), "refactoring.moveFile", project)
+ }
+ }
+ }
+
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ doRun(null)
+ return
+ }
+
+ with(MoveFilesOrDirectoriesDialogWithKotlinOptions(project, ::doRun)) {
+ setData(elements, initialTargetDirectory, "refactoring.moveFile")
+ show()
+ }
+}