diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 984561b4f8a..7df0566acaf 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -286,6 +286,9 @@
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsHandler"
order="before kotlin.moveFilesOrDirectories"/>
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
index 87d29ed4848..c1e7efe898a 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
@@ -22,6 +22,9 @@ import com.intellij.codeInsight.unwrap.UnwrapHandler
import com.intellij.ide.util.PsiElementListCellRenderer
import com.intellij.lang.java.JavaLanguage
import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.command.CommandAdapter
+import com.intellij.openapi.command.CommandEvent
+import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.colors.EditorColors
import com.intellij.openapi.editor.colors.EditorColorsManager
@@ -111,6 +114,8 @@ public fun File.toPsiDirectory(project: Project): PsiDirectory? {
public fun VirtualFile.toPsiFile(project: Project): PsiFile? = PsiManager.getInstance(project).findFile(this)
+public fun VirtualFile.toPsiDirectory(project: Project): PsiDirectory? = PsiManager.getInstance(project).findDirectory(this)
+
public fun PsiElement.getUsageContext(): PsiElement {
return when (this) {
is JetElement -> PsiTreeUtil.getParentOfType(this, javaClass(), javaClass())!!
@@ -645,4 +650,15 @@ public fun Project.runSynchronouslyWithProgress(progressTitle: String,
var result: T? = null
ProgressManager.getInstance().runProcessWithProgressSynchronously( { result = action() }, progressTitle, canBeCanceled, this)
return result
+}
+
+public fun invokeOnceOnCommandFinish(action: () -> Unit) {
+ val commandProcessor = CommandProcessor.getInstance()
+ val listener = object: CommandAdapter() {
+ override fun beforeCommandFinished(event: CommandEvent?) {
+ action()
+ commandProcessor.removeCommandListener(this)
+ }
+ }
+ commandProcessor.addCommandListener(listener)
}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt
index 6c6fee53952..1a038881afa 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt
@@ -58,7 +58,7 @@ public class KotlinChangePackageRefactoring(val file: JetFile) {
)
val declarationUsages = declarationProcessor.findUsages().toList()
- val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(PackageNameInfo(currentFqName, newFqName))
+ val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(PackageNameInfo(currentFqName, newFqName.toUnsafe()))
project.executeWriteCommand("Change file's package to '${newFqName.asString()}'") {
packageDirective.setFqName(newFqName)
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt
new file mode 100644
index 00000000000..bb42f9361e9
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/KotlinMoveDirectoryWithClassesHelper.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.moveFilesOrDirectories
+
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiDirectory
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.refactoring.listeners.RefactoringElementListener
+import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper
+import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
+import com.intellij.usageView.UsageInfo
+import com.intellij.util.Function
+import org.jetbrains.kotlin.idea.core.getPackage
+import org.jetbrains.kotlin.idea.core.refactoring.invokeOnceOnCommandFinish
+import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.JetFile
+import java.util.ArrayList
+import java.util.HashMap
+
+public class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
+ private class FileUsagesWrapper(
+ val psiFile: PsiFile,
+ val usages: List,
+ val moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor?
+ ) : UsageInfo(psiFile)
+
+ private class MoveContext(
+ val newParent: PsiDirectory,
+ val internalUsages: MoveKotlinFileHandler.InternalUsagesWrapper,
+ val moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor?
+ )
+
+ private val fileHandler = MoveKotlinFileHandler()
+
+ private var fileToMoveContext: MutableMap? = null
+
+ private fun getOrCreateMoveContextMap(): MutableMap {
+ return fileToMoveContext
+ ?: HashMap().apply {
+ fileToMoveContext = this
+ invokeOnceOnCommandFinish { fileToMoveContext = null }
+ }
+ }
+
+ override fun findUsages(
+ filesToMove: MutableCollection,
+ directoriesToMove: Array,
+ result: MutableCollection,
+ searchInComments: Boolean,
+ searchInNonJavaFiles: Boolean,
+ project: Project) {
+ filesToMove
+ .filterIsInstance()
+ .mapTo(result) { FileUsagesWrapper(it, fileHandler.findUsages(it, null, searchInComments, searchInNonJavaFiles), null) }
+ }
+
+ override fun beforeMove(psiFile: PsiFile) {
+
+ }
+
+ // Actual move logic is implemented in postProcessUsages since usages are not available here
+ override fun move(
+ file: PsiFile,
+ moveDestination: PsiDirectory,
+ oldToNewElementsMapping: MutableMap,
+ movedFiles: MutableList,
+ listener: RefactoringElementListener?
+ ): Boolean {
+ if (file !is JetFile) return false
+
+ val moveDeclarationsProcessor = fileHandler.initMoveProcessor(file, moveDestination)
+ val moveContextMap = getOrCreateMoveContextMap()
+ moveContextMap[file] = MoveContext(moveDestination,
+ fileHandler.findInternalUsages(file, moveDestination),
+ moveDeclarationsProcessor)
+ moveDestination.getPackage()?.let { newPackage -> file.packageDirective?.fqName = FqName(newPackage.qualifiedName) }
+ return true
+ }
+
+ override fun afterMove(newElement: PsiElement) {
+
+ }
+
+ override fun postProcessUsages(usages: Array, newDirMapper: Function) {
+ val fileToMoveContext = fileToMoveContext ?: return
+ try {
+ val usagesToProcessAfterMove = ArrayList()
+ usages
+ .filterIsInstance()
+ .forEach body@ {
+ val file = it.psiFile
+ val moveContext = fileToMoveContext[file] ?: return@body
+
+ MoveFilesOrDirectoriesUtil.doMoveFile(file, moveContext.newParent)
+ val movedFile = moveContext.newParent.findFile(file.name) ?: return@body
+
+ usagesToProcessAfterMove +=
+ FileUsagesWrapper(movedFile, it.usages + moveContext.internalUsages, moveContext.moveDeclarationsProcessor)
+ }
+ usagesToProcessAfterMove.forEach { fileHandler.retargetUsages(it.usages, it.moveDeclarationsProcessor!!) }
+ }
+ finally {
+ this.fileToMoveContext = null
+ }
+ }
+}
\ No newline at end of file
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 aaf5967b409..94e6b20a086 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
@@ -26,22 +26,16 @@ import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.idea.codeInsight.shorten.runWithElementsToShortenIsEmptyIgnored
import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.packageMatchesDirectory
-import org.jetbrains.kotlin.idea.refactoring.move.PackageNameInfo
-import org.jetbrains.kotlin.idea.refactoring.move.getInternalReferencesToUpdateOnPackageNameChange
-import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.DeferredJetFileKotlinMoveTarget
-import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsOptions
-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.idea.refactoring.move.*
+import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*
+import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetNamedDeclaration
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
public class MoveKotlinFileHandler : MoveFileHandler() {
- private class InternalUsagesWrapper(file: JetFile, val usages: List) : UsageInfo(file)
+ internal class InternalUsagesWrapper(file: JetFile, val usages: List) : UsageInfo(file)
// This is special 'PsiElement' whose purpose is to wrap MoveKotlinTopLevelDeclarationsProcessor
// so that it can be kept in the transition map
@@ -52,33 +46,42 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
override fun toString() = ""
}
- private fun JetFile.getPackageNameInfo(newParent: PsiDirectory, clearUserData: Boolean): PackageNameInfo? {
+ private fun JetFile.getPackageNameInfo(newParent: PsiDirectory?, clearUserData: Boolean): PackageNameInfo? {
val shouldUpdatePackageDirective = updatePackageDirective ?: packageMatchesDirectory()
updatePackageDirective = if (clearUserData) null else shouldUpdatePackageDirective
if (!shouldUpdatePackageDirective) return null
- val newPackage = newParent.getPackage() ?: return null
- val oldPackageName = getPackageFqName()
- val newPackageName = FqName(newPackage.getQualifiedName())
- if (oldPackageName == newPackageName) return null
+ val oldPackageName = packageFqName
+ val newPackage = newParent?.getPackage() ?: return PackageNameInfo(oldPackageName, UNKNOWN_PACKAGE_FQ_NAME)
+
+ val newPackageName = FqNameUnsafe(newPackage.qualifiedName)
+ if (oldPackageName.asString() == newPackageName.asString()) return null
return PackageNameInfo(oldPackageName, newPackageName)
}
- private fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory): MoveKotlinTopLevelDeclarationsProcessor? {
+ fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory?): MoveKotlinTopLevelDeclarationsProcessor? {
if (psiFile !is JetFile) return null
val packageNameInfo = psiFile.getPackageNameInfo(newParent, false) ?: return null
- val project = psiFile.getProject()
+ val project = psiFile.project
+
+ val newPackageName = packageNameInfo.newPackageName
+ val moveTarget = when (newPackageName) {
+ UNKNOWN_PACKAGE_FQ_NAME -> EmptyKotlinMoveTarget
+
+ else -> DeferredJetFileKotlinMoveTarget(project, newPackageName.toSafe()) {
+ MoveFilesOrDirectoriesUtil.doMoveFile(psiFile, newParent)
+ newParent?.findFile(psiFile.name) as? JetFile
+ }
+ }
+
val declarationMoveProcessor = MoveKotlinTopLevelDeclarationsProcessor(
project,
MoveKotlinTopLevelDeclarationsOptions(
- elementsToMove = psiFile.getDeclarations().filterIsInstance(),
- moveTarget = DeferredJetFileKotlinMoveTarget(project, packageNameInfo.newPackageName) {
- MoveFilesOrDirectoriesUtil.doMoveFile(psiFile, newParent)
- newParent.findFile(psiFile.getName()) as? JetFile
- },
+ elementsToMove = psiFile.declarations.filterIsInstance(),
+ moveTarget = moveTarget,
updateInternalReferences = false
),
Mover.Idle
@@ -91,9 +94,15 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
return !JavaProjectRootsUtil.isOutsideJavaSourceRoot(element)
}
+ fun findInternalUsages(file: JetFile, newParent: PsiDirectory): InternalUsagesWrapper {
+ val packageNameInfo = file.getPackageNameInfo(newParent, false)
+ val usages = packageNameInfo?.let { file.getInternalReferencesToUpdateOnPackageNameChange(it) } ?: emptyList()
+ return InternalUsagesWrapper(file, usages)
+ }
+
override fun findUsages(
psiFile: PsiFile,
- newParent: PsiDirectory,
+ newParent: PsiDirectory?,
searchInComments: Boolean,
searchInNonJavaFiles: Boolean
): List {
@@ -101,10 +110,7 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
val usages = ArrayList()
initMoveProcessor(psiFile, newParent)?.findUsages()?.let { usages += it }
- val packageNameInfo = psiFile.getPackageNameInfo(newParent, false)
- if (packageNameInfo != null) {
- usages += InternalUsagesWrapper(psiFile, psiFile.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo))
- }
+ newParent?.let { usages += findInternalUsages(psiFile, it) }
return usages
}
@@ -118,15 +124,20 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
if (file !is JetFile) return
val newDirectory = file.parent ?: return
val packageNameInfo = file.getPackageNameInfo(newDirectory, true) ?: return
- file.packageDirective?.fqName = packageNameInfo.newPackageName
+ val newPackageName = packageNameInfo.newPackageName
+ assert(newPackageName.isSafe, newPackageName)
+ file.packageDirective?.fqName = newPackageName.toSafe()
}
override fun retargetUsages(usageInfos: List?, oldToNewMap: Map) {
- postProcessMoveUsages(usageInfos?.firstIsInstanceOrNull()?.usages ?: emptyList())
val moveContext = oldToNewMap.keySet().firstIsInstanceOrNull() ?: return
- val processor = moveContext.declarationMoveProcessor
- processor.project.runWithElementsToShortenIsEmptyIgnored {
- usageInfos?.let { processor.execute(it) }
+ retargetUsages(usageInfos, moveContext.declarationMoveProcessor)
+ }
+
+ fun retargetUsages(usageInfos: List?, moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor) {
+ postProcessMoveUsages(usageInfos?.firstIsInstanceOrNull()?.usages ?: emptyList())
+ moveDeclarationsProcessor.project.runWithElementsToShortenIsEmptyIgnored {
+ usageInfos?.let { moveDeclarationsProcessor.execute(it) }
}
}
}
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt
index 6b86afe3f07..91d9f9148f5 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt
@@ -23,7 +23,7 @@ import com.intellij.psi.PsiManager
import com.intellij.refactoring.PackageWrapper
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.JetFile
-import java.util.*
+import java.util.HashMap
public interface KotlinMoveTarget {
val packageWrapper: PackageWrapper?
@@ -34,6 +34,13 @@ public interface KotlinMoveTarget {
fun verify(file: PsiFile): String?
}
+public object EmptyKotlinMoveTarget: KotlinMoveTarget {
+ override val packageWrapper: PackageWrapper? get() = null
+ override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null
+ override fun getTargetPsiIfExists(originalPsi: PsiElement) = null
+ override fun verify(file: PsiFile) = null
+}
+
public class JetFileKotlinMoveTarget(val targetFile: JetFile): KotlinMoveTarget {
override val packageWrapper: PackageWrapper? = targetFile.getPackageFqName().asString().let { packageName ->
PackageWrapper(PsiManager.getInstance(targetFile.getProject()), packageName)
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt
index af127ff00f2..0e32fc802f7 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt
@@ -184,6 +184,8 @@ public class MoveKotlinTopLevelDeclarationsProcessor(
}
fun collectConflictsInDeclarations() {
+ if (newPackageName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return
+
val declarationToReferenceTargets = HashMap>()
for (declaration in elementsToMove) {
val referenceToContext = JetFileReferencesResolver.resolve(element = declaration, resolveQualifiers = false)
@@ -251,7 +253,7 @@ public class MoveKotlinTopLevelDeclarationsProcessor(
?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.getText()}")
if (options.updateInternalReferences) {
- val packageNameInfo = PackageNameInfo(file!!.getPackageFqName(), targetFile.getPackageFqName())
+ val packageNameInfo = PackageNameInfo(file!!.getPackageFqName(), targetFile.getPackageFqName().toUnsafe())
val (usagesToProcessLater, usagesToProcessNow) = declaration
.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
.partition { it is MoveRenameUsageInfoForExtension }
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 989192a5554..ed22e10c1db 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt
@@ -56,6 +56,7 @@ 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.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -65,7 +66,9 @@ import java.util.ArrayList
import java.util.Collections
import java.util.Comparator
-public class PackageNameInfo(val oldPackageName: FqName, val newPackageName: FqName)
+val UNKNOWN_PACKAGE_FQ_NAME = FqNameUnsafe("org.jetbrains.kotlin.idea.refactoring.move.")
+
+public class PackageNameInfo(val oldPackageName: FqName, val newPackageName: FqNameUnsafe)
public fun JetElement.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo: PackageNameInfo): List {
val file = getContainingFile() as? JetFile ?: return listOf()
@@ -113,7 +116,7 @@ public fun JetElement.getInternalReferencesToUpdateOnPackageNameChange(packageNa
return when {
isExtension,
packageName == packageNameInfo.oldPackageName,
- packageName == packageNameInfo.newPackageName,
+ packageName?.asString() == packageNameInfo.newPackageName.asString(),
isImported(descriptor) -> {
createMoveUsageInfoIfPossible(refExpr.mainReference, declaration, false)
}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/after/p1/M.kt b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p1/M.kt
new file mode 100644
index 00000000000..3c69dd88067
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p1/M.kt
@@ -0,0 +1,9 @@
+package p1
+
+import p2.p.J
+import p2.p.K
+
+public class M {
+ val j = J()
+ val k = K()
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/JJ.java b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/JJ.java
new file mode 100644
index 00000000000..03f5a790e2e
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/JJ.java
@@ -0,0 +1,9 @@
+package p2;
+
+import p2.p.K;
+import p1.M;
+
+public class JJ {
+ K k = new K();
+ M m = new M();
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/L.kt b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/L.kt
new file mode 100644
index 00000000000..98811e1cb51
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/L.kt
@@ -0,0 +1,9 @@
+package p2
+
+import p1.M
+import p2.p.K
+
+public class L {
+ private val k = K()
+ private val m = M()
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/p/J.java b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/p/J.java
new file mode 100644
index 00000000000..95a08234eeb
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/p/J.java
@@ -0,0 +1,5 @@
+package p2.p;
+
+public class J {
+ K k = new K();
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/p/K.kt b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/p/K.kt
new file mode 100644
index 00000000000..e54063ab680
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/after/p2/p/K.kt
@@ -0,0 +1,5 @@
+package p2.p
+
+public class K {
+ val j = J()
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/M.kt b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/M.kt
new file mode 100644
index 00000000000..66a8664626d
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/M.kt
@@ -0,0 +1,9 @@
+package p1
+
+import p1.p.J
+import p1.p.K
+
+public class M {
+ val j = J()
+ val k = K()
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/p/J.java b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/p/J.java
new file mode 100644
index 00000000000..8560850b26b
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/p/J.java
@@ -0,0 +1,5 @@
+package p1.p;
+
+public class J {
+ K k = new K();
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/p/K.kt b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/p/K.kt
new file mode 100644
index 00000000000..a66e6c6a8e5
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p1/p/K.kt
@@ -0,0 +1,5 @@
+package p1.p
+
+public class K {
+ val j = J()
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/before/p2/JJ.java b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p2/JJ.java
new file mode 100644
index 00000000000..18ce461dd21
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p2/JJ.java
@@ -0,0 +1,9 @@
+package p2;
+
+import p1.p.K;
+import p1.M;
+
+public class JJ {
+ K k = new K();
+ M m = new M();
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/before/p2/L.kt b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p2/L.kt
new file mode 100644
index 00000000000..74bbe084f00
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/before/p2/L.kt
@@ -0,0 +1,9 @@
+package p2
+
+import p1.M
+import p1.p.K
+
+public class L {
+ private val k = K()
+ private val m = M()
+}
\ No newline at end of file
diff --git a/idea/testData/refactoring/move/kotlin/moveDirectory/moveDirectory.test b/idea/testData/refactoring/move/kotlin/moveDirectory/moveDirectory.test
new file mode 100644
index 00000000000..db0f92879ba
--- /dev/null
+++ b/idea/testData/refactoring/move/kotlin/moveDirectory/moveDirectory.test
@@ -0,0 +1,6 @@
+{
+ "mainFile": "p1/M.kt",
+ "type": "MOVE_DIRECTORY_WITH_CLASSES",
+ "sourceDir": "p1/p",
+ "targetDir": "p2"
+}
diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/fun.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/foo.kt
similarity index 100%
rename from idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/fun.kt
rename to idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveOnlyFunctionToPackageUsedInJava/after/newPackage/foo.kt
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt
index b4e9cca6a67..4628cdd8114 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractJetMoveTest.kt
@@ -30,6 +30,7 @@ import com.intellij.refactoring.PackageWrapper
import com.intellij.refactoring.move.MoveHandler
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassToInnerProcessor
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesProcessor
+import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesProcessor
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor
import com.intellij.refactoring.move.moveInner.MoveInnerProcessor
@@ -37,6 +38,7 @@ import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions
import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor
import com.intellij.util.ActionRunner
import org.jetbrains.kotlin.idea.core.refactoring.createKotlinFile
+import org.jetbrains.kotlin.idea.core.refactoring.toPsiDirectory
import org.jetbrains.kotlin.idea.jsonUtils.getNullableString
import org.jetbrains.kotlin.idea.jsonUtils.getString
import org.jetbrains.kotlin.idea.refactoring.move.changePackage.KotlinChangePackageRefactoring
@@ -264,6 +266,15 @@ enum class MoveAction {
override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementAtCaret: PsiElement?, config: JsonObject) {
KotlinChangePackageRefactoring(mainFile as JetFile).run(FqName(config.getString("newPackageName")))
}
+ },
+
+ MOVE_DIRECTORY_WITH_CLASSES {
+ override fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementAtCaret: PsiElement?, config: JsonObject) {
+ val project = mainFile.project
+ val sourceDir = rootDir.findFileByRelativePath(config.getString("sourceDir"))!!.toPsiDirectory(project)!!
+ val targetDir = rootDir.findFileByRelativePath(config.getString("targetDir"))!!.toPsiDirectory(project)!!
+ MoveDirectoryWithClassesProcessor(project, arrayOf(sourceDir), targetDir, true, true, true, {}).run()
+ }
};
abstract fun runRefactoring(rootDir: VirtualFile, mainFile: PsiFile, elementAtCaret: PsiElement?, config: JsonObject)
diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/JetMoveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/JetMoveTestGenerated.java
index fd95c3901cd..14e005c3043 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/JetMoveTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/JetMoveTestGenerated.java
@@ -227,6 +227,12 @@ public class JetMoveTestGenerated extends AbstractJetMoveTest {
doTest(fileName);
}
+ @TestMetadata("kotlin/moveDirectory/moveDirectory.test")
+ public void testKotlin_moveDirectory_MoveDirectory() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveDirectory/moveDirectory.test");
+ doTest(fileName);
+ }
+
@TestMetadata("kotlin/moveFile/addExtensionImport/addExtensionImport.test")
public void testKotlin_moveFile_addExtensionImport_AddExtensionImport() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveFile/addExtensionImport/addExtensionImport.test");