Clean up move refactoring

This commit is contained in:
Dmitry Gridin
2019-07-02 20:38:35 +07:00
parent beba1d82fc
commit 1a9abaeb2a
23 changed files with 714 additions and 968 deletions
@@ -49,10 +49,10 @@ abstract class MoveMemberOutOfObjectIntention(text: String) : SelfTargetingRange
if (element is KtClassOrObject) { if (element is KtClassOrObject) {
val moveDescriptor = MoveDeclarationsDescriptor( val moveDescriptor = MoveDeclarationsDescriptor(
project, project,
MoveSource(element), MoveSource(element),
KotlinMoveTargetForExistingElement(destination), KotlinMoveTargetForExistingElement(destination),
MoveDeclarationsDelegate.NestedClass() MoveDeclarationsDelegate.NestedClass()
) )
object : CompositeRefactoringRunner(project, MoveKotlinDeclarationsProcessor.REFACTORING_ID) { object : CompositeRefactoringRunner(project, MoveKotlinDeclarationsProcessor.REFACTORING_ID) {
override fun runRefactoring() = MoveKotlinDeclarationsProcessor(moveDescriptor).run() override fun runRefactoring() = MoveKotlinDeclarationsProcessor(moveDescriptor).run()
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move
@@ -28,7 +17,7 @@ sealed class AutocreatingPsiDirectoryWrapper {
class ByMoveDestination(private val moveDestination: MoveDestination) : AutocreatingPsiDirectoryWrapper() { class ByMoveDestination(private val moveDestination: MoveDestination) : AutocreatingPsiDirectoryWrapper() {
override fun getPackageName() = moveDestination.targetPackage.qualifiedName override fun getPackageName() = moveDestination.targetPackage.qualifiedName
override fun getOrCreateDirectory(source: PsiDirectory) = moveDestination.getTargetDirectory(source) override fun getOrCreateDirectory(source: PsiDirectory): PsiDirectory = moveDestination.getTargetDirectory(source)
} }
abstract fun getPackageName(): String abstract fun getPackageName(): String
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move
@@ -26,7 +15,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtValueArgumentList import org.jetbrains.kotlin.psi.KtValueArgumentList
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
class MoveJavaInnerClassKotlinUsagesHandler: MoveInnerClassUsagesHandler { class MoveJavaInnerClassKotlinUsagesHandler : MoveInnerClassUsagesHandler {
override fun correctInnerClassUsage(usage: UsageInfo, outerClass: PsiClass) { override fun correctInnerClassUsage(usage: UsageInfo, outerClass: PsiClass) {
val innerCall = usage.element?.parent as? KtCallExpression ?: return val innerCall = usage.element?.parent as? KtCallExpression ?: return
val receiverExpression = innerCall.getQualifiedExpressionForSelector()?.receiverExpression val receiverExpression = innerCall.getQualifiedExpressionForSelector()?.receiverExpression
@@ -35,13 +24,12 @@ class MoveJavaInnerClassKotlinUsagesHandler: MoveInnerClassUsagesHandler {
val argumentToAdd = psiFactory.createArgument(receiverExpression ?: psiFactory.createExpression("this")) val argumentToAdd = psiFactory.createArgument(receiverExpression ?: psiFactory.createExpression("this"))
val argumentList = val argumentList = innerCall.valueArgumentList
innerCall.valueArgumentList ?: (innerCall.lambdaArguments.firstOrNull()?.let { lambdaArg ->
?: (innerCall.lambdaArguments.firstOrNull()?.let { lambdaArg -> val anchor = PsiTreeUtil.skipSiblingsBackward(lambdaArg, PsiWhiteSpace::class.java)
val anchor = PsiTreeUtil.skipSiblingsBackward(lambdaArg, PsiWhiteSpace::class.java) innerCall.addAfter(psiFactory.createCallArguments("()"), anchor)
innerCall.addAfter(psiFactory.createCallArguments("()"), anchor) } as KtValueArgumentList?)
} as KtValueArgumentList?) ?: return
?: return
argumentList.addArgumentAfter(argumentToAdd, null) argumentList.addArgumentAfter(argumentToAdd, null)
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveClassesOrPackages package org.jetbrains.kotlin.idea.refactoring.move.moveClassesOrPackages
@@ -24,9 +13,9 @@ import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesToNewDirectoryDialog import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesToNewDirectoryDialog
class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog( class KotlinAwareMoveClassesOrPackagesToNewDirectoryDialog(
directory: PsiDirectory, directory: PsiDirectory,
elementsToMove: Array<out PsiElement>, elementsToMove: Array<out PsiElement>,
moveCallback: MoveCallback? moveCallback: MoveCallback?
) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) { ) : MoveClassesOrPackagesToNewDirectoryDialog(directory, elementsToMove, moveCallback) {
override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination? { override fun createDestination(aPackage: PsiPackage, directory: PsiDirectory): MoveDestination? {
val delegate = super.createDestination(aPackage, directory) ?: return null val delegate = super.createDestination(aPackage, directory) ?: return null
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -45,8 +34,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
private const val TIMEOUT_FOR_IMPORT_OPTIMIZING_MS: Long = 700L private const val TIMEOUT_FOR_IMPORT_OPTIMIZING_MS: Long = 700L
class ExtractDeclarationFromCurrentFileIntention : class ExtractDeclarationFromCurrentFileIntention :
SelfTargetingRangeIntention<KtClassOrObject>(KtClassOrObject::class.java, "Extract declaration from current file"), SelfTargetingRangeIntention<KtClassOrObject>(KtClassOrObject::class.java, "Extract declaration from current file"),
LowPriorityAction { LowPriorityAction {
override fun applicabilityRange(element: KtClassOrObject): TextRange? { override fun applicabilityRange(element: KtClassOrObject): TextRange? {
if (element.name == null) return null if (element.name == null) return null
if (element.parent !is KtFile) return null if (element.parent !is KtFile) return null
@@ -89,17 +78,24 @@ class ExtractDeclarationFromCurrentFileIntention :
// If automatic move is not possible, fall back to full-fledged Move Declarations refactoring // If automatic move is not possible, fall back to full-fledged Move Declarations refactoring
ApplicationManager.getApplication().invokeLater { ApplicationManager.getApplication().invokeLater {
MoveKotlinTopLevelDeclarationsDialog( MoveKotlinTopLevelDeclarationsDialog(
project, project,
setOf(element), setOf(element),
packageName.asString(), packageName.asString(),
directory, directory,
targetFile as? KtFile, targetFile as? KtFile,
true, true,
true, true,
true, true,
MoveCallback { MoveCallback {
runBlocking { withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) { OptimizeImportsProcessor(project, file).run() } } runBlocking {
withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) {
OptimizeImportsProcessor(
project,
file
).run()
}
} }
}
).show() ).show()
} }
return return
@@ -108,19 +104,19 @@ class ExtractDeclarationFromCurrentFileIntention :
createKotlinFile(targetFileName, directory, packageName.asString()) createKotlinFile(targetFileName, directory, packageName.asString())
} }
val descriptor = MoveDeclarationsDescriptor( val descriptor = MoveDeclarationsDescriptor(
project = project, project = project,
moveSource = MoveSource(element), moveSource = MoveSource(element),
moveTarget = moveTarget, moveTarget = moveTarget,
delegate = MoveDeclarationsDelegate.TopLevel, delegate = MoveDeclarationsDelegate.TopLevel,
searchInCommentsAndStrings = false, searchInCommentsAndStrings = false,
searchInNonCode = false, searchInNonCode = false,
moveCallback = MoveCallback { moveCallback = MoveCallback {
val newFile = directory.findFile(targetFileName) as KtFile val newFile = directory.findFile(targetFileName) as KtFile
val newDeclaration = newFile.declarations.first() val newDeclaration = newFile.declarations.first()
NavigationUtil.activateFileWithPsiElement(newFile) NavigationUtil.activateFileWithPsiElement(newFile)
FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset) FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset)
runBlocking { withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) { OptimizeImportsProcessor(project, file).run() } } runBlocking { withTimeoutOrNull(TIMEOUT_FOR_IMPORT_OPTIMIZING_MS) { OptimizeImportsProcessor(project, file).run() } }
} }
) )
MoveKotlinDeclarationsProcessor(descriptor).run() MoveKotlinDeclarationsProcessor(descriptor).run()
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -32,49 +21,40 @@ import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.allElem
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.shouldFixFqName import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.shouldFixFqName
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
class KotlinAwareMoveFilesOrDirectoriesProcessor @JvmOverloads constructor ( class KotlinAwareMoveFilesOrDirectoriesProcessor @JvmOverloads constructor(
project: Project, project: Project,
private val elementsToMove: List<PsiElement>, private val elementsToMove: List<PsiElement>,
private val targetDirectory: PsiDirectory, private val targetDirectory: PsiDirectory,
private val searchReferences: Boolean, private val searchReferences: Boolean,
searchInComments: Boolean, searchInComments: Boolean,
searchInNonJavaFiles: Boolean, searchInNonJavaFiles: Boolean,
moveCallback: MoveCallback?, moveCallback: MoveCallback?,
prepareSuccessfulCallback: Runnable = EmptyRunnable.INSTANCE prepareSuccessfulCallback: Runnable = EmptyRunnable.INSTANCE
) : MoveFilesOrDirectoriesProcessor(project, ) : MoveFilesOrDirectoriesProcessor(
elementsToMove.toTypedArray<PsiElement>(), project,
targetDirectory, elementsToMove.toTypedArray(),
true, targetDirectory,
searchInComments, true,
searchInNonJavaFiles, searchInComments,
moveCallback, searchInNonJavaFiles,
prepareSuccessfulCallback) { moveCallback,
prepareSuccessfulCallback
) {
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor { override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor {
return MoveFilesWithDeclarationsViewDescriptor(elementsToMove.toTypedArray<PsiElement>(), targetDirectory) return MoveFilesWithDeclarationsViewDescriptor(elementsToMove.toTypedArray(), targetDirectory)
} }
override fun findUsages(): Array<UsageInfo> { override fun findUsages(): Array<UsageInfo> {
try { try {
markScopeToMove(elementsToMove) markScopeToMove(elementsToMove)
return if (searchReferences) super.findUsages() else UsageInfo.EMPTY_ARRAY return if (searchReferences) super.findUsages() else UsageInfo.EMPTY_ARRAY
} } finally {
finally {
markScopeToMove(null) markScopeToMove(null)
} }
} }
override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean { override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean {
val usages = refUsages.get() val (conflicts, usages) = MoveToKotlinFileProcessor.preprocessConflictUsages(refUsages)
val (conflictUsages, usagesToProcess) = usages.partition { it is ConflictUsageInfo }
val conflicts = MultiMap<PsiElement, String>()
for (conflictUsage in conflictUsages) {
conflicts.putValues(conflictUsage.element, (conflictUsage as ConflictUsageInfo).messages)
}
refUsages.set(usagesToProcess.toTypedArray())
return showConflicts(conflicts, usages) return showConflicts(conflicts, usages)
} }
@@ -101,8 +81,7 @@ class KotlinAwareMoveFilesOrDirectoriesProcessor @JvmOverloads constructor (
try { try {
markShouldFixFqName(true) markShouldFixFqName(true)
super.performRefactoring(usages) super.performRefactoring(usages)
} } finally {
finally {
markShouldFixFqName(false) markShouldFixFqName(false)
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -52,16 +41,16 @@ interface KotlinDirectoryBasedMoveTarget : KotlinMoveTarget {
get() = super.targetScope ?: directory?.virtualFile get() = super.targetScope ?: directory?.virtualFile
} }
object EmptyKotlinMoveTarget: KotlinMoveTarget { object EmptyKotlinMoveTarget : KotlinMoveTarget {
override val targetContainerFqName = null override val targetContainerFqName: FqName? = null
override val targetFile = null override val targetFile: VirtualFile? = null
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? = null
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null
override fun verify(file: PsiFile) = null override fun verify(file: PsiFile): String? = null
} }
class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMoveTarget { class KotlinMoveTargetForExistingElement(val targetElement: KtElement) : KotlinMoveTarget {
override val targetContainerFqName = targetElement.containingKtFile.packageFqName override val targetContainerFqName = targetElement.containingKtFile.packageFqName
override val targetFile: VirtualFile? = targetElement.containingKtFile.virtualFile override val targetFile: VirtualFile? = targetElement.containingKtFile.virtualFile
@@ -74,9 +63,9 @@ class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMo
override fun verify(file: PsiFile): String? = null override fun verify(file: PsiFile): String? = null
} }
class KotlinMoveTargetForCompanion(val targetClass: KtClass): KotlinMoveTarget { class KotlinMoveTargetForCompanion(val targetClass: KtClass) : KotlinMoveTarget {
override val targetContainerFqName = targetClass.companionObjects.firstOrNull()?.fqName override val targetContainerFqName = targetClass.companionObjects.firstOrNull()?.fqName
?: targetClass.fqName!!.child(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) ?: targetClass.fqName!!.child(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT)
override val targetFile: VirtualFile? = targetClass.containingKtFile.virtualFile override val targetFile: VirtualFile? = targetClass.containingKtFile.virtualFile
@@ -89,11 +78,11 @@ class KotlinMoveTargetForCompanion(val targetClass: KtClass): KotlinMoveTarget {
} }
class KotlinMoveTargetForDeferredFile( class KotlinMoveTargetForDeferredFile(
override val targetContainerFqName: FqName, override val targetContainerFqName: FqName,
override val directory: PsiDirectory?, override val directory: PsiDirectory?,
override val targetFile: VirtualFile? = directory?.virtualFile, override val targetFile: VirtualFile? = directory?.virtualFile,
private val createFile: (KtFile) -> KtFile? private val createFile: (KtFile) -> KtFile?
): KotlinDirectoryBasedMoveTarget { ) : KotlinDirectoryBasedMoveTarget {
private val createdFiles = HashMap<KtFile, KtFile?>() private val createdFiles = HashMap<KtFile, KtFile?>()
override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? { override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? {
@@ -101,23 +90,23 @@ class KotlinMoveTargetForDeferredFile(
return createdFiles.getOrPutNullable(originalFile) { createFile(originalFile) } return createdFiles.getOrPutNullable(originalFile) { createFile(originalFile) }
} }
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null
// No additional verification is needed // No additional verification is needed
override fun verify(file: PsiFile): String? = null override fun verify(file: PsiFile): String? = null
} }
class KotlinDirectoryMoveTarget( class KotlinDirectoryMoveTarget(
override val targetContainerFqName: FqName, override val targetContainerFqName: FqName,
override val directory: PsiDirectory override val directory: PsiDirectory
) : KotlinDirectoryBasedMoveTarget { ) : KotlinDirectoryBasedMoveTarget {
override val targetFile: VirtualFile? = directory.virtualFile override val targetFile: VirtualFile? = directory.virtualFile
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile as? KtFile override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile as? KtFile
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null override fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? = null
override fun verify(file: PsiFile) = null override fun verify(file: PsiFile): String? = null
} }
fun KotlinMoveTarget.getTargetModule(project: Project) = targetScope?.getModule(project) fun KotlinMoveTarget.getTargetModule(project: Project) = targetScope?.getModule(project)
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -39,9 +28,9 @@ sealed class MoveDeclarationsDelegate {
open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList() open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
open fun collectConflicts( open fun collectConflicts(
descriptor: MoveDeclarationsDescriptor, descriptor: MoveDeclarationsDescriptor,
internalUsages: MutableSet<UsageInfo>, internalUsages: MutableSet<UsageInfo>,
conflicts: MultiMap<PsiElement, String> conflicts: MultiMap<PsiElement, String>
) { ) {
} }
@@ -63,8 +52,8 @@ sealed class MoveDeclarationsDelegate {
} }
class NestedClass( class NestedClass(
val newClassName: String? = null, val newClassName: String? = null,
val outerInstanceParameterName: String? = null val outerInstanceParameterName: String? = null
) : MoveDeclarationsDelegate() { ) : MoveDeclarationsDelegate() {
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo { override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!) val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!)
@@ -84,11 +73,10 @@ sealed class MoveDeclarationsDelegate {
} }
private fun isValidTargetForImplicitCompanionAsDispatchReceiver( private fun isValidTargetForImplicitCompanionAsDispatchReceiver(
moveDescriptor: MoveDeclarationsDescriptor, moveDescriptor: MoveDeclarationsDescriptor,
companionDescriptor: ClassDescriptor companionDescriptor: ClassDescriptor
): Boolean { ): Boolean {
val moveTarget = moveDescriptor.moveTarget return when (val moveTarget = moveDescriptor.moveTarget) {
return when (moveTarget) {
is KotlinMoveTargetForCompanion -> true is KotlinMoveTargetForCompanion -> true
is KotlinMoveTargetForExistingElement -> { is KotlinMoveTargetForExistingElement -> {
val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false
@@ -101,9 +89,9 @@ sealed class MoveDeclarationsDelegate {
} }
override fun collectConflicts( override fun collectConflicts(
descriptor: MoveDeclarationsDescriptor, descriptor: MoveDeclarationsDescriptor,
internalUsages: MutableSet<UsageInfo>, internalUsages: MutableSet<UsageInfo>,
conflicts: MultiMap<PsiElement, String> conflicts: MultiMap<PsiElement, String>
) { ) {
val usageIterator = internalUsages.iterator() val usageIterator = internalUsages.iterator()
while (usageIterator.hasNext()) { while (usageIterator.hasNext()) {
@@ -141,7 +129,7 @@ sealed class MoveDeclarationsDelegate {
if (outerInstanceParameterName != null) { if (outerInstanceParameterName != null) {
val type = (containingClassOrObject!!.unsafeResolveToDescriptor() as ClassDescriptor).defaultType val type = (containingClassOrObject!!.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
val parameter = KtPsiFactory(project) val parameter = KtPsiFactory(project)
.createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}") .createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}")
createPrimaryConstructorParameterListIfAbsent().addParameter(parameter).isToBeShortened = true createPrimaryConstructorParameterListIfAbsent().addParameter(parameter).isToBeShortened = true
} }
} }
@@ -162,8 +150,8 @@ sealed class MoveDeclarationsDelegate {
val lightOuterClass = outerClass?.toLightClass() val lightOuterClass = outerClass?.toLightClass()
if (lightOuterClass != null) { if (lightOuterClass != null) {
MoveInnerClassUsagesHandler.EP_NAME MoveInnerClassUsagesHandler.EP_NAME
.forLanguage(usage.element!!.language) .forLanguage(usage.element!!.language)
?.correctInnerClassUsage(usage, lightOuterClass, outerInstanceParameterName) ?.correctInnerClassUsage(usage, lightOuterClass, outerInstanceParameterName)
} }
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -25,21 +14,24 @@ import com.intellij.usageView.UsageViewDescriptor
import com.intellij.usageView.UsageViewUtil import com.intellij.usageView.UsageViewUtil
internal class MoveFilesWithDeclarationsViewDescriptor( internal class MoveFilesWithDeclarationsViewDescriptor(
private val myElementsToMove: Array<PsiElement>, private val myElementsToMove: Array<PsiElement>,
newParent: PsiDirectory newParent: PsiDirectory
) : UsageViewDescriptor { ) : UsageViewDescriptor {
private var myProcessedElementsHeader: String? = null private var myProcessedElementsHeader: String? = null
private val myCodeReferencesText: String private val myCodeReferencesText: String
init { init {
if (myElementsToMove.size == 1) { if (myElementsToMove.size == 1) {
myProcessedElementsHeader = RefactoringBundle.message("move.single.element.elements.header", myProcessedElementsHeader = RefactoringBundle.message(
UsageViewUtil.getType(myElementsToMove[0]), "move.single.element.elements.header",
newParent.virtualFile.presentableUrl).capitalize() UsageViewUtil.getType(myElementsToMove[0]),
myCodeReferencesText = "References in code to ${UsageViewUtil.getType(myElementsToMove[0])} ${UsageViewUtil.getLongName(myElementsToMove[0])} and its declarations" newParent.virtualFile.presentableUrl
} ).capitalize()
else { myCodeReferencesText =
myProcessedElementsHeader = StringUtil.capitalize(RefactoringBundle.message("move.files.elements.header", newParent.virtualFile.presentableUrl)) "References in code to ${UsageViewUtil.getType(myElementsToMove[0])} ${UsageViewUtil.getLongName(myElementsToMove[0])} and its declarations"
} else {
myProcessedElementsHeader =
StringUtil.capitalize(RefactoringBundle.message("move.files.elements.header", newParent.virtualFile.presentableUrl))
myCodeReferencesText = RefactoringBundle.message("references.found.in.code") myCodeReferencesText = RefactoringBundle.message("references.found.in.code")
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -153,10 +142,13 @@ class MoveKotlinDeclarationsProcessor(
private var nonCodeUsages: Array<NonCodeUsageInfo>? = null private var nonCodeUsages: Array<NonCodeUsageInfo>? = null
private val moveEntireFile = descriptor.moveSource is MoveSource.File private val moveEntireFile = descriptor.moveSource is MoveSource.File
private val elementsToMove = descriptor.moveSource.elementsToMove.filter { e -> e.parent != descriptor.moveTarget.getTargetPsiIfExists(e) } private val elementsToMove = descriptor.moveSource.elementsToMove.filter { e ->
e.parent != descriptor.moveTarget.getTargetPsiIfExists(e)
}
private val kotlinToLightElementsBySourceFile = elementsToMove private val kotlinToLightElementsBySourceFile = elementsToMove
.groupBy { it.containingKtFile } .groupBy { it.containingKtFile }
.mapValues { it.value.keysToMap { it.toLightElements().ifEmpty { listOf(it) } } } .mapValues { it.value.keysToMap { declaration -> declaration.toLightElements().ifEmpty { listOf(declaration) } } }
private val conflicts = MultiMap<PsiElement, String>() private val conflicts = MultiMap<PsiElement, String>()
override fun getRefactoringId() = REFACTORING_ID override fun getRefactoringId() = REFACTORING_ID
@@ -209,10 +201,9 @@ class MoveKotlinDeclarationsProcessor(
val results = ReferencesSearch val results = ReferencesSearch
.search(lightElement, searchScope) .search(lightElement, searchScope)
.mapNotNullTo(ArrayList()) { ref -> .mapNotNullTo(ArrayList()) { ref ->
if (foundReferences.add(ref) && elementsToMove.none { it.isAncestor(ref.element)}) { if (foundReferences.add(ref) && elementsToMove.none { it.isAncestor(ref.element) }) {
createMoveUsageInfoIfPossible(ref, lightElement, addImportToOriginalFile = true, isInternal = false) createMoveUsageInfoIfPossible(ref, lightElement, addImportToOriginalFile = true, isInternal = false)
} } else null
else null
} }
val name = lightElement.getKotlinFqName()?.quoteIfNeeded()?.asString() val name = lightElement.getKotlinFqName()?.quoteIfNeeded()?.asString()
@@ -29,23 +29,25 @@ import com.intellij.util.containers.MultiMap
import com.intellij.util.text.UniqueNameGenerator import com.intellij.util.text.UniqueNameGenerator
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
class MoveToKotlinFileProcessor @JvmOverloads constructor ( class MoveToKotlinFileProcessor @JvmOverloads constructor(
project: Project, project: Project,
private val sourceFile: KtFile, private val sourceFile: KtFile,
private val targetDirectory: PsiDirectory, private val targetDirectory: PsiDirectory,
private val targetFileName: String, private val targetFileName: String,
searchInComments: Boolean, searchInComments: Boolean,
searchInNonJavaFiles: Boolean, searchInNonJavaFiles: Boolean,
moveCallback: MoveCallback?, moveCallback: MoveCallback?,
prepareSuccessfulCallback: Runnable = EmptyRunnable.INSTANCE prepareSuccessfulCallback: Runnable = EmptyRunnable.INSTANCE
) : MoveFilesOrDirectoriesProcessor(project, ) : MoveFilesOrDirectoriesProcessor(
arrayOf(sourceFile), project,
targetDirectory, arrayOf(sourceFile),
true, targetDirectory,
searchInComments, true,
searchInNonJavaFiles, searchInComments,
moveCallback, searchInNonJavaFiles,
prepareSuccessfulCallback) { moveCallback,
prepareSuccessfulCallback
) {
override fun getCommandName() = "Move ${sourceFile.name}" override fun getCommandName() = "Move ${sourceFile.name}"
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor { override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor {
@@ -53,17 +55,7 @@ class MoveToKotlinFileProcessor @JvmOverloads constructor (
} }
override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean { override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean {
val usages = refUsages.get() val (conflicts, usages) = preprocessConflictUsages(refUsages)
val (conflictUsages, usagesToProcess) = usages.partition { it is ConflictUsageInfo }
val conflicts = MultiMap<PsiElement, String>()
for (conflictUsage in conflictUsages) {
conflicts.putValues(conflictUsage.element, (conflictUsage as ConflictUsageInfo).messages)
}
refUsages.set(usagesToProcess.toTypedArray())
return showConflicts(conflicts, usages) return showConflicts(conflicts, usages)
} }
@@ -85,11 +77,29 @@ class MoveToKotlinFileProcessor @JvmOverloads constructor (
try { try {
super.performRefactoring(usages) super.performRefactoring(usages)
} } finally {
finally {
if (needTemporaryRename) { if (needTemporaryRename) {
sourceFile.name = targetFileName sourceFile.name = targetFileName
} }
} }
} }
companion object {
data class ConflictUsages(val conflicts: MultiMap<PsiElement, String>, @Suppress("ArrayInDataClass") val usages: Array<UsageInfo>)
fun preprocessConflictUsages(refUsages: Ref<Array<UsageInfo>>): ConflictUsages {
val usages: Array<UsageInfo> = refUsages.get()
val (conflictUsages, usagesToProcess) = usages.partition { it is ConflictUsageInfo }
val conflicts = MultiMap<PsiElement, String>()
for (conflictUsage in conflictUsages) {
conflicts.putValues(conflictUsage.element, (conflictUsage as ConflictUsageInfo).messages)
}
refUsages.set(usagesToProcess.toTypedArray())
return ConflictUsages(conflicts, usages)
}
}
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
@@ -62,6 +51,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
@@ -73,7 +63,6 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile import org.jetbrains.kotlin.resolve.lazy.descriptors.findPackageFragmentForFile
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
@@ -115,22 +104,19 @@ class MoveConflictChecker(
private fun KotlinMoveTarget.getContainerDescriptor(): DeclarationDescriptor? { private fun KotlinMoveTarget.getContainerDescriptor(): DeclarationDescriptor? {
return when (this) { return when (this) {
is KotlinMoveTargetForExistingElement -> { is KotlinMoveTargetForExistingElement -> when (val targetElement = targetElement) {
val targetElement = targetElement is KtNamedDeclaration -> resolutionFacade.resolveToDescriptor(targetElement)
when (targetElement) {
is KtNamedDeclaration -> resolutionFacade.resolveToDescriptor(targetElement)
is KtFile -> { is KtFile -> {
val packageFragment = val packageFragment =
targetElement targetElement
.findModuleDescriptor() .findModuleDescriptor()
.findPackageFragmentForFile(targetElement) .findPackageFragmentForFile(targetElement)
packageFragment?.withSource(targetElement) packageFragment?.withSource(targetElement)
}
else -> null
} }
else -> null
} }
is KotlinDirectoryBasedMoveTarget -> { is KotlinDirectoryBasedMoveTarget -> {
@@ -159,8 +145,7 @@ class MoveConflictChecker(
): DeclarationDescriptor? { ): DeclarationDescriptor? {
if (newContainer == null && newVisibility == null) return this if (newContainer == null && newVisibility == null) return this
val wrappedDescriptor = this return when (val wrappedDescriptor = this) {
return when (wrappedDescriptor) {
// We rely on visibility not depending on more specific type of CallableMemberDescriptor // We rely on visibility not depending on more specific type of CallableMemberDescriptor
is CallableMemberDescriptor -> object : CallableMemberDescriptor by wrappedDescriptor { is CallableMemberDescriptor -> object : CallableMemberDescriptor by wrappedDescriptor {
override fun getOriginal() = this override fun getOriginal() = this
@@ -255,7 +240,7 @@ class MoveConflictChecker(
val referencedElementsToSkip = newConflicts.keySet().mapNotNullTo(HashSet()) { it.namedUnwrappedElement } val referencedElementsToSkip = newConflicts.keySet().mapNotNullTo(HashSet()) { it.namedUnwrappedElement }
externalUsages.removeIf { externalUsages.removeIf {
it is MoveRenameUsageInfo && it is MoveRenameUsageInfo &&
it.referencedElement?.namedUnwrappedElement?.let { it in referencedElementsToSkip } ?: false it.referencedElement?.namedUnwrappedElement?.let { element -> element in referencedElementsToSkip } ?: false
} }
conflicts.putAllValues(newConflicts) conflicts.putAllValues(newConflicts)
} }
@@ -318,20 +303,20 @@ class MoveConflictChecker(
val renderedImportableTarget = DESCRIPTOR_RENDERER_FOR_COMPARISON.render(importableDescriptor) val renderedImportableTarget = DESCRIPTOR_RENDERER_FOR_COMPARISON.render(importableDescriptor)
val renderedTarget by lazy { DESCRIPTOR_RENDERER_FOR_COMPARISON.render(targetDescriptor) } val renderedTarget by lazy { DESCRIPTOR_RENDERER_FOR_COMPARISON.render(targetDescriptor) }
return newTargetDescriptors.any { return newTargetDescriptors.any { descriptor ->
if (DESCRIPTOR_RENDERER_FOR_COMPARISON.render(it) != renderedImportableTarget) return@any false if (DESCRIPTOR_RENDERER_FOR_COMPARISON.render(descriptor) != renderedImportableTarget) return@any false
if (importableDescriptor == targetDescriptor) return@any true if (importableDescriptor == targetDescriptor) return@any true
val candidateDescriptors: Collection<DeclarationDescriptor> = when (targetDescriptor) { val candidateDescriptors: Collection<DeclarationDescriptor> = when (targetDescriptor) {
is ConstructorDescriptor -> { is ConstructorDescriptor -> {
(it as? ClassDescriptor)?.constructors ?: emptyList<DeclarationDescriptor>() (descriptor as? ClassDescriptor)?.constructors ?: emptyList()
} }
is PropertyAccessorDescriptor -> { is PropertyAccessorDescriptor -> {
(it as? PropertyDescriptor) (descriptor as? PropertyDescriptor)
?.let { if (targetDescriptor is PropertyGetterDescriptor) it.getter else it.setter } ?.let { if (targetDescriptor is PropertyGetterDescriptor) it.getter else it.setter }
?.let { listOf(it) } ?.let { listOf(it) }
?: emptyList<DeclarationDescriptor>() ?: emptyList()
} }
else -> emptyList() else -> emptyList()
@@ -375,7 +360,7 @@ class MoveConflictChecker(
referencesToSkip += refExpr referencesToSkip += refExpr
} }
} }
internalUsages.removeIf { it.reference?.element?.let { it in referencesToSkip } ?: false } internalUsages.removeIf { it.reference?.element?.let { element -> element in referencesToSkip } ?: false }
} }
fun checkVisibilityInUsages(usages: Collection<UsageInfo>, conflicts: MultiMap<PsiElement, String>) { fun checkVisibilityInUsages(usages: Collection<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
@@ -396,7 +381,7 @@ class MoveConflictChecker(
) continue ) continue
val container = element.getUsageContext() val container = element.getUsageContext()
if (!declarationToContainers.getOrPut(referencedElement) { HashSet<PsiElement>() }.add(container)) continue if (!declarationToContainers.getOrPut(referencedElement) { HashSet() }.add(container)) continue
val targetContainer = moveTarget.getContainerDescriptor() ?: continue val targetContainer = moveTarget.getContainerDescriptor() ?: continue
@@ -1,5 +1,5 @@
/* /*
* Copyright 2000-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -41,13 +41,13 @@ import java.io.File
import javax.swing.JComponent import javax.swing.JComponent
class KotlinAwareMoveFilesOrDirectoriesDialog( class KotlinAwareMoveFilesOrDirectoriesDialog(
private val project: Project, private val project: Project,
private val initialDirectory: PsiDirectory?, private val initialDirectory: PsiDirectory?,
private val callback: (KotlinAwareMoveFilesOrDirectoriesDialog?) -> Unit private val callback: (KotlinAwareMoveFilesOrDirectoriesDialog?) -> Unit
) : DialogWrapper(project, true) { ) : DialogWrapper(project, true) {
companion object { companion object {
private val RECENT_KEYS = "MoveFile.RECENT_KEYS" private const val RECENT_KEYS = "MoveFile.RECENT_KEYS"
private val MOVE_FILES_OPEN_IN_EDITOR = "MoveFile.OpenInEditor" private const val MOVE_FILES_OPEN_IN_EDITOR = "MoveFile.OpenInEditor"
} }
private val nameLabel = JBLabelDecorator.createJBLabelDecorator().setBold(true) private val nameLabel = JBLabelDecorator.createJBLabelDecorator().setBold(true)
@@ -81,11 +81,13 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
RecentsManager.getInstance(project).getRecentEntries(RECENT_KEYS)?.let { targetDirectoryField.childComponent.history = it } RecentsManager.getInstance(project).getRecentEntries(RECENT_KEYS)?.let { targetDirectoryField.childComponent.history = it }
val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor() val descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor()
targetDirectoryField.addBrowseFolderListener(RefactoringBundle.message("select.target.directory"), targetDirectoryField.addBrowseFolderListener(
RefactoringBundle.message("the.file.will.be.moved.to.this.directory"), RefactoringBundle.message("select.target.directory"),
project, RefactoringBundle.message("the.file.will.be.moved.to.this.directory"),
descriptor, project,
TextComponentAccessor.TEXT_FIELD_WITH_HISTORY_WHOLE_TEXT) descriptor,
TextComponentAccessor.TEXT_FIELD_WITH_HISTORY_WHOLE_TEXT
)
val textField = targetDirectoryField.childComponent.textEditor val textField = targetDirectoryField.childComponent.textEditor
FileChooserFactory.getInstance().installFileCompletion(textField, descriptor, true, disposable) FileChooserFactory.getInstance().installFileCompletion(textField, descriptor, true, disposable)
textField.onTextChange { validateOKButton() } textField.onTextChange { validateOKButton() }
@@ -96,13 +98,13 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
val shortcutText = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_CODE_COMPLETION)) val shortcutText = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_CODE_COMPLETION))
return FormBuilder.createFormBuilder() return FormBuilder.createFormBuilder()
.addComponent(nameLabel) .addComponent(nameLabel)
.addLabeledComponent(RefactoringBundle.message("move.files.to.directory.label"), targetDirectoryField, UIUtil.LARGE_VGAP) .addLabeledComponent(RefactoringBundle.message("move.files.to.directory.label"), targetDirectoryField, UIUtil.LARGE_VGAP)
.addTooltip(RefactoringBundle.message("path.completion.shortcut", shortcutText)) .addTooltip(RefactoringBundle.message("path.completion.shortcut", shortcutText))
.addComponentToRightColumn(searchReferencesCb, UIUtil.LARGE_VGAP) .addComponentToRightColumn(searchReferencesCb, UIUtil.LARGE_VGAP)
.addComponentToRightColumn(openInEditorCb, UIUtil.LARGE_VGAP) .addComponentToRightColumn(openInEditorCb, UIUtil.LARGE_VGAP)
.addComponentToRightColumn(updatePackageDirectiveCb, UIUtil.LARGE_VGAP) .addComponentToRightColumn(updatePackageDirectiveCb, UIUtil.LARGE_VGAP)
.panel .panel
} }
fun setData(psiElements: Array<out PsiElement>, initialTargetDirectory: PsiDirectory?, helpID: String) { fun setData(psiElements: Array<out PsiElement>, initialTargetDirectory: PsiDirectory?, helpID: String) {
@@ -113,8 +115,7 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
is PsiFile -> RefactoringBundle.message("move.file.0", shortenedPath) is PsiFile -> RefactoringBundle.message("move.file.0", shortenedPath)
else -> RefactoringBundle.message("move.directory.0", shortenedPath) else -> RefactoringBundle.message("move.directory.0", shortenedPath)
} }
} } else {
else {
val isFile = psiElements.all { it is PsiFile } val isFile = psiElements.all { it is PsiFile }
val isDirectory = psiElements.all { it is PsiDirectory } val isDirectory = psiElements.all { it is PsiDirectory }
nameLabel.text = when { nameLabel.text = when {
@@ -129,7 +130,7 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
validateOKButton() validateOKButton()
this.helpID = helpID this.helpID = helpID
with (updatePackageDirectiveCb) { with(updatePackageDirectiveCb) {
val jetFiles = psiElements.filterIsInstance<KtFile>().filter(KtFile::isInJavaSourceRoot) val jetFiles = psiElements.filterIsInstance<KtFile>().filter(KtFile::isInJavaSourceRoot)
if (jetFiles.isEmpty()) { if (jetFiles.isEmpty()) {
parent.remove(updatePackageDirectiveCb) parent.remove(updatePackageDirectiveCb)
@@ -150,7 +151,7 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
} }
private fun validateOKButton() { private fun validateOKButton() {
isOKActionEnabled = targetDirectoryField.childComponent.text.length > 0 isOKActionEnabled = targetDirectoryField.childComponent.text.isNotEmpty()
} }
override fun doOKAction() { override fun doOKAction() {
@@ -172,17 +173,18 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
} }
try { try {
targetDirectory = DirectoryUtil.mkdirs(PsiManager.getInstance(project), directoryName) targetDirectory = DirectoryUtil.mkdirs(PsiManager.getInstance(project), directoryName)
} } catch (e: IncorrectOperationException) {
catch (e: IncorrectOperationException) {
// ignore // ignore
} }
} }
if (targetDirectory == null) { if (targetDirectory == null) {
CommonRefactoringUtil.showErrorMessage(title, CommonRefactoringUtil.showErrorMessage(
RefactoringBundle.message("cannot.create.directory"), title,
helpID, RefactoringBundle.message("cannot.create.directory"),
project) helpID,
project
)
return@executeCommand return@executeCommand
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations.ui package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui
@@ -29,10 +18,10 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import java.awt.BorderLayout import java.awt.BorderLayout
import javax.swing.* import javax.swing.*
internal class KotlinSelectNestedClassRefactoringDialog private constructor ( internal class KotlinSelectNestedClassRefactoringDialog private constructor(
project: Project, project: Project,
private val nestedClass: KtClassOrObject, private val nestedClass: KtClassOrObject,
private val targetContainer: PsiElement? private val targetContainer: PsiElement?
) : DialogWrapper(project, true) { ) : DialogWrapper(project, true) {
private val moveToUpperLevelButton = JRadioButton() private val moveToUpperLevelButton = JRadioButton()
private val moveMembersButton = JRadioButton() private val moveMembersButton = JRadioButton()
@@ -83,32 +72,34 @@ internal class KotlinSelectNestedClassRefactoringDialog private constructor (
companion object { companion object {
private fun MoveKotlinNestedClassesToUpperLevelDialog( private fun MoveKotlinNestedClassesToUpperLevelDialog(
nestedClass: KtClassOrObject, nestedClass: KtClassOrObject,
targetContainer: PsiElement? targetContainer: PsiElement?
): MoveKotlinNestedClassesToUpperLevelDialog? { ): MoveKotlinNestedClassesToUpperLevelDialog? {
val outerClass = nestedClass.containingClassOrObject ?: return null val outerClass = nestedClass.containingClassOrObject ?: return null
val newTarget = targetContainer val newTarget = targetContainer
?: outerClass.containingClassOrObject ?: outerClass.containingClassOrObject
?: outerClass.containingFile.let { it.containingDirectory ?: it } ?: outerClass.containingFile.let { it.containingDirectory ?: it }
return MoveKotlinNestedClassesToUpperLevelDialog(nestedClass.project, nestedClass, newTarget) return MoveKotlinNestedClassesToUpperLevelDialog(nestedClass.project, nestedClass, newTarget)
} }
private fun MoveKotlinNestedClassesDialog( private fun MoveKotlinNestedClassesDialog(
nestedClass: KtClassOrObject, nestedClass: KtClassOrObject,
targetContainer: PsiElement? targetContainer: PsiElement?
): MoveKotlinNestedClassesDialog { ): MoveKotlinNestedClassesDialog {
return MoveKotlinNestedClassesDialog(nestedClass.project, return MoveKotlinNestedClassesDialog(
listOf(nestedClass), nestedClass.project,
nestedClass.containingClassOrObject!!, listOf(nestedClass),
targetContainer as? KtClassOrObject ?: nestedClass.containingClassOrObject!!, nestedClass.containingClassOrObject!!,
null) targetContainer as? KtClassOrObject ?: nestedClass.containingClassOrObject!!,
null
)
} }
fun chooseNestedClassRefactoring(nestedClass: KtClassOrObject, targetContainer: PsiElement?) { fun chooseNestedClassRefactoring(nestedClass: KtClassOrObject, targetContainer: PsiElement?) {
val project = nestedClass.project val project = nestedClass.project
val dialog = when { val dialog = when {
targetContainer != null && targetContainer !is KtClassOrObject || targetContainer != null && targetContainer !is KtClassOrObject ||
nestedClass is KtClass && nestedClass.isInner() -> { nestedClass is KtClass && nestedClass.isInner() -> {
MoveKotlinNestedClassesToUpperLevelDialog(nestedClass, targetContainer) MoveKotlinNestedClassesToUpperLevelDialog(nestedClass, targetContainer)
} }
nestedClass is KtEnumEntry -> return nestedClass is KtEnumEntry -> return
@@ -1,23 +1,10 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations.ui; package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.ide.util.ClassFilter;
import com.intellij.ide.util.TreeClassChooser; import com.intellij.ide.util.TreeClassChooser;
import com.intellij.ide.util.TreeJavaClassChooserDialog; import com.intellij.ide.util.TreeJavaClassChooserDialog;
import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentEvent;
@@ -36,7 +23,6 @@ import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler; import com.intellij.refactoring.move.MoveHandler;
import com.intellij.refactoring.ui.RefactoringDialog; import com.intellij.refactoring.ui.RefactoringDialog;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt; import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
@@ -59,8 +45,7 @@ import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.List; import java.util.List;
import java.util.Objects;
import static org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessorKt.MoveSource;
public class MoveKotlinNestedClassesDialog extends RefactoringDialog { public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
private static final String RECENTS_KEY = MoveKotlinNestedClassesDialog.class.getName() + ".RECENTS_KEY"; private static final String RECENTS_KEY = MoveKotlinNestedClassesDialog.class.getName() + ".RECENTS_KEY";
@@ -74,6 +59,7 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
private JPanel targetClassChooserPanel; private JPanel targetClassChooserPanel;
private KotlinMemberSelectionTable memberTable; private KotlinMemberSelectionTable memberTable;
private PsiElement targetClass; private PsiElement targetClass;
public MoveKotlinNestedClassesDialog( public MoveKotlinNestedClassesDialog(
@NotNull Project project, @NotNull Project project,
@NotNull List<KtClassOrObject> elementsToMove, @NotNull List<KtClassOrObject> elementsToMove,
@@ -111,23 +97,20 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
RefactoringBundle.message("choose.destination.class"), RefactoringBundle.message("choose.destination.class"),
myProject, myProject,
GlobalSearchScope.projectScope(myProject), GlobalSearchScope.projectScope(myProject),
new ClassFilter() { aClass -> {
@Override if (!(aClass instanceof KtLightClassForSourceDeclaration)) return false;
public boolean isAccepted(PsiClass aClass) { KtClassOrObject classOrObject = ((KtLightClassForSourceDeclaration) aClass).getKotlinOrigin();
if (!(aClass instanceof KtLightClassForSourceDeclaration)) return false;
KtClassOrObject classOrObject = ((KtLightClassForSourceDeclaration) aClass).getKotlinOrigin();
if (classOrObject instanceof KtObjectDeclaration) { if (classOrObject instanceof KtObjectDeclaration) {
return !((KtObjectDeclaration) classOrObject).isObjectLiteral(); return !((KtObjectDeclaration) classOrObject).isObjectLiteral();
}
if (classOrObject instanceof KtClass) {
KtClass ktClass = (KtClass) classOrObject;
return !(ktClass.isInner() || ktClass.isAnnotation());
}
return false;
} }
if (classOrObject instanceof KtClass) {
KtClass ktClass = (KtClass) classOrObject;
return !(ktClass.isInner() || ktClass.isAnnotation());
}
return false;
}, },
null, null,
null, null,
@@ -144,13 +127,14 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
return LightClassUtilsKt.toLightClass(((KtClassOrObjectTreeNode) userObject).getValue()); return LightClassUtilsKt.toLightClass(((KtClassOrObjectTreeNode) userObject).getValue());
} }
}; };
chooser.selectDirectory((targetClass != null ? targetClass : originalClass).getContainingFile().getContainingDirectory()); chooser.selectDirectory(
(targetClass != null ? targetClass : originalClass).getContainingFile().getContainingDirectory());
chooser.showDialog(); chooser.showDialog();
PsiClass aClass = chooser.getSelected(); PsiClass aClass = chooser.getSelected();
if (aClass instanceof KtLightClassForSourceDeclaration) { if (aClass instanceof KtLightClassForSourceDeclaration) {
targetClass = ((KtLightClassForSourceDeclaration) aClass).getKotlinOrigin(); targetClass = ((KtLightClassForSourceDeclaration) aClass).getKotlinOrigin();
targetClassChooser.setText(aClass.getQualifiedName()); targetClassChooser.setText(Objects.requireNonNull(aClass.getQualifiedName()));
} }
else { else {
targetClass = aClass; targetClass = aClass;
@@ -164,15 +148,12 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
if (codeFragment != null) { if (codeFragment != null) {
CompletionUtilsKt.setExtraCompletionFilter( CompletionUtilsKt.setExtraCompletionFilter(
codeFragment, codeFragment,
new Function1<LookupElement, Boolean>() { lookupElement -> {
@Override Object lookupObject = lookupElement.getObject();
public Boolean invoke(LookupElement lookupElement) { if (!(lookupObject instanceof DeclarationLookupObject)) return false;
Object lookupObject = lookupElement.getObject(); PsiElement psiElement = ((DeclarationLookupObject) lookupObject).getPsiElement();
if (!(lookupObject instanceof DeclarationLookupObject)) return false; if (lookupObject instanceof PackageLookupObject) return true;
PsiElement psiElement = ((DeclarationLookupObject) lookupObject).getPsiElement(); return (psiElement instanceof KtClassOrObject) && KotlinRefactoringUtilKt.canRefactor(psiElement);
if (lookupObject instanceof PackageLookupObject) return true;
return (psiElement instanceof KtClassOrObject) && KotlinRefactoringUtilKt.canRefactor(psiElement);
}
} }
); );
} }
@@ -193,28 +174,27 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
targetClassChooserPanel.add(targetClassChooser); targetClassChooserPanel.add(targetClassChooser);
} }
private void initMemberInfo(@NotNull final List<KtClassOrObject> elementsToMove) { private void initMemberInfo(@NotNull List<KtClassOrObject> elementsToMove) {
List<KotlinMemberInfo> memberInfos = CollectionsKt.mapNotNull( List<KotlinMemberInfo> memberInfos = CollectionsKt.mapNotNull(
originalClass.getDeclarations(), originalClass.getDeclarations(),
new Function1<KtDeclaration, KotlinMemberInfo>() { declaration -> {
@Override if (!(declaration instanceof KtClassOrObject)) return null;
public KotlinMemberInfo invoke(KtDeclaration declaration) { KtClassOrObject classOrObject = (KtClassOrObject) declaration;
if (!(declaration instanceof KtClassOrObject)) return null;
KtClassOrObject classOrObject = (KtClassOrObject) declaration;
if (classOrObject instanceof KtClass && ((KtClass) classOrObject).isInner()) return null; if (classOrObject instanceof KtClass && ((KtClass) classOrObject).isInner()) return null;
if (classOrObject instanceof KtObjectDeclaration && ((KtObjectDeclaration) classOrObject).isCompanion()) return null; if (classOrObject instanceof KtObjectDeclaration && ((KtObjectDeclaration) classOrObject).isCompanion()) {
return null;
KotlinMemberInfo memberInfo = new KotlinMemberInfo(classOrObject, false);
memberInfo.setChecked(elementsToMove.contains(declaration));
return memberInfo;
} }
KotlinMemberInfo memberInfo = new KotlinMemberInfo(classOrObject, false);
memberInfo.setChecked(elementsToMove.contains(declaration));
return memberInfo;
} }
); );
KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null); KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null);
memberTable = selectionPanel.getTable(); memberTable = selectionPanel.getTable();
MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl(); MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl();
memberInfoModel.memberInfoChanged(new MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo>(memberInfos)); memberInfoModel.memberInfoChanged(new MemberInfoChange<>(memberInfos));
selectionPanel.getTable().setMemberInfoModel(memberInfoModel); selectionPanel.getTable().setMemberInfoModel(memberInfoModel);
selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel); selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel);
membersInfoPanel.add(selectionPanel, BorderLayout.CENTER); membersInfoPanel.add(selectionPanel, BorderLayout.CENTER);
@@ -223,12 +203,7 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
private List<KtClassOrObject> getSelectedElementsToMove() { private List<KtClassOrObject> getSelectedElementsToMove() {
return CollectionsKt.map( return CollectionsKt.map(
memberTable.getSelectedMemberInfos(), memberTable.getSelectedMemberInfos(),
new Function1<KotlinMemberInfo, KtClassOrObject>() { info -> (KtClassOrObject) info.getMember()
@Override
public KtClassOrObject invoke(KotlinMemberInfo info) {
return (KtClassOrObject) info.getMember();
}
}
); );
} }
@@ -266,7 +241,7 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
MoveDeclarationsDelegate.NestedClass delegate = new MoveDeclarationsDelegate.NestedClass(); MoveDeclarationsDelegate.NestedClass delegate = new MoveDeclarationsDelegate.NestedClass();
MoveDeclarationsDescriptor descriptor = new MoveDeclarationsDescriptor( MoveDeclarationsDescriptor descriptor = new MoveDeclarationsDescriptor(
myProject, myProject,
MoveSource(elementsToMove), MoveKotlinDeclarationsProcessorKt.MoveSource(elementsToMove),
target, target,
delegate, delegate,
false, false,
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2016 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations.ui; package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;
@@ -66,13 +55,9 @@ import org.jetbrains.kotlin.types.KotlinType;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessorKt.MoveSource;
public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase { public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
@NonNls private static final String RECENTS_KEY = MoveKotlinNestedClassesToUpperLevelDialog.class.getName() + ".RECENTS_KEY"; @NonNls private static final String RECENTS_KEY = MoveKotlinNestedClassesToUpperLevelDialog.class.getName() + ".RECENTS_KEY";
@@ -160,7 +145,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
} }
private boolean isThisNeeded() { private boolean isThisNeeded() {
return innerClass instanceof KtClass && MoveUtilsKt.traverseOuterInstanceReferences((KtClass) innerClass, true); return innerClass instanceof KtClass && MoveUtilsKt.traverseOuterInstanceReferences(innerClass, true);
} }
@Nullable @Nullable
@@ -180,12 +165,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
if (innerClass instanceof KtClass && ((KtClass) innerClass).isInner()) { if (innerClass instanceof KtClass && ((KtClass) innerClass).isInner()) {
passOuterClassCheckBox.setSelected(true); passOuterClassCheckBox.setSelected(true);
passOuterClassCheckBox.addItemListener(new ItemListener() { passOuterClassCheckBox.addItemListener(e -> parameterField.setEnabled(passOuterClassCheckBox.isSelected()));
@Override
public void itemStateChanged(ItemEvent e) {
parameterField.setEnabled(passOuterClassCheckBox.isSelected());
}
});
} }
else { else {
passOuterClassCheckBox.setSelected(false); passOuterClassCheckBox.setSelected(false);
@@ -199,12 +179,9 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
parameterField.setEnabled(thisNeeded); parameterField.setEnabled(thisNeeded);
} }
passOuterClassCheckBox.addItemListener(new ItemListener() { passOuterClassCheckBox.addItemListener(e -> {
@Override boolean selected = passOuterClassCheckBox.isSelected();
public void itemStateChanged(ItemEvent e) { parameterField.getComponent().setEnabled(selected);
boolean selected = passOuterClassCheckBox.isSelected();
parameterField.getComponent().setEnabled(selected);
}
}); });
if (!(targetContainer instanceof PsiDirectory)) { if (!(targetContainer instanceof PsiDirectory)) {
@@ -218,7 +195,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
innerClassBody != null innerClassBody != null
? new NewDeclarationNameValidator(innerClassBody, (PsiElement) null, ? new NewDeclarationNameValidator(innerClassBody, (PsiElement) null,
NewDeclarationNameValidator.Target.VARIABLES, NewDeclarationNameValidator.Target.VARIABLES,
Collections.<KtDeclaration>emptyList()) Collections.emptyList())
: new CollectingNameValidator(); : new CollectingNameValidator();
List<String> suggestions = KotlinNameSuggester.INSTANCE.suggestNamesByType(getOuterInstanceType(), validator, "outer"); List<String> suggestions = KotlinNameSuggester.INSTANCE.suggestNamesByType(getOuterInstanceType(), validator, "outer");
parameterField.setSuggestions(ArrayUtil.toStringArray(suggestions)); parameterField.setSuggestions(ArrayUtil.toStringArray(suggestions));
@@ -268,8 +245,8 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
if (!Comparing.equal(oldPackageFqName != null ? oldPackageFqName.asString() : null, targetName)) { if (!Comparing.equal(oldPackageFqName != null ? oldPackageFqName.asString() : null, targetName)) {
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project); ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project);
List<VirtualFile> contentSourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project); List<VirtualFile> contentSourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project);
final PackageWrapper newPackage = new PackageWrapper(PsiManager.getInstance(project), targetName); PackageWrapper newPackage = new PackageWrapper(PsiManager.getInstance(project), targetName);
final VirtualFile targetSourceRoot; VirtualFile targetSourceRoot;
if (contentSourceRoots.size() > 1) { if (contentSourceRoots.size() > 1) {
PsiDirectory initialDir = null; PsiDirectory initialDir = null;
PsiPackage oldPackage = oldPackageFqName != null PsiPackage oldPackage = oldPackageFqName != null
@@ -293,15 +270,12 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
} }
PsiDirectory dir = RefactoringUtil.findPackageDirectoryInSourceRoot(newPackage, targetSourceRoot); PsiDirectory dir = RefactoringUtil.findPackageDirectoryInSourceRoot(newPackage, targetSourceRoot);
if (dir == null) { if (dir == null) {
dir = ApplicationManager.getApplication().runWriteAction(new NullableComputable<PsiDirectory>() { dir = ApplicationManager.getApplication().runWriteAction((NullableComputable<PsiDirectory>) () -> {
@Override try {
public PsiDirectory compute() { return RefactoringUtil.createPackageDirectoryInSourceRoot(newPackage, targetSourceRoot);
try { }
return RefactoringUtil.createPackageDirectoryInSourceRoot(newPackage, targetSourceRoot); catch (IncorrectOperationException e) {
} return null;
catch (IncorrectOperationException e) {
return null;
}
} }
}); });
} }
@@ -321,12 +295,20 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
String className = getClassName(); String className = getClassName();
String parameterName = getParameterName(); String parameterName = getParameterName();
if (className != null && className.isEmpty()) throw new ConfigurationException(RefactoringBundle.message("no.class.name.specified")); if (className != null && className.isEmpty()) {
if (!KtPsiUtilKt.isIdentifier(className)) throw new ConfigurationException(RefactoringMessageUtil.getIncorrectIdentifierMessage(className)); throw new ConfigurationException(RefactoringBundle.message("no.class.name.specified"));
}
if (!KtPsiUtilKt.isIdentifier(className)) {
throw new ConfigurationException(RefactoringMessageUtil.getIncorrectIdentifierMessage(className));
}
if (passOuterClassCheckBox.isSelected()) { if (passOuterClassCheckBox.isSelected()) {
if (parameterName != null && parameterName.isEmpty()) throw new ConfigurationException(RefactoringBundle.message("no.parameter.name.specified")); if (parameterName != null && parameterName.isEmpty()) {
if (!KtPsiUtilKt.isIdentifier(parameterName)) throw new ConfigurationException(RefactoringMessageUtil.getIncorrectIdentifierMessage(parameterName)); throw new ConfigurationException(RefactoringBundle.message("no.parameter.name.specified"));
}
if (!KtPsiUtilKt.isIdentifier(parameterName)) {
throw new ConfigurationException(RefactoringMessageUtil.getIncorrectIdentifierMessage(parameterName));
}
} }
PsiElement targetContainer = getTargetContainer(); PsiElement targetContainer = getTargetContainer();
@@ -350,7 +332,9 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
.getPackage(targetPackageFqName) .getPackage(targetPackageFqName)
.getMemberScope() .getMemberScope()
.getContributedClassifier(Name.identifier(className), NoLookupLocation.FROM_IDE); .getContributedClassifier(Name.identifier(className), NoLookupLocation.FROM_IDE);
if (existingClass != null) throw new ConfigurationException("Class " + className + " already exists in package " + targetPackageFqName); if (existingClass != null) {
throw new ConfigurationException("Class " + className + " already exists in package " + targetPackageFqName);
}
PsiDirectory targetDir = targetContainer instanceof PsiDirectory PsiDirectory targetDir = targetContainer instanceof PsiDirectory
? (PsiDirectory) targetContainer ? (PsiDirectory) targetContainer
@@ -382,34 +366,23 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
KotlinMoveTarget moveTarget; KotlinMoveTarget moveTarget;
if (target instanceof PsiDirectory) { if (target instanceof PsiDirectory) {
final PsiDirectory targetDir = (PsiDirectory) target; PsiDirectory targetDir = (PsiDirectory) target;
final FqName targetPackageFqName = getTargetPackageFqName(target); FqName targetPackageFqName = getTargetPackageFqName(target);
if (targetPackageFqName == null) return; if (targetPackageFqName == null) return;
final String targetFileName = KotlinNameSuggester.INSTANCE.suggestNameByName( String targetFileName = KotlinNameSuggester.INSTANCE.suggestNameByName(
newClassName, newClassName,
new Function1<String, Boolean>() { s -> targetDir.findFile(s + "." + KotlinFileType.EXTENSION) == null
@Override
public Boolean invoke(String s) {
return targetDir.findFile(s + "." + KotlinFileType.EXTENSION) == null;
}
}
) + "." + KotlinFileType.EXTENSION; ) + "." + KotlinFileType.EXTENSION;
moveTarget = new KotlinMoveTargetForDeferredFile( moveTarget = new KotlinMoveTargetForDeferredFile(
targetPackageFqName, targetPackageFqName,
targetDir, targetDir,
null, null,
new Function1<KtFile, KtFile>() { originalFile -> KotlinRefactoringUtilKt.createKotlinFile(targetFileName, targetDir, targetPackageFqName.asString())
@Override
public KtFile invoke(@NotNull KtFile originalFile) {
return KotlinRefactoringUtilKt.createKotlinFile(targetFileName, targetDir, targetPackageFqName.asString());
}
}
); );
} }
else { else {
//noinspection ConstantConditions
moveTarget = new KotlinMoveTargetForExistingElement((KtElement) target); moveTarget = new KotlinMoveTargetForExistingElement((KtElement) target);
} }
@@ -417,7 +390,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
MoveDeclarationsDelegate delegate = new MoveDeclarationsDelegate.NestedClass(newClassName, outerInstanceParameterName); MoveDeclarationsDelegate delegate = new MoveDeclarationsDelegate.NestedClass(newClassName, outerInstanceParameterName);
MoveDeclarationsDescriptor moveDescriptor = new MoveDeclarationsDescriptor( MoveDeclarationsDescriptor moveDescriptor = new MoveDeclarationsDescriptor(
project, project,
MoveSource(innerClass), MoveKotlinDeclarationsProcessorKt.MoveSource(innerClass),
moveTarget, moveTarget,
delegate, delegate,
isSearchInComments(), isSearchInComments(),
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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.moveDeclarations.ui; package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;
@@ -32,8 +21,10 @@ import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*; import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.refactoring.*; import com.intellij.refactoring.*;
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel; import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
import com.intellij.refactoring.classMembers.MemberInfoBase;
import com.intellij.refactoring.classMembers.MemberInfoChange; import com.intellij.refactoring.classMembers.MemberInfoChange;
import com.intellij.refactoring.classMembers.MemberInfoChangeListener; import com.intellij.refactoring.classMembers.MemberInfoChangeListener;
import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveCallback;
@@ -46,12 +37,9 @@ import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.ui.ComboboxWithBrowseButton; import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.RecentsManager; import com.intellij.ui.RecentsManager;
import com.intellij.ui.ReferenceEditorComboWithBrowseButton; import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException; import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.UIUtil;
import kotlin.collections.CollectionsKt; import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinFileType; import org.jetbrains.kotlin.idea.KotlinFileType;
@@ -70,19 +58,17 @@ import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtNamedDeclaration; import org.jetbrains.kotlin.psi.KtNamedDeclaration;
import org.jetbrains.kotlin.psi.KtPureElement;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt; import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.*; import java.util.*;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessorKt.MoveSource;
public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private static final String RECENTS_KEY = "MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY"; private static final String RECENTS_KEY = "MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY";
@@ -143,12 +129,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return CollectionsKt.distinct( return CollectionsKt.distinct(
CollectionsKt.map( CollectionsKt.map(
elementsToMove, elementsToMove,
new Function1<KtNamedDeclaration, KtFile>() { KtPureElement::getContainingKtFile
@Override
public KtFile invoke(KtNamedDeclaration declaration) {
return declaration.getContainingKtFile();
}
}
) )
); );
} }
@@ -159,12 +140,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
CollectionsKt.distinct( CollectionsKt.distinct(
CollectionsKt.map( CollectionsKt.map(
sourceFiles, sourceFiles,
new Function1<KtFile, PsiDirectory>() { PsiFileImpl::getParent
@Override
public PsiDirectory invoke(KtFile jetFile) {
return jetFile.getParent();
}
}
) )
) )
); );
@@ -174,12 +150,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return CollectionsKt.filterIsInstance( return CollectionsKt.filterIsInstance(
CollectionsKt.flatMap( CollectionsKt.flatMap(
sourceFiles, sourceFiles,
new Function1<KtFile, Iterable<?>>() { KtPsiUtilKt::getFileOrScriptDeclarations
@Override
public Iterable<?> invoke(KtFile ktFile) {
return KtPsiUtilKt.getFileOrScriptDeclarations(ktFile);
}
}
), ),
KtNamedDeclaration.class KtNamedDeclaration.class
); );
@@ -196,7 +167,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private static List<PsiFile> getFilesExistingInTargetDir( private static List<PsiFile> getFilesExistingInTargetDir(
@NotNull List<KtFile> sourceFiles, @NotNull List<KtFile> sourceFiles,
@Nullable String targetFileName, @Nullable String targetFileName,
@Nullable final PsiDirectory targetDirectory @Nullable PsiDirectory targetDirectory
) { ) {
if (targetDirectory == null) return emptyList(); if (targetDirectory == null) return emptyList();
@@ -205,62 +176,44 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
? Collections.singletonList(targetFileName) ? Collections.singletonList(targetFileName)
: CollectionsKt.map( : CollectionsKt.map(
sourceFiles, sourceFiles,
new Function1<KtFile, String>() { PsiFileImpl::getName
@Override
public String invoke(KtFile jetFile) {
return jetFile.getName();
}
}
); );
return CollectionsKt.filterNotNull( return CollectionsKt.filterNotNull(
CollectionsKt.map( CollectionsKt.map(
fileNames, fileNames,
new Function1<String, PsiFile>() { targetDirectory::findFile
@Override
public PsiFile invoke(String s) {
return targetDirectory.findFile(s);
}
}
) )
); );
} }
private void initMemberInfo( private void initMemberInfo(
@NotNull final Set<KtNamedDeclaration> elementsToMove, @NotNull Set<KtNamedDeclaration> elementsToMove,
@NotNull List<KtFile> sourceFiles @NotNull List<KtFile> sourceFiles
) { ) {
final List<KotlinMemberInfo> memberInfos = CollectionsKt.map( List<KotlinMemberInfo> memberInfos = CollectionsKt.map(
getAllDeclarations(sourceFiles), getAllDeclarations(sourceFiles),
new Function1<KtNamedDeclaration, KotlinMemberInfo>() { declaration -> {
@Override KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false);
public KotlinMemberInfo invoke(KtNamedDeclaration declaration) { memberInfo.setChecked(elementsToMove.contains(declaration));
KotlinMemberInfo memberInfo = new KotlinMemberInfo(declaration, false); return memberInfo;
memberInfo.setChecked(elementsToMove.contains(declaration));
return memberInfo;
}
} }
); );
KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null); KotlinMemberSelectionPanel selectionPanel = new KotlinMemberSelectionPanel(getTitle(), memberInfos, null);
memberTable = selectionPanel.getTable(); memberTable = selectionPanel.getTable();
MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl(); MemberInfoModelImpl memberInfoModel = new MemberInfoModelImpl();
memberInfoModel.memberInfoChanged(new MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo>(memberInfos)); memberInfoModel.memberInfoChanged(new MemberInfoChange<>(memberInfos));
selectionPanel.getTable().setMemberInfoModel(memberInfoModel); selectionPanel.getTable().setMemberInfoModel(memberInfoModel);
selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel); selectionPanel.getTable().addMemberInfoChangeListener(memberInfoModel);
selectionPanel.getTable().addMemberInfoChangeListener( selectionPanel.getTable().addMemberInfoChangeListener(
new MemberInfoChangeListener<KtNamedDeclaration, KotlinMemberInfo>() { new MemberInfoChangeListener<KtNamedDeclaration, KotlinMemberInfo>() {
private boolean shouldUpdateFileNameField(final Collection<KotlinMemberInfo> changedMembers) { private boolean shouldUpdateFileNameField(Collection<KotlinMemberInfo> changedMembers) {
if (!tfFileNameInPackage.isEnabled()) return true; if (!tfFileNameInPackage.isEnabled()) return true;
Collection<KtNamedDeclaration> previousDeclarations = CollectionsKt.filterNotNull( Collection<KtNamedDeclaration> previousDeclarations = CollectionsKt.filterNotNull(
CollectionsKt.map( CollectionsKt.map(
memberInfos, memberInfos,
new Function1<KotlinMemberInfo, KtNamedDeclaration>() { info -> changedMembers.contains(info) != info.isChecked() ? info.getMember() : null
@Override
public KtNamedDeclaration invoke(KotlinMemberInfo info) {
return changedMembers.contains(info) != info.isChecked() ? info.getMember() : null;
}
}
) )
); );
String suggestedText = previousDeclarations.isEmpty() String suggestedText = previousDeclarations.isEmpty()
@@ -270,7 +223,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
@Override @Override
public void memberInfoChanged(MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo> event) { public void memberInfoChanged(@NotNull MemberInfoChange<KtNamedDeclaration, KotlinMemberInfo> event) {
updatePackageDirectiveCheckBox(); updatePackageDirectiveCheckBox();
updateFileNameInPackageField(); updateFileNameInPackageField();
// Update file name field only if it user hasn't changed it to some non-default value // Update file name field only if it user hasn't changed it to some non-default value
@@ -285,7 +238,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private void updateSuggestedFileName() { private void updateSuggestedFileName() {
tfFileNameInPackage.setText(MoveUtilsKt.guessNewFileName(getSelectedElementsToMove())); tfFileNameInPackage.setText(MoveUtilsKt.guessNewFileName(getSelectedElementsToMove()));
} }
private void updateFileNameInPackageField() { private void updateFileNameInPackageField() {
@@ -316,14 +268,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
classPackageChooser.getChildComponent() classPackageChooser.getChildComponent()
); );
cbSpecifyFileNameInPackage.addActionListener( cbSpecifyFileNameInPackage.addActionListener(e -> updateFileNameInPackageField());
new ActionListener() {
@Override
public void actionPerformed(@NotNull ActionEvent e) {
updateFileNameInPackageField();
}
}
);
cbUpdatePackageDirective.setSelected(arePackagesAndDirectoryMatched(sourceFiles)); cbUpdatePackageDirective.setSelected(arePackagesAndDirectoryMatched(sourceFiles));
} }
@@ -342,22 +287,16 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
rbMoveToPackage.addActionListener( rbMoveToPackage.addActionListener(
new ActionListener() { e -> {
@Override classPackageChooser.requestFocus();
public void actionPerformed(@NotNull ActionEvent e) { updateControls();
classPackageChooser.requestFocus();
updateControls();
}
} }
); );
rbMoveToFile.addActionListener( rbMoveToFile.addActionListener(
new ActionListener() { e -> {
@Override fileChooser.requestFocus();
public void actionPerformed(@NotNull ActionEvent e) { updateControls();
fileChooser.requestFocus();
updateControls();
}
} }
); );
} }
@@ -367,33 +306,30 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
@NotNull Set<KtNamedDeclaration> elementsToMove, @NotNull Set<KtNamedDeclaration> elementsToMove,
@NotNull List<KtFile> sourceFiles @NotNull List<KtFile> sourceFiles
) { ) {
final PsiDirectory sourceDir = sourceFiles.get(0).getParent(); PsiDirectory sourceDir = sourceFiles.get(0).getParent();
assert sourceDir != null : sourceFiles.get(0).getVirtualFile().getPath(); assert sourceDir != null : sourceFiles.get(0).getVirtualFile().getPath();
fileChooser.addActionListener( fileChooser.addActionListener(
new ActionListener() { e -> {
@Override KotlinFileChooserDialog dialog = new KotlinFileChooserDialog("Choose Containing File", myProject);
public void actionPerformed(ActionEvent e) {
KotlinFileChooserDialog dialog = new KotlinFileChooserDialog("Choose Containing File", myProject);
File targetFile = new File(getTargetFilePath()); File targetFile1 = new File(getTargetFilePath());
PsiFile targetPsiFile = PhysicalFileSystemUtilsKt.toPsiFile(targetFile, myProject); PsiFile targetPsiFile = PhysicalFileSystemUtilsKt.toPsiFile(targetFile1, myProject);
if (targetPsiFile instanceof KtFile) { if (targetPsiFile instanceof KtFile) {
dialog.select((KtFile) targetPsiFile); dialog.select((KtFile) targetPsiFile);
} }
else { else {
PsiDirectory targetDir = PhysicalFileSystemUtilsKt.toPsiDirectory(targetFile.getParentFile(), myProject); PsiDirectory targetDir = PhysicalFileSystemUtilsKt.toPsiDirectory(targetFile1.getParentFile(), myProject);
if (targetDir == null) { if (targetDir == null) {
targetDir = sourceDir; targetDir = sourceDir;
}
dialog.selectDirectory(targetDir);
} }
dialog.selectDirectory(targetDir);
}
dialog.showDialog(); dialog.showDialog();
KtFile selectedFile = dialog.isOK() ? dialog.getSelected() : null; KtFile selectedFile = dialog.isOK() ? dialog.getSelected() : null;
if (selectedFile != null) { if (selectedFile != null) {
fileChooser.setText(selectedFile.getVirtualFile().getPath()); fileChooser.setText(selectedFile.getVirtualFile().getPath());
}
} }
} }
); );
@@ -446,12 +382,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private boolean isFullFileMove() { private boolean isFullFileMove() {
Map<KtFile, List<KtNamedDeclaration>> fileToElements = CollectionsKt.groupBy( Map<KtFile, List<KtNamedDeclaration>> fileToElements = CollectionsKt.groupBy(
getSelectedElementsToMove(), getSelectedElementsToMove(),
new Function1<KtNamedDeclaration, KtFile>() { KtPureElement::getContainingKtFile
@Override
public KtFile invoke(KtNamedDeclaration declaration) {
return declaration.getContainingKtFile();
}
}
); );
for (Map.Entry<KtFile, List<KtNamedDeclaration>> entry : fileToElements.entrySet()) { for (Map.Entry<KtFile, List<KtNamedDeclaration>> entry : fileToElements.entrySet()) {
if (KtPsiUtilKt.getFileOrScriptDeclarations(entry.getKey()).size() != entry.getValue().size()) return false; if (KtPsiUtilKt.getFileOrScriptDeclarations(entry.getKey()).size() != entry.getValue().size()) return false;
@@ -486,7 +417,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
if (ret != Messages.YES) return null; if (ret != Messages.YES) return null;
} }
DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper)destinationFolderCB.getComboBox().getSelectedItem(); DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper) destinationFolderCB.getComboBox().getSelectedItem();
PsiDirectory selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : null; PsiDirectory selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : null;
if (selectedPsiDirectory == null) { if (selectedPsiDirectory == null) {
if (initialTargetDirectory != null) { if (initialTargetDirectory != null) {
@@ -525,9 +456,9 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
if (targetDirWithMoveDestination == null) return null; if (targetDirWithMoveDestination == null) return null;
VirtualFile targetDir = targetDirWithMoveDestination.getFirst(); VirtualFile targetDir = targetDirWithMoveDestination.getFirst();
final MoveDestination moveDestination = targetDirWithMoveDestination.getSecond(); MoveDestination moveDestination = targetDirWithMoveDestination.getSecond();
final String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText(); String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText();
if (targetFileName != null && !checkTargetFileName(targetFileName)) return null; if (targetFileName != null && !checkTargetFileName(targetFileName)) return null;
PsiDirectory targetDirectory = moveDestination.getTargetIfExists(sourceDirectory); PsiDirectory targetDirectory = moveDestination.getTargetIfExists(sourceDirectory);
@@ -537,12 +468,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
if (filesExistingInTargetDir.size() > 1) { if (filesExistingInTargetDir.size() > 1) {
String filePathsToReport = StringUtil.join( String filePathsToReport = StringUtil.join(
filesExistingInTargetDir, filesExistingInTargetDir,
new Function<PsiFile, String>() { file -> file.getVirtualFile().getPath(),
@Override
public String fun(PsiFile file) {
return file.getVirtualFile().getPath();
}
},
"\n" "\n"
); );
Messages.showErrorDialog( Messages.showErrorDialog(
@@ -560,7 +486,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
"File '%s' already exists. Do you want to move selected declarations to this file?", "File '%s' already exists. Do you want to move selected declarations to this file?",
targetFile.getVirtualFile().getPath() targetFile.getVirtualFile().getPath()
); );
int ret= int ret =
Messages.showYesNoDialog(myProject, question, RefactoringBundle.message("move.title"), Messages.showYesNoDialog(myProject, question, RefactoringBundle.message("move.title"),
Messages.getQuestionIcon()); Messages.getQuestionIcon());
if (ret != Messages.YES) return null; if (ret != Messages.YES) return null;
@@ -576,19 +502,14 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
new FqName(getTargetPackage()), new FqName(getTargetPackage()),
moveDestination.getTargetIfExists(sourceFiles.get(0)), moveDestination.getTargetIfExists(sourceFiles.get(0)),
targetDir, targetDir,
new Function1<KtFile, KtFile>() { originalFile -> KotlinRefactoringUtilKt.getOrCreateKotlinFile(
@Override targetFileName != null ? targetFileName : originalFile.getName(),
public KtFile invoke(@NotNull KtFile originalFile) { moveDestination.getTargetDirectory(originalFile)
return KotlinRefactoringUtilKt.getOrCreateKotlinFile( )
targetFileName != null ? targetFileName : originalFile.getName(),
moveDestination.getTargetDirectory(originalFile)
);
}
}
); );
} }
final File targetFile = new File(getTargetFilePath()); File targetFile = new File(getTargetFilePath());
if (!checkTargetFileName(targetFile.getName())) return null; if (!checkTargetFileName(targetFile.getName())) return null;
KtFile jetFile = (KtFile) PhysicalFileSystemUtilsKt.toPsiFile(targetFile, myProject); KtFile jetFile = (KtFile) PhysicalFileSystemUtilsKt.toPsiFile(targetFile, myProject);
if (jetFile != null) { if (jetFile != null) {
@@ -602,7 +523,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
Path targetFilePath = targetFile.toPath(); Path targetFilePath = targetFile.toPath();
Path targetDirPath = targetFilePath.getParent(); Path targetDirPath = targetFilePath.getParent();
if (targetDirPath == null || !targetDirPath.startsWith(getProject().getBasePath())) { if (targetDirPath == null || !targetDirPath.startsWith(Objects.requireNonNull(getProject().getBasePath()))) {
setErrorText("Incorrect target path. Directory " + targetDirPath + " does not belong to current project."); setErrorText("Incorrect target path. Directory " + targetDirPath + " does not belong to current project.");
return null; return null;
} }
@@ -625,7 +546,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
File targetDir = targetDirPath.toFile(); File targetDir = targetDirPath.toFile();
final PsiDirectory psiDirectory = targetDir != null ? PhysicalFileSystemUtilsKt.toPsiDirectory(targetDir, myProject) : null; PsiDirectory psiDirectory = targetDir != null ? PhysicalFileSystemUtilsKt.toPsiDirectory(targetDir, myProject) : null;
if (psiDirectory == null) { if (psiDirectory == null) {
setErrorText("No directory found for file: " + targetFile.getPath()); setErrorText("No directory found for file: " + targetFile.getPath());
return null; return null;
@@ -633,13 +554,8 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
Set<FqName> sourcePackageFqNames = CollectionsKt.mapTo( Set<FqName> sourcePackageFqNames = CollectionsKt.mapTo(
sourceFiles, sourceFiles,
new LinkedHashSet<FqName>(), new LinkedHashSet<>(),
new Function1<KtFile, FqName>() { KtFile::getPackageFqName
@Override
public FqName invoke(KtFile file) {
return file.getPackageFqName();
}
}
); );
FqName targetPackageFqName = CollectionsKt.singleOrNull(sourcePackageFqNames); FqName targetPackageFqName = CollectionsKt.singleOrNull(sourcePackageFqNames);
if (targetPackageFqName == null) { if (targetPackageFqName == null) {
@@ -651,17 +567,12 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
targetPackageFqName = new FqName(psiPackage.getQualifiedName()); targetPackageFqName = new FqName(psiPackage.getQualifiedName());
} }
final String finalTargetPackageFqName = targetPackageFqName.asString(); String finalTargetPackageFqName = targetPackageFqName.asString();
return new KotlinMoveTargetForDeferredFile( return new KotlinMoveTargetForDeferredFile(
targetPackageFqName, targetPackageFqName,
psiDirectory, psiDirectory,
null, null,
new Function1<KtFile, KtFile>() { originalFile -> KotlinRefactoringUtilKt.getOrCreateKotlinFile(targetFile.getName(), psiDirectory, finalTargetPackageFqName)
@Override
public KtFile invoke(@NotNull KtFile originalFile) {
return KotlinRefactoringUtilKt.getOrCreateKotlinFile(targetFile.getName(), psiDirectory, finalTargetPackageFqName);
}
}
); );
} }
@@ -692,12 +603,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
private List<KtNamedDeclaration> getSelectedElementsToMove() { private List<KtNamedDeclaration> getSelectedElementsToMove() {
return CollectionsKt.map( return CollectionsKt.map(
memberTable.getSelectedMemberInfos(), memberTable.getSelectedMemberInfos(),
new Function1<KotlinMemberInfo, KtNamedDeclaration>() { MemberInfoBase::getMember
@Override
public KtNamedDeclaration invoke(KotlinMemberInfo info) {
return info.getMember();
}
}
); );
} }
@@ -736,7 +642,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
List<KtNamedDeclaration> elementsToMove = getSelectedElementsToMove(); List<KtNamedDeclaration> elementsToMove = getSelectedElementsToMove();
List<KtFile> sourceFiles = getSourceFiles(elementsToMove); List<KtFile> sourceFiles = getSourceFiles(elementsToMove);
final PsiDirectory sourceDirectory = getSourceDirectory(sourceFiles); PsiDirectory sourceDirectory = getSourceDirectory(sourceFiles);
for (PsiElement element : elementsToMove) { for (PsiElement element : elementsToMove) {
String message = target.verify(element.getContainingFile()); String message = target.verify(element.getContainingFile());
@@ -754,7 +660,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
Pair<VirtualFile, ? extends MoveDestination> sourceRootWithMoveDestination = Pair<VirtualFile, ? extends MoveDestination> sourceRootWithMoveDestination =
selectPackageBasedTargetDirAndDestination(false); selectPackageBasedTargetDirAndDestination(false);
//noinspection ConstantConditions //noinspection ConstantConditions
final MoveDestination moveDestination = sourceRootWithMoveDestination.getSecond(); MoveDestination moveDestination = sourceRootWithMoveDestination.getSecond();
PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceDirectory); PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceDirectory);
String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText(); String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText();
@@ -762,12 +668,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
if (filesExistingInTargetDir.isEmpty() if (filesExistingInTargetDir.isEmpty()
|| (filesExistingInTargetDir.size() == 1 && sourceFiles.contains(filesExistingInTargetDir.get(0)))) { || (filesExistingInTargetDir.size() == 1 && sourceFiles.contains(filesExistingInTargetDir.get(0)))) {
PsiDirectory targetDirectory = ApplicationUtilsKt.runWriteAction( PsiDirectory targetDirectory = ApplicationUtilsKt.runWriteAction(
new Function0<PsiDirectory>() { () -> moveDestination.getTargetDirectory(sourceDirectory)
@Override
public PsiDirectory invoke() {
return moveDestination.getTargetDirectory(sourceDirectory);
}
}
); );
for (KtFile sourceFile : sourceFiles) { for (KtFile sourceFile : sourceFiles) {
@@ -809,7 +710,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor( MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor(
myProject, myProject,
MoveSource(elementsToMove), MoveKotlinDeclarationsProcessorKt.MoveSource(elementsToMove),
target, target,
MoveDeclarationsDelegate.TopLevel.INSTANCE, MoveDeclarationsDelegate.TopLevel.INSTANCE,
isSearchInComments(), isSearchInComments(),
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
@@ -31,10 +20,14 @@ class FqNameFixingMoveJavaFileHandler : MoveFileHandler() {
private val delegate = MoveJavaFileHandler() private val delegate = MoveJavaFileHandler()
override fun canProcessElement(element: PsiFile) = override fun canProcessElement(element: PsiFile) =
delegate.canProcessElement(element) delegate.canProcessElement(element)
override fun findUsages(psiFile: PsiFile, newParent: PsiDirectory?, searchInComments: Boolean, searchInNonJavaFiles: Boolean) = override fun findUsages(
delegate.findUsages(psiFile, newParent, searchInComments, searchInNonJavaFiles) psiFile: PsiFile,
newParent: PsiDirectory?,
searchInComments: Boolean,
searchInNonJavaFiles: Boolean
): MutableList<UsageInfo>? = delegate.findUsages(psiFile, newParent, searchInComments, searchInNonJavaFiles)
override fun prepareMovedFile(file: PsiFile, moveDestination: PsiDirectory, oldToNewMap: MutableMap<PsiElement, PsiElement>) { override fun prepareMovedFile(file: PsiFile, moveDestination: PsiDirectory, oldToNewMap: MutableMap<PsiElement, PsiElement>) {
delegate.prepareMovedFile(file, moveDestination, oldToNewMap) delegate.prepareMovedFile(file, moveDestination, oldToNewMap)
@@ -47,8 +40,8 @@ class FqNameFixingMoveJavaFileHandler : MoveFileHandler() {
} }
override fun updateMovedFile(file: PsiFile) = override fun updateMovedFile(file: PsiFile) =
delegate.updateMovedFile(file) delegate.updateMovedFile(file)
override fun retargetUsages(usageInfos: MutableList<UsageInfo>, oldToNewMap: MutableMap<PsiElement, PsiElement>) = override fun retargetUsages(usageInfos: MutableList<UsageInfo>, oldToNewMap: MutableMap<PsiElement, PsiElement>) =
delegate.retargetUsages(usageInfos, oldToNewMap) delegate.retargetUsages(usageInfos, oldToNewMap)
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
@@ -40,14 +29,14 @@ import java.util.*
class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() { class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
private data class FileUsagesWrapper( private data class FileUsagesWrapper(
val psiFile: KtFile, val psiFile: KtFile,
val usages: List<UsageInfo>, val usages: List<UsageInfo>,
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor? val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
) : UsageInfo(psiFile) ) : UsageInfo(psiFile)
private class MoveContext( private class MoveContext(
val newParent: PsiDirectory, val newParent: PsiDirectory,
val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor? val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
) )
private val fileHandler = MoveKotlinFileHandler() private val fileHandler = MoveKotlinFileHandler()
@@ -62,23 +51,24 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
} }
override fun findUsages( override fun findUsages(
filesToMove: MutableCollection<PsiFile>, filesToMove: MutableCollection<PsiFile>,
directoriesToMove: Array<out PsiDirectory>, directoriesToMove: Array<out PsiDirectory>,
result: MutableCollection<UsageInfo>, result: MutableCollection<UsageInfo>,
searchInComments: Boolean, searchInComments: Boolean,
searchInNonJavaFiles: Boolean, searchInNonJavaFiles: Boolean,
project: Project) { project: Project
) {
filesToMove filesToMove
.filterIsInstance<KtFile>() .filterIsInstance<KtFile>()
.mapTo(result) { FileUsagesWrapper(it, fileHandler.findUsages(it, null, false), null) } .mapTo(result) { FileUsagesWrapper(it, fileHandler.findUsages(it, null, false), null) }
} }
override fun preprocessUsages( override fun preprocessUsages(
project: Project, project: Project,
files: MutableSet<PsiFile>, files: MutableSet<PsiFile>,
infos: Array<UsageInfo>, infos: Array<UsageInfo>,
directory: PsiDirectory?, directory: PsiDirectory?,
conflicts: MultiMap<PsiElement, String> conflicts: MultiMap<PsiElement, String>
) { ) {
val psiPackage = directory?.getPackage() ?: return val psiPackage = directory?.getPackage() ?: return
val moveTarget = KotlinDirectoryMoveTarget(FqName(psiPackage.qualifiedName), directory) val moveTarget = KotlinDirectoryMoveTarget(FqName(psiPackage.qualifiedName), directory)
@@ -101,11 +91,11 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
// Actual move logic is implemented in postProcessUsages since usages are not available here // Actual move logic is implemented in postProcessUsages since usages are not available here
override fun move( override fun move(
file: PsiFile, file: PsiFile,
moveDestination: PsiDirectory, moveDestination: PsiDirectory,
oldToNewElementsMapping: MutableMap<PsiElement, PsiElement>, oldToNewElementsMapping: MutableMap<PsiElement, PsiElement>,
movedFiles: MutableList<PsiFile>, movedFiles: MutableList<PsiFile>,
listener: RefactoringElementListener? listener: RefactoringElementListener?
): Boolean { ): Boolean {
if (file !is KtFile) return false if (file !is KtFile) return false
@@ -130,7 +120,7 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
val usagesToProcess = ArrayList<FileUsagesWrapper>() val usagesToProcess = ArrayList<FileUsagesWrapper>()
usages usages
.filterIsInstance<FileUsagesWrapper>() .filterIsInstance<FileUsagesWrapper>()
.forEach body@ { .forEach body@{
val file = it.psiFile val file = it.psiFile
val moveContext = fileToMoveContext[file] ?: return@body val moveContext = fileToMoveContext[file] ?: return@body
@@ -142,8 +132,7 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
usagesToProcess += FileUsagesWrapper(movedFile as KtFile, it.usages, moveDeclarationsProcessor) usagesToProcess += FileUsagesWrapper(movedFile as KtFile, it.usages, moveDeclarationsProcessor)
} }
usagesToProcess.forEach { fileHandler.retargetUsages(it.usages, it.moveDeclarationsProcessor!!) } usagesToProcess.forEach { fileHandler.retargetUsages(it.usages, it.moveDeclarationsProcessor!!) }
} } finally {
finally {
this.fileToMoveContext = null this.fileToMoveContext = null
} }
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
@@ -55,14 +44,20 @@ class KotlinMoveFilesOrDirectoriesHandler : MoveFilesOrDirectoriesHandler() {
if (!(targetContainer == null || targetContainer is PsiDirectory || targetContainer is PsiDirectoryContainer)) return if (!(targetContainer == null || targetContainer is PsiDirectory || targetContainer is PsiDirectoryContainer)) return
moveFilesOrDirectories( moveFilesOrDirectories(
project, project,
adjustForMove(project, elements, targetContainer) ?: return, adjustForMove(project, elements, targetContainer) ?: return,
targetContainer, targetContainer,
callback?.let { MoveCallback { it.refactoringCompleted() } } callback?.let { MoveCallback { it.refactoringCompleted() } }
) )
} }
override fun tryToMove(element: PsiElement, project: Project, dataContext: DataContext?, reference: PsiReference?, editor: Editor?): Boolean { override fun tryToMove(
element: PsiElement,
project: Project,
dataContext: DataContext?,
reference: PsiReference?,
editor: Editor?
): Boolean {
if (element is KtLightClassForFacade) { if (element is KtLightClassForFacade) {
doMove(project, element.files.toTypedArray(), dataContext?.getData(LangDataKeys.TARGET_PSI_ELEMENT), null) doMove(project, element.files.toTypedArray(), dataContext?.getData(LangDataKeys.TARGET_PSI_ELEMENT), null)
return true return true
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
@@ -48,9 +37,9 @@ class MoveKotlinFileHandler : MoveFileHandler() {
// This is special 'PsiElement' whose purpose is to wrap MoveKotlinTopLevelDeclarationsProcessor // This is special 'PsiElement' whose purpose is to wrap MoveKotlinTopLevelDeclarationsProcessor
// so that it can be kept in the usage info list // so that it can be kept in the usage info list
private class MoveContext( private class MoveContext(
val file: PsiFile, val file: PsiFile,
val declarationMoveProcessor: MoveKotlinDeclarationsProcessor val declarationMoveProcessor: MoveKotlinDeclarationsProcessor
): LightElement(file.manager, KotlinLanguage.INSTANCE) { ) : LightElement(file.manager, KotlinLanguage.INSTANCE) {
override fun toString() = "" override fun toString() = ""
} }
@@ -61,12 +50,15 @@ class MoveKotlinFileHandler : MoveFileHandler() {
if (!shouldUpdatePackageDirective) return null if (!shouldUpdatePackageDirective) return null
val oldPackageName = packageFqName val oldPackageName = packageFqName
val newPackage = newParent?.getPackage() ?: return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), val newPackage = newParent?.getPackage() ?: return ContainerChangeInfo(
ContainerInfo.UnknownPackage) ContainerInfo.Package(oldPackageName),
ContainerInfo.UnknownPackage
)
val newPackageName = FqNameUnsafe(newPackage.qualifiedName) val newPackageName = FqNameUnsafe(newPackage.qualifiedName)
if (oldPackageName.asString() == newPackageName.asString() if (oldPackageName.asString() == newPackageName.asString()
&& ModuleUtilCore.findModuleForPsiElement(this) == ModuleUtilCore.findModuleForPsiElement(newParent)) return null && ModuleUtilCore.findModuleForPsiElement(this) == ModuleUtilCore.findModuleForPsiElement(newParent)
) return null
if (!newPackageName.hasIdentifiersOnly()) return null if (!newPackageName.hasIdentifiersOnly()) return null
return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), ContainerInfo.Package(newPackageName.toSafe())) return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), ContainerInfo.Package(newPackageName.toSafe()))
@@ -78,8 +70,7 @@ class MoveKotlinFileHandler : MoveFileHandler() {
val project = psiFile.project val project = psiFile.project
val newPackage = packageNameInfo.newContainer val moveTarget = when (val newPackage = packageNameInfo.newContainer) {
val moveTarget = when (newPackage) {
ContainerInfo.UnknownPackage -> EmptyKotlinMoveTarget ContainerInfo.UnknownPackage -> EmptyKotlinMoveTarget
else -> KotlinMoveTargetForDeferredFile(newPackage.fqName!!, newParent) { else -> KotlinMoveTargetForDeferredFile(newPackage.fqName!!, newParent) {
@@ -108,18 +99,18 @@ class MoveKotlinFileHandler : MoveFileHandler() {
} }
override fun findUsages( override fun findUsages(
psiFile: PsiFile, psiFile: PsiFile,
newParent: PsiDirectory?, newParent: PsiDirectory?,
searchInComments: Boolean, searchInComments: Boolean,
searchInNonJavaFiles: Boolean searchInNonJavaFiles: Boolean
): List<UsageInfo> { ): List<UsageInfo> {
return findUsages(psiFile, newParent, true) return findUsages(psiFile, newParent, true)
} }
fun findUsages( fun findUsages(
psiFile: PsiFile, psiFile: PsiFile,
newParent: PsiDirectory?, newParent: PsiDirectory?,
withConflicts: Boolean withConflicts: Boolean
): List<UsageInfo> { ): List<UsageInfo> {
if (psiFile !is KtFile) return emptyList() if (psiFile !is KtFile) return emptyList()
@@ -149,7 +140,7 @@ class MoveKotlinFileHandler : MoveFileHandler() {
override fun retargetUsages(usageInfos: List<UsageInfo>?, oldToNewMap: Map<PsiElement, PsiElement>) { override fun retargetUsages(usageInfos: List<UsageInfo>?, oldToNewMap: Map<PsiElement, PsiElement>) {
val currentFile = (usageInfos?.firstOrNull() as? FileInfo)?.element val currentFile = (usageInfos?.firstOrNull() as? FileInfo)?.element
val moveContext = oldToNewMap.keys.firstOrNull { it is MoveContext && it.file == currentFile} as? MoveContext ?: return val moveContext = oldToNewMap.keys.firstOrNull { it is MoveContext && it.file == currentFile } as? MoveContext ?: return
retargetUsages(usageInfos, moveContext.declarationMoveProcessor) retargetUsages(usageInfos, moveContext.declarationMoveProcessor)
} }
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2015 JetBrains s.r.o. * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.idea.refactoring.move
@@ -64,11 +53,11 @@ sealed class ContainerInfo {
abstract fun matches(descriptor: DeclarationDescriptor): Boolean abstract fun matches(descriptor: DeclarationDescriptor): Boolean
object UnknownPackage : ContainerInfo() { object UnknownPackage : ContainerInfo() {
override val fqName = null override val fqName: FqName? = null
override fun matches(descriptor: DeclarationDescriptor) = descriptor is PackageViewDescriptor override fun matches(descriptor: DeclarationDescriptor) = descriptor is PackageViewDescriptor
} }
class Package(override val fqName: FqName): ContainerInfo() { class Package(override val fqName: FqName) : ContainerInfo() {
override fun matches(descriptor: DeclarationDescriptor): Boolean { override fun matches(descriptor: DeclarationDescriptor): Boolean {
return descriptor is PackageFragmentDescriptor && descriptor.fqName == fqName return descriptor is PackageFragmentDescriptor && descriptor.fqName == fqName
} }
@@ -100,8 +89,8 @@ fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(containerChangeIn
private typealias UsageInfoFactory = (KtSimpleNameExpression) -> UsageInfo? private typealias UsageInfoFactory = (KtSimpleNameExpression) -> UsageInfo?
fun KtElement.processInternalReferencesToUpdateOnPackageNameChange( fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
containerChangeInfo: ContainerChangeInfo, containerChangeInfo: ContainerChangeInfo,
body: (originalRefExpr: KtSimpleNameExpression, usageFactory: UsageInfoFactory) -> Unit body: (originalRefExpr: KtSimpleNameExpression, usageFactory: UsageInfoFactory) -> Unit
) { ) {
val file = containingFile as? KtFile ?: return val file = containingFile as? KtFile ?: return
@@ -111,8 +100,7 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
val fqName = DescriptorUtils.getFqName(descriptor).let { if (it.isSafe) it.toSafe() else return@isImported false } val fqName = DescriptorUtils.getFqName(descriptor).let { if (it.isSafe) it.toSafe() else return@isImported false }
if (importPaths.any { fqName.isImported(it, false) }) return true if (importPaths.any { fqName.isImported(it, false) }) return true
val containingDescriptor = descriptor.containingDeclaration return when (val containingDescriptor = descriptor.containingDeclaration) {
return when (containingDescriptor) {
is ClassDescriptor, is PackageViewDescriptor -> isImported(containingDescriptor) is ClassDescriptor, is PackageViewDescriptor -> isImported(containingDescriptor)
else -> false else -> false
} }
@@ -138,7 +126,8 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
var result = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return@lazy null var result = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return@lazy null
if (descriptor.isCompanionObject() if (descriptor.isCompanionObject()
&& bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, refExpr] != null) { && bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, refExpr] != null
) {
result = (result as KtObjectDeclaration).containingClassOrObject ?: result result = (result as KtObjectDeclaration).containingClassOrObject ?: result
} }
@@ -150,8 +139,8 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
if (isExtension && containingDescriptor is ClassDescriptor) { if (isExtension && containingDescriptor is ClassDescriptor) {
val dispatchReceiver = refExpr.getResolvedCall(bindingContext)?.dispatchReceiver val dispatchReceiver = refExpr.getResolvedCall(bindingContext)?.dispatchReceiver
val implicitClass = (dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor val implicitClass = (dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor
if (implicitClass?.isCompanionObject ?: false) { if (implicitClass?.isCompanionObject == true) {
return { ImplicitCompanionAsDispatchReceiverUsageInfo(it, implicitClass!!) } return { ImplicitCompanionAsDispatchReceiverUsageInfo(it, implicitClass) }
} }
if (dispatchReceiver != null || containingDescriptor.kind != ClassKind.OBJECT) return null if (dispatchReceiver != null || containingDescriptor.kind != ClassKind.OBJECT) return null
} }
@@ -159,8 +148,9 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
if (!isExtension) { if (!isExtension) {
if (!(containingDescriptor is PackageFragmentDescriptor if (!(containingDescriptor is PackageFragmentDescriptor
|| containingDescriptor is ClassDescriptor && containingDescriptor.kind == ClassKind.OBJECT || containingDescriptor is ClassDescriptor && containingDescriptor.kind == ClassKind.OBJECT
|| descriptor is JavaCallableMemberDescriptor && ((declaration as? PsiMember)?.hasModifierProperty(PsiModifier.STATIC) ?: false))) return null || descriptor is JavaCallableMemberDescriptor && ((declaration as? PsiMember)?.hasModifierProperty(PsiModifier.STATIC) == true))
) return null
} }
} }
@@ -169,15 +159,15 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
val (oldContainer, newContainer) = containerChangeInfo val (oldContainer, newContainer) = containerChangeInfo
val containerFqName = descriptor val containerFqName = descriptor
.parents .parents
.mapNotNull { .mapNotNull {
when { when {
oldContainer.matches(it) -> oldContainer.fqName oldContainer.matches(it) -> oldContainer.fqName
newContainer.matches(it) -> newContainer.fqName newContainer.matches(it) -> newContainer.fqName
else -> null else -> null
}
} }
.firstOrNull() }
.firstOrNull()
val isImported = isImported(descriptor) val isImported = isImported(descriptor)
if (isImported && this is KtFile) return null if (isImported && this is KtFile) return null
@@ -207,9 +197,9 @@ internal fun markInternalUsages(usages: Collection<UsageInfo>) {
} }
internal fun restoreInternalUsages( internal fun restoreInternalUsages(
scope: KtElement, scope: KtElement,
oldToNewElementsMapping: Map<PsiElement, PsiElement>, oldToNewElementsMapping: Map<PsiElement, PsiElement>,
forcedRestore: Boolean = false forcedRestore: Boolean = false
): List<UsageInfo> { ): List<UsageInfo> {
return scope.collectDescendantsOfType<KtSimpleNameExpression>().mapNotNull { return scope.collectDescendantsOfType<KtSimpleNameExpression>().mapNotNull {
val usageInfo = it.internalUsageInfo val usageInfo = it.internalUsageInfo
@@ -226,8 +216,8 @@ internal fun cleanUpInternalUsages(usages: Collection<UsageInfo>) {
} }
class ImplicitCompanionAsDispatchReceiverUsageInfo( class ImplicitCompanionAsDispatchReceiverUsageInfo(
callee: KtSimpleNameExpression, callee: KtSimpleNameExpression,
val companionDescriptor: ClassDescriptor val companionDescriptor: ClassDescriptor
) : UsageInfo(callee) ) : UsageInfo(callee)
interface KotlinMoveUsage { interface KotlinMoveUsage {
@@ -237,25 +227,46 @@ interface KotlinMoveUsage {
} }
class UnqualifiableMoveRenameUsageInfo( class UnqualifiableMoveRenameUsageInfo(
element: PsiElement, element: PsiElement,
reference: PsiReference, reference: PsiReference,
referencedElement: PsiElement, referencedElement: PsiElement,
val originalFile: PsiFile, val originalFile: PsiFile,
val addImportToOriginalFile: Boolean, val addImportToOriginalFile: Boolean,
override val isInternal: Boolean override val isInternal: Boolean
): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), KotlinMoveUsage { ) : MoveRenameUsageInfo(
element,
reference,
reference.rangeInElement.startOffset,
reference.rangeInElement.endOffset,
referencedElement,
false
), KotlinMoveUsage {
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? { override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
return UnqualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, isInternal) return UnqualifiableMoveRenameUsageInfo(
refExpr,
refExpr.mainReference,
referencedElement,
originalFile,
addImportToOriginalFile,
isInternal
)
} }
} }
class QualifiableMoveRenameUsageInfo( class QualifiableMoveRenameUsageInfo(
element: PsiElement, element: PsiElement,
reference: PsiReference, reference: PsiReference,
referencedElement: PsiElement, referencedElement: PsiElement,
override val isInternal: Boolean override val isInternal: Boolean
): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), ) : MoveRenameUsageInfo(
KotlinMoveUsage { element,
reference,
reference.rangeInElement.startOffset,
reference.rangeInElement.endOffset,
referencedElement,
false
),
KotlinMoveUsage {
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? { override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, isInternal) return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, isInternal)
} }
@@ -272,9 +283,23 @@ class CallableReferenceMoveRenameUsageInfo(
val originalFile: PsiFile, val originalFile: PsiFile,
val addImportToOriginalFile: Boolean, val addImportToOriginalFile: Boolean,
override val isInternal: Boolean override val isInternal: Boolean
) : MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), DeferredKotlinMoveUsage { ) : MoveRenameUsageInfo(
element,
reference,
reference.rangeInElement.startOffset,
reference.rangeInElement.endOffset,
referencedElement,
false
), DeferredKotlinMoveUsage {
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? { override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
return CallableReferenceMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, isInternal) return CallableReferenceMoveRenameUsageInfo(
refExpr,
refExpr.mainReference,
referencedElement,
originalFile,
addImportToOriginalFile,
isInternal
)
} }
override fun resolve(newElement: PsiElement): UsageInfo? { override fun resolve(newElement: PsiElement): UsageInfo? {
@@ -284,28 +309,35 @@ class CallableReferenceMoveRenameUsageInfo(
val referencedElement = referencedElement ?: return null val referencedElement = referencedElement ?: return null
if (target != null && target.isTopLevelKtOrJavaMember()) { if (target != null && target.isTopLevelKtOrJavaMember()) {
element.getStrictParentOfType<KtCallableReferenceExpression>()?.receiverExpression?.delete() element.getStrictParentOfType<KtCallableReferenceExpression>()?.receiverExpression?.delete()
return UnqualifiableMoveRenameUsageInfo(element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal) return UnqualifiableMoveRenameUsageInfo(
element,
reference,
referencedElement,
element.containingFile!!,
addImportToOriginalFile,
isInternal
)
} }
return QualifiableMoveRenameUsageInfo(element, reference, referencedElement, isInternal) return QualifiableMoveRenameUsageInfo(element, reference, referencedElement, isInternal)
} }
} }
fun createMoveUsageInfoIfPossible( fun createMoveUsageInfoIfPossible(
reference: PsiReference, reference: PsiReference,
referencedElement: PsiElement, referencedElement: PsiElement,
addImportToOriginalFile: Boolean, addImportToOriginalFile: Boolean,
isInternal: Boolean isInternal: Boolean
): UsageInfo? { ): UsageInfo? {
val element = reference.element val element = reference.element
return when (getReferenceKind(reference, referencedElement)) { return when (getReferenceKind(reference, referencedElement)) {
ReferenceKind.QUALIFIABLE -> QualifiableMoveRenameUsageInfo( ReferenceKind.QUALIFIABLE -> QualifiableMoveRenameUsageInfo(
element, reference, referencedElement, isInternal element, reference, referencedElement, isInternal
) )
ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo( ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo(
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
) )
ReferenceKind.CALLABLE_REFERENCE -> CallableReferenceMoveRenameUsageInfo( ReferenceKind.CALLABLE_REFERENCE -> CallableReferenceMoveRenameUsageInfo(
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
) )
else -> null else -> null
} }
@@ -349,14 +381,14 @@ private fun getReferenceKind(reference: PsiReference, referencedElement: PsiElem
private fun isCallableReference(reference: PsiReference): Boolean { private fun isCallableReference(reference: PsiReference): Boolean {
return reference is KtSimpleNameReference return reference is KtSimpleNameReference
&& reference.element.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null && reference.element.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null
} }
fun guessNewFileName(declarationsToMove: Collection<KtNamedDeclaration>): String? { fun guessNewFileName(declarationsToMove: Collection<KtNamedDeclaration>): String? {
if (declarationsToMove.isEmpty()) return null if (declarationsToMove.isEmpty()) return null
val representative = declarationsToMove.singleOrNull() val representative = declarationsToMove.singleOrNull()
?: declarationsToMove.filterIsInstance<KtClassOrObject>().singleOrNull() ?: declarationsToMove.filterIsInstance<KtClassOrObject>().singleOrNull()
representative?.let { return "${it.name}.${KotlinFileType.EXTENSION}" } representative?.let { return "${it.name}.${KotlinFileType.EXTENSION}" }
return declarationsToMove.first().containingFile.name return declarationsToMove.first().containingFile.name
@@ -384,7 +416,8 @@ private fun updateJavaReference(reference: PsiReferenceExpression, oldElement: P
if (newClass != null && reference.qualifierExpression != null) { if (newClass != null && reference.qualifierExpression != null) {
val mockMoveMembersOptions = MockMoveMembersOptions(newClass.qualifiedName, arrayOf(newElement)) val mockMoveMembersOptions = MockMoveMembersOptions(newClass.qualifiedName, arrayOf(newElement))
val moveMembersUsageInfo = MoveMembersProcessor.MoveMembersUsageInfo( val moveMembersUsageInfo = MoveMembersProcessor.MoveMembersUsageInfo(
newElement, reference.element, newClass, reference.qualifierExpression, reference) newElement, reference.element, newClass, reference.qualifierExpression, reference
)
val moveMemberHandler = MoveMemberHandler.EP_NAME.forLanguage(reference.element.language) val moveMemberHandler = MoveMemberHandler.EP_NAME.forLanguage(reference.element.language)
if (moveMemberHandler != null) { if (moveMemberHandler != null) {
moveMemberHandler.changeExternalUsage(mockMoveMembersOptions, moveMembersUsageInfo) moveMemberHandler.changeExternalUsage(mockMoveMembersOptions, moveMembersUsageInfo)
@@ -398,10 +431,10 @@ private fun updateJavaReference(reference: PsiReferenceExpression, oldElement: P
internal fun mapToNewOrThis(e: PsiElement, oldToNewElementsMapping: Map<PsiElement, PsiElement>) = oldToNewElementsMapping[e] ?: e internal fun mapToNewOrThis(e: PsiElement, oldToNewElementsMapping: Map<PsiElement, PsiElement>) = oldToNewElementsMapping[e] ?: e
private fun postProcessMoveUsage( private fun postProcessMoveUsage(
usage: UsageInfo, usage: UsageInfo,
oldToNewElementsMapping: Map<PsiElement, PsiElement>, oldToNewElementsMapping: Map<PsiElement, PsiElement>,
nonCodeUsages: ArrayList<NonCodeUsageInfo>, nonCodeUsages: ArrayList<NonCodeUsageInfo>,
shorteningMode: ShorteningMode shorteningMode: ShorteningMode
) { ) {
if (usage is NonCodeUsageInfo) { if (usage is NonCodeUsageInfo) {
nonCodeUsages.add(usage) nonCodeUsages.add(usage)
@@ -415,12 +448,17 @@ private fun postProcessMoveUsage(
when (usage) { when (usage) {
is DeferredKotlinMoveUsage -> { is DeferredKotlinMoveUsage -> {
val newUsage = usage.resolve(newElement) ?: return val newUsage = usage.resolve(newElement) ?: return
postProcessMoveUsage(newUsage, oldToNewElementsMapping, nonCodeUsages, shorteningMode) postProcessMoveUsage(newUsage, oldToNewElementsMapping, nonCodeUsages, shorteningMode)
} }
is UnqualifiableMoveRenameUsageInfo -> { is UnqualifiableMoveRenameUsageInfo -> {
val file = with(usage) { if (addImportToOriginalFile) originalFile else mapToNewOrThis(originalFile, oldToNewElementsMapping) } as KtFile val file = with(usage) {
if (addImportToOriginalFile) originalFile else mapToNewOrThis(
originalFile,
oldToNewElementsMapping
)
} as KtFile
addDelayedImportRequest(newElement, file) addDelayedImportRequest(newElement, file)
} }
@@ -438,8 +476,7 @@ private fun processReference(reference: PsiReference?, newElement: PsiElement, s
reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> return reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> return
else -> reference?.bindToElement(newElement) else -> reference?.bindToElement(newElement)
} }
} } catch (e: IncorrectOperationException) {
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented // Suppress exception if bindToElement is not implemented
} }
} }
@@ -448,31 +485,31 @@ private fun processReference(reference: PsiReference?, newElement: PsiElement, s
* Perform usage postprocessing and return non-code usages * Perform usage postprocessing and return non-code usages
*/ */
fun postProcessMoveUsages( fun postProcessMoveUsages(
usages: Collection<UsageInfo>, usages: Collection<UsageInfo>,
oldToNewElementsMapping: Map<PsiElement, PsiElement> = Collections.emptyMap(), oldToNewElementsMapping: Map<PsiElement, PsiElement> = Collections.emptyMap(),
shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING
): List<NonCodeUsageInfo> { ): List<NonCodeUsageInfo> {
val sortedUsages = usages.sortedWith( val sortedUsages = usages.sortedWith(
Comparator<UsageInfo> { o1, o2 -> Comparator { o1, o2 ->
val file1 = o1.virtualFile val file1 = o1.virtualFile
val file2 = o2.virtualFile val file2 = o2.virtualFile
if (Comparing.equal(file1, file2)) { if (Comparing.equal(file1, file2)) {
val rangeInElement1 = o1.rangeInElement val rangeInElement1 = o1.rangeInElement
val rangeInElement2 = o2.rangeInElement val rangeInElement2 = o2.rangeInElement
if (rangeInElement1 != null && rangeInElement2 != null) { if (rangeInElement1 != null && rangeInElement2 != null) {
return@Comparator rangeInElement2.startOffset - rangeInElement1.startOffset return@Comparator rangeInElement2.startOffset - rangeInElement1.startOffset
}
return@Comparator 0
} }
if (file1 == null) return@Comparator -1 return@Comparator 0
if (file2 == null) return@Comparator 1
Comparing.compare(file1.path, file2.path)
} }
if (file1 == null) return@Comparator -1
if (file2 == null) return@Comparator 1
Comparing.compare(file1.path, file2.path)
}
) )
val nonCodeUsages = ArrayList<NonCodeUsageInfo>() val nonCodeUsages = ArrayList<NonCodeUsageInfo>()
val progressStep = 1.0/sortedUsages.size val progressStep = 1.0 / sortedUsages.size
val progressIndicator = ProgressManager.getInstance().progressIndicator val progressIndicator = ProgressManager.getInstance().progressIndicator
progressIndicator?.text = "Updating usages..." progressIndicator?.text = "Updating usages..."
usageLoop@ for ((i, usage) in sortedUsages.withIndex()) { usageLoop@ for ((i, usage) in sortedUsages.withIndex()) {
@@ -499,17 +536,17 @@ sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, private val is
} }
class ExplicitThis( class ExplicitThis(
expression: KtThisExpression, expression: KtThisExpression,
isIndirectOuter: Boolean isIndirectOuter: Boolean
) : OuterInstanceReferenceUsageInfo(expression, isIndirectOuter) { ) : OuterInstanceReferenceUsageInfo(expression, isIndirectOuter) {
val expression: KtThisExpression? val expression: KtThisExpression?
get() = element as? KtThisExpression get() = element as? KtThisExpression
} }
class ImplicitReceiver( class ImplicitReceiver(
callElement: KtElement, callElement: KtElement,
isIndirectOuter: Boolean, isIndirectOuter: Boolean,
private val isDoubleReceiver: Boolean private val isDoubleReceiver: Boolean
) : OuterInstanceReferenceUsageInfo(callElement, isIndirectOuter) { ) : OuterInstanceReferenceUsageInfo(callElement, isIndirectOuter) {
val callElement: KtElement? val callElement: KtElement?
get() = element as? KtElement get() = element as? KtElement
@@ -535,7 +572,11 @@ sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, private val is
} }
@JvmOverloads @JvmOverloads
fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boolean, body: (OuterInstanceReferenceUsageInfo) -> Unit = {}): Boolean { fun traverseOuterInstanceReferences(
member: KtNamedDeclaration,
stopAtFirst: Boolean,
body: (OuterInstanceReferenceUsageInfo) -> Unit = {}
): Boolean {
if (member is KtObjectDeclaration || member is KtClass && !member.isInner()) return false if (member is KtObjectDeclaration || member is KtClass && !member.isInner()) return false
val context = member.analyzeWithContent() val context = member.analyzeWithContent()
@@ -543,53 +584,53 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo
val outerClassDescriptor = containingClassOrObject.unsafeResolveToDescriptor() as ClassDescriptor val outerClassDescriptor = containingClassOrObject.unsafeResolveToDescriptor() as ClassDescriptor
var found = false var found = false
member.accept( member.accept(
object : PsiRecursiveElementWalkingVisitor() { object : PsiRecursiveElementWalkingVisitor() {
private fun getOuterInstanceReference(element: PsiElement): OuterInstanceReferenceUsageInfo? { private fun getOuterInstanceReference(element: PsiElement): OuterInstanceReferenceUsageInfo? {
return when (element) { return when (element) {
is KtThisExpression -> { is KtThisExpression -> {
val descriptor = context[BindingContext.REFERENCE_TARGET, element.instanceReference] val descriptor = context[BindingContext.REFERENCE_TARGET, element.instanceReference]
val isIndirect = when { val isIndirect = when {
descriptor == outerClassDescriptor -> false descriptor == outerClassDescriptor -> false
descriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> true descriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> true
else -> return null else -> return null
}
OuterInstanceReferenceUsageInfo.ExplicitThis(element, isIndirect)
} }
is KtSimpleNameExpression -> { OuterInstanceReferenceUsageInfo.ExplicitThis(element, isIndirect)
val resolvedCall = element.getResolvedCall(context) ?: return null }
val dispatchReceiver = resolvedCall.dispatchReceiver as? ImplicitReceiver is KtSimpleNameExpression -> {
val extensionReceiver = resolvedCall.extensionReceiver as? ImplicitReceiver val resolvedCall = element.getResolvedCall(context) ?: return null
var isIndirect = false val dispatchReceiver = resolvedCall.dispatchReceiver as? ImplicitReceiver
val isDoubleReceiver = when (outerClassDescriptor) { val extensionReceiver = resolvedCall.extensionReceiver as? ImplicitReceiver
dispatchReceiver?.declarationDescriptor -> extensionReceiver != null var isIndirect = false
extensionReceiver?.declarationDescriptor -> dispatchReceiver != null val isDoubleReceiver = when (outerClassDescriptor) {
else -> { dispatchReceiver?.declarationDescriptor -> extensionReceiver != null
isIndirect = true extensionReceiver?.declarationDescriptor -> dispatchReceiver != null
when { else -> {
dispatchReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> isIndirect = true
extensionReceiver != null when {
extensionReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> dispatchReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false ->
dispatchReceiver != null extensionReceiver != null
else -> return null extensionReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false ->
} dispatchReceiver != null
else -> return null
} }
} }
OuterInstanceReferenceUsageInfo.ImplicitReceiver(resolvedCall.call.callElement, isIndirect, isDoubleReceiver)
} }
else -> null OuterInstanceReferenceUsageInfo.ImplicitReceiver(resolvedCall.call.callElement, isIndirect, isDoubleReceiver)
} }
} else -> null
override fun visitElement(element: PsiElement) {
getOuterInstanceReference(element)?.let {
body(it)
found = true
if (stopAtFirst) stopWalking()
return
}
super.visitElement(element)
} }
} }
override fun visitElement(element: PsiElement) {
getOuterInstanceReference(element)?.let {
body(it)
found = true
if (stopAtFirst) stopWalking()
return
}
super.visitElement(element)
}
}
) )
return found return found
} }
@@ -24,10 +24,12 @@ import org.jetbrains.kotlin.idea.core.util.toPsiDirectory
import org.jetbrains.kotlin.idea.core.util.toPsiFile import org.jetbrains.kotlin.idea.core.util.toPsiFile
import org.jetbrains.kotlin.idea.jsonUtils.getNullableString import org.jetbrains.kotlin.idea.jsonUtils.getNullableString
import org.jetbrains.kotlin.idea.jsonUtils.getString import org.jetbrains.kotlin.idea.jsonUtils.getString
import org.jetbrains.kotlin.idea.refactoring.* import org.jetbrains.kotlin.idea.refactoring.AbstractMultifileRefactoringTest
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.refactoring.move.changePackage.KotlinChangePackageRefactoring import org.jetbrains.kotlin.idea.refactoring.move.changePackage.KotlinChangePackageRefactoring
import org.jetbrains.kotlin.idea.refactoring.move.moveClassesOrPackages.KotlinAwareDelegatingMoveDestination import org.jetbrains.kotlin.idea.refactoring.move.moveClassesOrPackages.KotlinAwareDelegatingMoveDestination
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.runRefactoringTest
import org.jetbrains.kotlin.idea.search.allScope import org.jetbrains.kotlin.idea.search.allScope
import org.jetbrains.kotlin.idea.search.projectScope import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex
@@ -69,12 +71,12 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val targetPackage = config.getString("targetPackage") val targetPackage = config.getString("targetPackage")
MoveClassesOrPackagesProcessor( MoveClassesOrPackagesProcessor(
mainFile.project, mainFile.project,
classesToMove.toTypedArray(), classesToMove.toTypedArray(),
MultipleRootsMoveDestination(PackageWrapper(mainFile.manager, targetPackage)), MultipleRootsMoveDestination(PackageWrapper(mainFile.manager, targetPackage)),
/* searchInComments = */ false, /* searchInComments = */ false,
/* searchInNonJavaFiles = */ true, /* searchInNonJavaFiles = */ true,
/* moveCallback = */ null /* moveCallback = */ null
).run() ).run()
} }
}, },
@@ -93,22 +95,21 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val moveDestination = if (targetDirectory != null) { val moveDestination = if (targetDirectory != null) {
val targetSourceRoot = ProjectRootManager.getInstance(project).fileIndex.getSourceRootForFile(targetDirectory.virtualFile)!! val targetSourceRoot = ProjectRootManager.getInstance(project).fileIndex.getSourceRootForFile(targetDirectory.virtualFile)!!
KotlinAwareDelegatingMoveDestination( KotlinAwareDelegatingMoveDestination(
AutocreatingSingleSourceRootMoveDestination(targetPackageWrapper, targetSourceRoot), AutocreatingSingleSourceRootMoveDestination(targetPackageWrapper, targetSourceRoot),
targetPackage, targetPackage,
targetDirectory targetDirectory
) )
} } else {
else {
MultipleRootsMoveDestination(targetPackageWrapper) MultipleRootsMoveDestination(targetPackageWrapper)
} }
MoveClassesOrPackagesProcessor( MoveClassesOrPackagesProcessor(
project, project,
arrayOf(sourcePackage), arrayOf(sourcePackage),
moveDestination, moveDestination,
/* searchInComments = */ false, /* searchInComments = */ false,
/* searchInNonJavaFiles = */ true, /* searchInNonJavaFiles = */ true,
/* moveCallback = */ null /* moveCallback = */ null
).run() ).run()
} }
}, },
@@ -121,12 +122,12 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val targetClass = config.getString("targetClass") val targetClass = config.getString("targetClass")
MoveClassToInnerProcessor( MoveClassToInnerProcessor(
project, project,
classesToMove.toTypedArray(), classesToMove.toTypedArray(),
JavaPsiFacade.getInstance(project).findClass(targetClass, project.allScope())!!, JavaPsiFacade.getInstance(project).findClass(targetClass, project.allScope())!!,
/* searchInComments = */ false, /* searchInComments = */ false,
/* searchInNonJavaFiles = */ true, /* searchInNonJavaFiles = */ true,
/* moveCallback = */ null /* moveCallback = */ null
).run() ).run()
} }
}, },
@@ -141,12 +142,12 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val targetPackage = config.getString("targetPackage") val targetPackage = config.getString("targetPackage")
MoveInnerProcessor( MoveInnerProcessor(
project, project,
classToMove, classToMove,
newClassName, newClassName,
outerInstanceParameterName != null, outerInstanceParameterName != null,
outerInstanceParameterName, outerInstanceParameterName,
JavaPsiFacade.getInstance(project).findPackage(targetPackage)!!.directories[0] JavaPsiFacade.getInstance(project).findPackage(targetPackage)!!.directories[0]
).run() ).run()
} }
}, },
@@ -161,29 +162,27 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
ActionRunner.runInsideWriteAction { VfsUtil.createDirectoryIfMissing(rootDir, targetDirPath) } ActionRunner.runInsideWriteAction { VfsUtil.createDirectoryIfMissing(rootDir, targetDirPath) }
val newParent = if (targetPackage != null) { val newParent = if (targetPackage != null) {
JavaPsiFacade.getInstance(project).findPackage(targetPackage)!!.directories[0] JavaPsiFacade.getInstance(project).findPackage(targetPackage)!!.directories[0]
} } else {
else {
rootDir.findFileByRelativePath(targetDirPath)!!.toPsiDirectory(project)!! rootDir.findFileByRelativePath(targetDirPath)!!.toPsiDirectory(project)!!
} }
MoveFilesOrDirectoriesProcessor( MoveFilesOrDirectoriesProcessor(
project, project,
arrayOf(mainFile), arrayOf(mainFile),
newParent, newParent,
/* searchInComments = */ false, /* searchInComments = */ false,
/* searchInNonJavaFiles = */ true, /* searchInNonJavaFiles = */ true,
/* moveCallback = */ null, /* moveCallback = */ null,
/* prepareSuccessfulCallback = */ null /* prepareSuccessfulCallback = */ null
).run() ).run()
} } else {
else {
val targetFile = config.getString("targetFile") val targetFile = config.getString("targetFile")
MoveHandler.doMove( MoveHandler.doMove(
project, project,
arrayOf(mainFile), arrayOf(mainFile),
PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(targetFile)!!)!!, PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(targetFile)!!)!!,
/* dataContext = */ null, /* dataContext = */ null,
/* callback = */ null /* callback = */ null
) )
} }
} }
@@ -199,13 +198,13 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val targetDirPath = config.getString("targetDirectory") val targetDirPath = config.getString("targetDirectory")
val targetDir = rootDir.findFileByRelativePath(targetDirPath)!!.toPsiDirectory(project)!! val targetDir = rootDir.findFileByRelativePath(targetDirPath)!!.toPsiDirectory(project)!!
KotlinAwareMoveFilesOrDirectoriesProcessor( KotlinAwareMoveFilesOrDirectoriesProcessor(
project, project,
elementsToMove, elementsToMove,
targetDir, targetDir,
true, true,
searchInComments = true, searchInComments = true,
searchInNonJavaFiles = true, searchInNonJavaFiles = true,
moveCallback = null moveCallback = null
).run() ).run()
} }
}, },
@@ -251,7 +250,7 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val project = mainFile.project val project = mainFile.project
val sourceDir = rootDir.findFileByRelativePath(config.getString("sourceDir"))!!.toPsiDirectory(project)!! val sourceDir = rootDir.findFileByRelativePath(config.getString("sourceDir"))!!.toPsiDirectory(project)!!
val targetDir = rootDir.findFileByRelativePath(config.getString("targetDir"))!!.toPsiDirectory(project)!! val targetDir = rootDir.findFileByRelativePath(config.getString("targetDir"))!!.toPsiDirectory(project)!!
MoveDirectoryWithClassesProcessor(project, arrayOf(sourceDir), targetDir, true, true, true, {}).run() MoveDirectoryWithClassesProcessor(project, arrayOf(sourceDir), targetDir, true, true, true) {}.run()
} }
}, },
@@ -261,24 +260,24 @@ enum class MoveAction : AbstractMultifileRefactoringTest.RefactoringAction {
val elementToMove = elementsAtCaret.single().getNonStrictParentOfType<KtClassOrObject>()!! val elementToMove = elementsAtCaret.single().getNonStrictParentOfType<KtClassOrObject>()!!
val targetClassName = config.getNullableString("targetClass") val targetClassName = config.getNullableString("targetClass")
val targetClass = val targetClass =
if (targetClassName != null) { if (targetClassName != null) {
KotlinFullClassNameIndex.getInstance().get(targetClassName, project, project.projectScope()).first()!! KotlinFullClassNameIndex.getInstance().get(targetClassName, project, project.projectScope()).first()!!
} } else null
else null val delegate = MoveDeclarationsDelegate.NestedClass(
val delegate = MoveDeclarationsDelegate.NestedClass(config.getNullableString("newName"), config.getNullableString("newName"),
config.getNullableString("outerInstanceParameter")) config.getNullableString("outerInstanceParameter")
)
val moveTarget = val moveTarget =
if (targetClass != null) { if (targetClass != null) {
KotlinMoveTargetForExistingElement(targetClass) KotlinMoveTargetForExistingElement(targetClass)
} } else {
else { val fileName = (delegate.newClassName ?: elementToMove.name!!) + ".kt"
val fileName = (delegate.newClassName ?: elementToMove.name!!) + ".kt" val targetPackageFqName = (mainFile as KtFile).packageFqName
val targetPackageFqName = (mainFile as KtFile).packageFqName val targetDir = mainFile.containingDirectory!!
val targetDir = mainFile.containingDirectory!! KotlinMoveTargetForDeferredFile(targetPackageFqName, targetDir, null) {
KotlinMoveTargetForDeferredFile(targetPackageFqName, targetDir, null) { createKotlinFile(fileName, targetDir, targetPackageFqName.asString())
createKotlinFile(fileName, targetDir, targetPackageFqName.asString())
}
} }
}
val descriptor = MoveDeclarationsDescriptor(project, MoveSource(elementToMove), moveTarget, delegate) val descriptor = MoveDeclarationsDescriptor(project, MoveSource(elementToMove), moveTarget, delegate)
MoveKotlinDeclarationsProcessor(descriptor).run() MoveKotlinDeclarationsProcessor(descriptor).run()
} }