@@ -22,25 +22,33 @@ import com.intellij.openapi.project.Project
22
22
import com.intellij.openapi.roots.ProjectRootManager
23
23
import com.intellij.openapi.ui.Messages
24
24
import com.intellij.openapi.util.Key
25
+ import com.intellij.openapi.vfs.VirtualFile
25
26
import com.intellij.psi.PsiDirectory
26
27
import com.intellij.psi.PsiElement
27
28
import com.intellij.psi.PsiFile
28
29
import com.intellij.psi.PsiNamedElement
30
+ import com.intellij.refactoring.BaseRefactoringProcessor
29
31
import com.intellij.refactoring.RefactoringBundle
30
32
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
31
33
import com.intellij.refactoring.copy.CopyHandlerDelegateBase
32
34
import com.intellij.refactoring.rename.RenameProcessor
33
35
import com.intellij.refactoring.util.MoveRenameUsageInfo
34
36
import com.intellij.usageView.UsageInfo
35
37
import com.intellij.util.IncorrectOperationException
38
+ import com.intellij.util.containers.MultiMap
36
39
import org.jetbrains.annotations.TestOnly
37
40
import org.jetbrains.kotlin.idea.codeInsight.shorten.performDelayedRefactoringRequests
38
41
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
42
+ import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
39
43
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
40
44
import org.jetbrains.kotlin.idea.refactoring.move.*
45
+ import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinDirectoryMoveTarget
46
+ import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveConflictChecker
47
+ import org.jetbrains.kotlin.idea.refactoring.toPsiDirectory
41
48
import org.jetbrains.kotlin.idea.util.application.executeCommand
42
49
import org.jetbrains.kotlin.idea.util.application.runReadAction
43
50
import org.jetbrains.kotlin.idea.util.application.runWriteAction
51
+ import org.jetbrains.kotlin.idea.util.sourceRoot
44
52
import org.jetbrains.kotlin.name.FqName
45
53
import org.jetbrains.kotlin.psi.KtElement
46
54
import org.jetbrains.kotlin.psi.KtFile
@@ -55,7 +63,7 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
55
63
@set:TestOnly
56
64
var Project .newName: String? by UserDataProperty (Key .create(" NEW_NAME" ))
57
65
58
- private fun PsiElement.getElementsToCopy (): List <PsiNamedElement > {
66
+ private fun PsiElement.getElementsToCopy (): List <KtElement > {
59
67
val declarationOrFile = parentsWithSelf.firstOrNull { it is KtFile || (it is KtNamedDeclaration && it.parent is KtFile ) }
60
68
return when (declarationOrFile) {
61
69
is KtFile -> declarationOrFile.declarations.filterIsInstance<KtNamedDeclaration >().ifEmpty { listOf (declarationOrFile) }
@@ -147,15 +155,16 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
147
155
148
156
val project = initialTargetDirectory.project
149
157
150
- if (ProjectRootManager .getInstance(project).fileIndex.getSourceRootForFile(initialTargetDirectory.virtualFile) == null ) return
151
-
152
158
val commandName = " Copy Declarations"
153
159
154
160
var openInEditor = false
155
161
var newName: String? = singleElementToCopy?.name ? : originalFile.name
156
162
var targetDirWrapper: AutocreatingPsiDirectoryWrapper = initialTargetDirectory.toDirectoryWrapper()
163
+ var targetSourceRoot: VirtualFile ? = initialTargetDirectory.sourceRoot ? : return
164
+
165
+ val isUnitTestMode = ApplicationManager .getApplication().isUnitTestMode
157
166
158
- if (! ApplicationManager .getApplication(). isUnitTestMode) {
167
+ if (! isUnitTestMode) {
159
168
if (singleElementToCopy != null && singleElementToCopy is KtNamedDeclaration ) {
160
169
val dialog = CopyKotlinDeclarationDialog (singleElementToCopy, initialTargetDirectory, project)
161
170
dialog.title = commandName
@@ -164,13 +173,15 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
164
173
openInEditor = dialog.openInEditor
165
174
newName = dialog.newName ? : singleElementToCopy.name
166
175
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ? : return
176
+ targetSourceRoot = dialog.targetSourceRoot
167
177
}
168
178
else {
169
179
val dialog = CopyFilesOrDirectoriesDialog (arrayOf(originalFile), initialTargetDirectory, project, false )
170
180
if (! dialog.showAndGet()) return
171
181
openInEditor = dialog.openInEditor()
172
182
newName = dialog.newName
173
183
targetDirWrapper = dialog.targetDirectory?.toDirectoryWrapper() ? : return
184
+ targetSourceRoot = dialog.targetDirectory?.sourceRoot
174
185
}
175
186
}
176
187
else {
@@ -185,60 +196,80 @@ class CopyKotlinDeclarationsHandler : CopyHandlerDelegateBase() {
185
196
ContainerInfo .Package (originalFile.packageFqName),
186
197
ContainerInfo .Package (FqName (targetPackageName))
187
198
)
188
- elementsToCopy.flatMap { elementToCopy ->
189
- ( elementToCopy as KtElement ) .getInternalReferencesToUpdateOnPackageNameChange(changeInfo).filter {
199
+ elementsToCopy.flatMapTo( LinkedHashSet ()) { elementToCopy ->
200
+ elementToCopy.getInternalReferencesToUpdateOnPackageNameChange(changeInfo).filter {
190
201
val referencedElement = (it as ? MoveRenameUsageInfo )?.referencedElement
191
202
referencedElement == null || ! elementToCopy.isAncestor(referencedElement)
192
203
}
193
204
}
194
205
}
195
206
markInternalUsages(internalUsages)
196
207
197
- val restoredInternalUsages = ArrayList <UsageInfo >()
208
+ fun doRefactor () {
209
+ val restoredInternalUsages = ArrayList <UsageInfo >()
198
210
199
- project.executeCommand(commandName) {
200
- try {
201
- val targetDirectory = runWriteAction { targetDirWrapper.getOrCreateDirectory(initialTargetDirectory) }
202
- val targetFileName = if (newName?.contains(" ." ) ? : false ) newName!! else newName + " ." + originalFile.virtualFile.extension
211
+ project.executeCommand(commandName) {
212
+ try {
213
+ val targetDirectory = runWriteAction { targetDirWrapper.getOrCreateDirectory(initialTargetDirectory) }
214
+ val targetFileName = if (newName?.contains(" ." ) ? : false ) newName!! else newName + " ." + originalFile.virtualFile.extension
203
215
204
- val oldToNewElementsMapping = HashMap <PsiElement , PsiElement >()
216
+ val oldToNewElementsMapping = HashMap <PsiElement , PsiElement >()
217
+
218
+ val targetFile: KtFile
219
+ if (singleElementToCopy is KtFile ) {
220
+ targetFile = runWriteAction { targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile }
221
+ }
222
+ else {
223
+ targetFile = getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ? : return @executeCommand
224
+ runWriteAction {
225
+ val newElements = elementsToCopy.map { targetFile.add(it.copy()) as KtNamedDeclaration }
226
+ elementsToCopy.zip(newElements).toMap(oldToNewElementsMapping)
227
+ }
228
+ }
205
229
206
- val targetFile: KtFile
207
- if (singleElementToCopy is KtFile ) {
208
- targetFile = runWriteAction { targetDirectory.copyFileFrom(targetFileName, singleElementToCopy) as KtFile }
209
- }
210
- else {
211
- targetFile = getOrCreateTargetFile(originalFile, targetDirectory, targetFileName, commandName) ? : return @executeCommand
212
230
runWriteAction {
213
- val newElements = elementsToCopy.map { targetFile.add(it.copy()) as KtNamedDeclaration }
214
- elementsToCopy.zip(newElements).toMap(oldToNewElementsMapping)
231
+ for (newElement in oldToNewElementsMapping.values) {
232
+ restoredInternalUsages + = restoreInternalUsages(newElement as KtElement , oldToNewElementsMapping, true )
233
+ postProcessMoveUsages(restoredInternalUsages, oldToNewElementsMapping)
234
+ }
235
+
236
+ performDelayedRefactoringRequests(project)
215
237
}
216
- }
217
238
218
- runWriteAction {
219
- for (newElement in oldToNewElementsMapping.values) {
220
- restoredInternalUsages + = restoreInternalUsages(newElement as KtElement , oldToNewElementsMapping, true )
221
- postProcessMoveUsages(restoredInternalUsages, oldToNewElementsMapping)
239
+ oldToNewElementsMapping.values.singleOrNull()?.let {
240
+ RenameProcessor (project, it, newName!! .quoteIfNeeded(), false , false ).run ()
222
241
}
223
242
224
- performDelayedRefactoringRequests(project)
243
+ if (openInEditor) {
244
+ EditorHelper .openFilesInEditor(arrayOf(targetFile))
245
+ }
225
246
}
226
-
227
- oldToNewElementsMapping.values.singleOrNull()?.let {
228
- RenameProcessor (project, it, newName!! .quoteIfNeeded(), false , false ).run ()
247
+ catch (e: IncorrectOperationException ) {
248
+ Messages .showMessageDialog(project, e.message, RefactoringBundle .message(" error.title" ), Messages .getErrorIcon())
229
249
}
230
-
231
- if (openInEditor) {
232
- EditorHelper .openFilesInEditor(arrayOf(targetFile))
250
+ finally {
251
+ cleanUpInternalUsages(internalUsages + restoredInternalUsages)
233
252
}
234
253
}
235
- catch (e: IncorrectOperationException ) {
236
- Messages .showMessageDialog(project, e.message, RefactoringBundle .message(" error.title" ), Messages .getErrorIcon())
237
- }
238
- finally {
239
- cleanUpInternalUsages(internalUsages + restoredInternalUsages)
254
+ }
255
+
256
+ val conflicts = MultiMap <PsiElement , String >()
257
+
258
+ if (! (isUnitTestMode && BaseRefactoringProcessor .ConflictsInTestsException .isTestIgnore())) {
259
+ val targetSourceRootPsi = targetSourceRoot?.toPsiDirectory(project)
260
+ if (targetSourceRootPsi != null ) {
261
+ val conflictChecker = MoveConflictChecker (
262
+ project,
263
+ elementsToCopy,
264
+ KotlinDirectoryMoveTarget (FqName .ROOT , targetSourceRootPsi),
265
+ originalFile
266
+ )
267
+ conflictChecker.checkModuleConflictsInDeclarations(internalUsages, conflicts)
268
+ conflictChecker.checkVisibilityInDeclarations(conflicts)
240
269
}
241
270
}
271
+
272
+ project.checkConflictsInteractively(conflicts, onAccept = ::doRefactor)
242
273
}
243
274
244
275
override fun doClone (element : PsiElement ) {
0 commit comments