Move: Make minor improvements and refactorings
This commit is contained in:
@@ -401,3 +401,8 @@ fun DeclarationDescriptor.isPublishedApi(): Boolean {
|
||||
val descriptor = if (this is CallableMemberDescriptor) DescriptorUtils.getDirectMember(this) else this
|
||||
return descriptor.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.publishedApi)
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.isAncestorOf(descriptor: DeclarationDescriptor, strict: Boolean): Boolean
|
||||
= DescriptorUtils.isAncestor(this, descriptor, strict)
|
||||
|
||||
fun DeclarationDescriptor.isCompanionObject(): Boolean = DescriptorUtils.isCompanionObject(this)
|
||||
+3
-2
@@ -58,10 +58,11 @@ class MoveMemberOutOfCompanionObjectIntention : SelfTargetingRangeIntention<KtNa
|
||||
}
|
||||
|
||||
if (element is KtClassOrObject) {
|
||||
val moveDescriptor = MoveDeclarationsDescriptor(listOf(element),
|
||||
val moveDescriptor = MoveDeclarationsDescriptor(project,
|
||||
listOf(element),
|
||||
KotlinMoveTargetForExistingElement(targetClass),
|
||||
MoveDeclarationsDelegate.NestedClass())
|
||||
MoveKotlinDeclarationsProcessor(project, moveDescriptor).run()
|
||||
MoveKotlinDeclarationsProcessor(moveDescriptor).run()
|
||||
deleteCompanionIfEmpty()
|
||||
return
|
||||
}
|
||||
|
||||
+3
-3
@@ -46,7 +46,6 @@ import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.OuterInstanceReferenceUsageInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.collectOuterInstanceReferences
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.reportConflictIfAny
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.traverseOuterInstanceReferences
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||
@@ -262,11 +261,12 @@ class MoveMemberToCompanionObjectIntention : SelfTargetingRangeIntention<KtNamed
|
||||
return Mover.Default(originalElement, targetContainer).apply { movedClass = this as KtClassOrObject }
|
||||
}
|
||||
}
|
||||
val moveDescriptor = MoveDeclarationsDescriptor(listOf(element),
|
||||
val moveDescriptor = MoveDeclarationsDescriptor(project,
|
||||
listOf(element),
|
||||
KotlinMoveTargetForCompanion(containingClass),
|
||||
MoveDeclarationsDelegate.NestedClass(null, outerInstanceName),
|
||||
moveCallback = MoveCallback { runTemplateForInstanceParam(movedClass!!, nameSuggestions, editor) })
|
||||
MoveKotlinDeclarationsProcessor(project, moveDescriptor, mover).run()
|
||||
MoveKotlinDeclarationsProcessor(moveDescriptor, mover).run()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.intellij.refactoring.util.RefactoringMessageDialog
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.lazilyProcessInternalReferencesToUpdateOnPackageNameChange
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.processInternalReferencesToUpdateOnPackageNameChange
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -82,7 +82,7 @@ internal fun preProcessInternalUsages(element: KtElement, usages: List<KtElement
|
||||
for (targetPackage in targetPackages) {
|
||||
if (targetPackage == mainFile.packageFqName) continue
|
||||
val packageNameInfo = ContainerChangeInfo(ContainerInfo.Package(mainFile.packageFqName), ContainerInfo.Package(targetPackage))
|
||||
element.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory ->
|
||||
element.processInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory ->
|
||||
val infos =
|
||||
expr.internalUsageInfos
|
||||
?: LinkedHashMap<FqName, (KtSimpleNameExpression) -> UsageInfo?>().apply { expr.internalUsageInfos = this }
|
||||
|
||||
+1
-1
@@ -40,8 +40,8 @@ class KotlinChangePackageRefactoring(val file: KtFile) {
|
||||
val currentFqName = packageDirective.fqName
|
||||
|
||||
val declarationProcessor = MoveKotlinDeclarationsProcessor(
|
||||
project,
|
||||
MoveDeclarationsDescriptor(
|
||||
project = project,
|
||||
elementsToMove = file.declarations.filterIsInstance<KtNamedDeclaration>(),
|
||||
moveTarget = object: KotlinDirectoryBasedMoveTarget {
|
||||
override val targetContainerFqName = newFqName
|
||||
|
||||
+3
-2
@@ -93,7 +93,8 @@ class MoveDeclarationToSeparateFileIntention :
|
||||
val moveTarget = KotlinMoveTargetForDeferredFile(packageName, directory, null) {
|
||||
createKotlinFile(targetFileName, directory, packageName.asString())
|
||||
}
|
||||
val moveOptions = MoveDeclarationsDescriptor(
|
||||
val descriptor = MoveDeclarationsDescriptor(
|
||||
project = project,
|
||||
elementsToMove = listOf(element),
|
||||
moveTarget = moveTarget,
|
||||
delegate = MoveDeclarationsDelegate.TopLevel,
|
||||
@@ -107,7 +108,7 @@ class MoveDeclarationToSeparateFileIntention :
|
||||
}
|
||||
)
|
||||
|
||||
val move = { MoveKotlinDeclarationsProcessor(project, moveOptions).run() }
|
||||
val move = { MoveKotlinDeclarationsProcessor(descriptor).run() }
|
||||
val optimizeImports = { OptimizeImportsProcessor(project, file).run() }
|
||||
|
||||
move.runRefactoringWithPostprocessing(project, MoveKotlinDeclarationsProcessor.REFACTORING_ID, optimizeImports)
|
||||
|
||||
+25
-32
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler
|
||||
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
||||
@@ -35,37 +34,30 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
|
||||
sealed class MoveDeclarationsDelegate {
|
||||
abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo
|
||||
abstract fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo>
|
||||
abstract fun collectConflicts(
|
||||
|
||||
open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
|
||||
|
||||
open fun collectConflicts(
|
||||
descriptor: MoveDeclarationsDescriptor,
|
||||
internalUsages: MutableSet<UsageInfo>,
|
||||
conflicts: MultiMap<PsiElement, String>
|
||||
)
|
||||
abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration)
|
||||
abstract fun preprocessUsages(project: Project, descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>)
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
open fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
|
||||
|
||||
}
|
||||
|
||||
open fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
|
||||
|
||||
}
|
||||
|
||||
object TopLevel : MoveDeclarationsDelegate() {
|
||||
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
|
||||
return ContainerChangeInfo(ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName),
|
||||
ContainerInfo.Package(moveTarget.targetContainerFqName!!))
|
||||
}
|
||||
|
||||
override fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
|
||||
|
||||
override fun collectConflicts(
|
||||
descriptor: MoveDeclarationsDescriptor,
|
||||
internalUsages: MutableSet<UsageInfo>,
|
||||
conflicts: MultiMap<PsiElement, String>
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
|
||||
|
||||
}
|
||||
|
||||
override fun preprocessUsages(project: Project, descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
|
||||
|
||||
val sourcePackage = ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName)
|
||||
val targetPackage = moveTarget.targetContainerFqName?.let { ContainerInfo.Package(it) } ?: ContainerInfo.UnknownPackage
|
||||
return ContainerChangeInfo(sourcePackage, targetPackage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,10 +68,11 @@ sealed class MoveDeclarationsDelegate {
|
||||
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
|
||||
val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!)
|
||||
val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject
|
||||
val newInfo = if (movingToClass) {
|
||||
ContainerInfo.Class(moveTarget.targetContainerFqName!!)
|
||||
} else {
|
||||
ContainerInfo.Package(moveTarget.targetContainerFqName!!)
|
||||
val targetContainerFqName = moveTarget.targetContainerFqName
|
||||
val newInfo = when {
|
||||
targetContainerFqName == null -> ContainerInfo.UnknownPackage
|
||||
movingToClass -> ContainerInfo.Class(targetContainerFqName)
|
||||
else -> ContainerInfo.Package(targetContainerFqName)
|
||||
}
|
||||
return ContainerChangeInfo(originalInfo, newInfo)
|
||||
}
|
||||
@@ -137,9 +130,9 @@ sealed class MoveDeclarationsDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
override fun preprocessUsages(project: Project, descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
|
||||
override fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List<UsageInfo>) {
|
||||
if (outerInstanceParameterName == null) return
|
||||
val psiFactory = KtPsiFactory(project)
|
||||
val psiFactory = KtPsiFactory(descriptor.project)
|
||||
val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName)
|
||||
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass
|
||||
|
||||
|
||||
+34
-38
@@ -86,6 +86,7 @@ interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
|
||||
internal var KtSimpleNameExpression.internalUsageInfo: UsageInfo? by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFO"))
|
||||
|
||||
class MoveDeclarationsDescriptor(
|
||||
val project: Project,
|
||||
val elementsToMove: Collection<KtNamedDeclaration>,
|
||||
val moveTarget: KotlinMoveTarget,
|
||||
val delegate: MoveDeclarationsDelegate,
|
||||
@@ -99,15 +100,35 @@ class MoveDeclarationsDescriptor(
|
||||
|
||||
class ConflictUsageInfo(element: PsiElement, val messages: Collection<String>) : UsageInfo(element)
|
||||
|
||||
private object ElementHashingStrategy : TObjectHashingStrategy<PsiElement> {
|
||||
override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean {
|
||||
if (e1 === e2) return true
|
||||
// Name should be enough to distinguish different light elements based on the same original declaration
|
||||
if (e1 is KtLightElement<*, *> && e2 is KtLightElement<*, *>) {
|
||||
return e1.kotlinOrigin == e2.kotlinOrigin && e1.name == e2.name
|
||||
}
|
||||
return e1 == e2
|
||||
}
|
||||
|
||||
override fun computeHashCode(e: PsiElement?): Int {
|
||||
return when (e) {
|
||||
null -> 0
|
||||
is KtLightElement<*, *> -> (e.kotlinOrigin?.hashCode() ?: 0) * 31 + (e.name?.hashCode() ?: 0)
|
||||
else -> e.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MoveKotlinDeclarationsProcessor(
|
||||
val project: Project,
|
||||
val descriptor: MoveDeclarationsDescriptor,
|
||||
val mover: Mover = Mover.Default) : BaseRefactoringProcessor(project) {
|
||||
val mover: Mover = Mover.Default) : BaseRefactoringProcessor(descriptor.project) {
|
||||
companion object {
|
||||
private val REFACTORING_NAME = "Move declarations"
|
||||
val REFACTORING_ID = "move.kotlin.declarations"
|
||||
}
|
||||
|
||||
val project get() = descriptor.project
|
||||
|
||||
private var nonCodeUsages: Array<NonCodeUsageInfo>? = null
|
||||
private val elementsToMove = descriptor.elementsToMove.filter { e -> e.parent != descriptor.moveTarget.getTargetPsiIfExists(e) }
|
||||
private val kotlinToLightElementsBySourceFile = elementsToMove
|
||||
@@ -132,16 +153,16 @@ class MoveKotlinDeclarationsProcessor(
|
||||
val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: ""
|
||||
|
||||
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableCollection<UsageInfo>) {
|
||||
kotlinToLightElements.values.flatMap { it }.flatMapTo(result) { lightElement ->
|
||||
kotlinToLightElements.values.flatten().flatMapTo(result) { lightElement ->
|
||||
val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
|
||||
|
||||
val foundReferences = HashSet<PsiReference>()
|
||||
val projectScope = lightElement.project.projectScope()
|
||||
val projectScope = project.projectScope()
|
||||
val results = ReferencesSearch
|
||||
.search(lightElement, projectScope, false)
|
||||
.search(lightElement, projectScope)
|
||||
.mapNotNullTo(ArrayList()) { ref ->
|
||||
if (foundReferences.add(ref) && elementsToMove.all { !it.isAncestor(ref.element)}) {
|
||||
createMoveUsageInfoIfPossible(ref, lightElement, true, null)
|
||||
if (foundReferences.add(ref) && elementsToMove.none { it.isAncestor(ref.element)}) {
|
||||
createMoveUsageInfoIfPossible(ref, lightElement, true, false)
|
||||
}
|
||||
else null
|
||||
}
|
||||
@@ -166,7 +187,6 @@ class MoveKotlinDeclarationsProcessor(
|
||||
}
|
||||
}
|
||||
|
||||
val usagesToProcessBeforeMove = LinkedHashSet<UsageInfo>()
|
||||
val usages = ArrayList<UsageInfo>()
|
||||
val conflictChecker = MoveConflictChecker(project, elementsToMove, descriptor.moveTarget, elementsToMove.first())
|
||||
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
|
||||
@@ -196,8 +216,6 @@ class MoveKotlinDeclarationsProcessor(
|
||||
usages += externalUsages
|
||||
}
|
||||
|
||||
descriptor.delegate.collectConflicts(descriptor, usagesToProcessBeforeMove, conflicts)
|
||||
|
||||
return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray())
|
||||
}
|
||||
|
||||
@@ -216,7 +234,7 @@ class MoveKotlinDeclarationsProcessor(
|
||||
}
|
||||
|
||||
val usageList = usages.toList()
|
||||
val (oldInternalUsages, externalUsages) = usageList.partition { (it as? InternableUsage)?.scope != null }
|
||||
val (oldInternalUsages, externalUsages) = usageList.partition { it is KotlinMoveUsage && it.isInternal }
|
||||
val newInternalUsages = ArrayList<UsageInfo>()
|
||||
|
||||
oldInternalUsages.forEach { (it.element as? KtSimpleNameExpression)?.internalUsageInfo = it }
|
||||
@@ -224,28 +242,9 @@ class MoveKotlinDeclarationsProcessor(
|
||||
val usagesToProcess = ArrayList(externalUsages)
|
||||
|
||||
try {
|
||||
descriptor.delegate.preprocessUsages(project, descriptor, usageList)
|
||||
descriptor.delegate.preprocessUsages(descriptor, usageList)
|
||||
|
||||
val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>(
|
||||
object: TObjectHashingStrategy<PsiElement> {
|
||||
override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean {
|
||||
if (e1 === e2) return true
|
||||
// Name should be enough to distinguish different light elements based on the same original declaration
|
||||
if (e1 is KtLightElement<*, *> && e2 is KtLightElement<*, *>) {
|
||||
return e1.kotlinOrigin == e2.kotlinOrigin && e1.name == e2.name
|
||||
}
|
||||
return e1 == e2
|
||||
}
|
||||
|
||||
override fun computeHashCode(e: PsiElement?): Int {
|
||||
return when (e) {
|
||||
null -> 0
|
||||
is KtLightElement<*, *> -> (e.kotlinOrigin?.hashCode() ?: 0) * 31 + (e.name?.hashCode() ?: 0)
|
||||
else -> e.hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>(ElementHashingStrategy)
|
||||
|
||||
val newDeclarations = ArrayList<KtNamedDeclaration>()
|
||||
|
||||
@@ -279,9 +278,9 @@ class MoveKotlinDeclarationsProcessor(
|
||||
val usageInfo = it.internalUsageInfo
|
||||
if (usageInfo?.element != null) return@mapNotNull usageInfo
|
||||
val referencedElement = (usageInfo as? MoveRenameUsageInfo)?.referencedElement ?: return@mapNotNull null
|
||||
val newReferencedElement = oldToNewElementsMapping[referencedElement] ?: referencedElement
|
||||
val newReferencedElement = mapToNewOrThis(referencedElement, oldToNewElementsMapping)
|
||||
if (!newReferencedElement.isValid) return@mapNotNull null
|
||||
(usageInfo as? InternableUsage)?.refresh(it, newReferencedElement)
|
||||
(usageInfo as? KotlinMoveUsage)?.refresh(it, newReferencedElement)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,10 +294,7 @@ class MoveKotlinDeclarationsProcessor(
|
||||
}
|
||||
finally {
|
||||
(newInternalUsages + oldInternalUsages).forEach {
|
||||
val element = it.element as? KtSimpleNameExpression ?: return@forEach
|
||||
if (element.isValid) {
|
||||
element.internalUsageInfo = null
|
||||
}
|
||||
(it.element as? KtSimpleNameExpression)?.internalUsageInfo = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -253,9 +253,9 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
|
||||
KotlinMoveTarget target = new KotlinMoveTargetForExistingElement(targetClass);
|
||||
MoveDeclarationsDelegate.NestedClass delegate = new MoveDeclarationsDelegate.NestedClass();
|
||||
MoveDeclarationsDescriptor descriptor = new MoveDeclarationsDescriptor(
|
||||
elementsToMove, target, delegate, false, false, false, false, moveCallback, openInEditorCheckBox.isSelected()
|
||||
myProject, elementsToMove, target, delegate, false, false, false, false, moveCallback, openInEditorCheckBox.isSelected()
|
||||
);
|
||||
invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, descriptor, Mover.Default.INSTANCE));
|
||||
invokeRefactoring(new MoveKotlinDeclarationsProcessor(descriptor, Mover.Default.INSTANCE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -406,6 +406,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
|
||||
String newClassName = getClassName();
|
||||
MoveDeclarationsDelegate delegate = new MoveDeclarationsDelegate.NestedClass(newClassName, outerInstanceParameterName);
|
||||
MoveDeclarationsDescriptor moveDescriptor = new MoveDeclarationsDescriptor(
|
||||
project,
|
||||
CollectionsKt.listOf(innerClass),
|
||||
moveTarget,
|
||||
delegate,
|
||||
@@ -418,6 +419,6 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
|
||||
);
|
||||
saveOpenInEditorOption();
|
||||
|
||||
invokeRefactoring(new MoveKotlinDeclarationsProcessor(project, moveDescriptor, Mover.Default.INSTANCE));
|
||||
invokeRefactoring(new MoveKotlinDeclarationsProcessor(moveDescriptor, Mover.Default.INSTANCE));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -24,7 +24,6 @@ import com.intellij.openapi.fileTypes.FileTypeManager;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.JavaProjectRootsUtil;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -764,6 +763,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
}
|
||||
|
||||
MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor(
|
||||
myProject,
|
||||
elementsToMove,
|
||||
target,
|
||||
MoveDeclarationsDelegate.TopLevel.INSTANCE,
|
||||
@@ -774,7 +774,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
|
||||
moveCallback,
|
||||
false
|
||||
);
|
||||
invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE));
|
||||
invokeRefactoring(new MoveKotlinDeclarationsProcessor(options, Mover.Default.INSTANCE));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject);
|
||||
|
||||
+5
-3
@@ -18,7 +18,10 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories
|
||||
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.PsiCompiledElement
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.impl.light.LightElement
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
|
||||
@@ -35,7 +38,6 @@ import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import java.util.*
|
||||
|
||||
class MoveKotlinFileHandler : MoveFileHandler() {
|
||||
internal class InternalUsagesWrapper(file: KtFile, val usages: List<UsageInfo>) : UsageInfo(file)
|
||||
@@ -88,8 +90,8 @@ class MoveKotlinFileHandler : MoveFileHandler() {
|
||||
}
|
||||
|
||||
val declarationMoveProcessor = MoveKotlinDeclarationsProcessor(
|
||||
project,
|
||||
MoveDeclarationsDescriptor(
|
||||
project = project,
|
||||
elementsToMove = psiFile.declarations.filterIsInstance<KtNamedDeclaration>(),
|
||||
moveTarget = moveTarget,
|
||||
delegate = MoveDeclarationsDelegate.TopLevel,
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.move
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
|
||||
import com.intellij.refactoring.move.MoveCallback
|
||||
import com.intellij.refactoring.move.MoveHandler
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveFilesWithDeclarationsProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
fun invokeMoveFilesOrDirectoriesRefactoring(
|
||||
moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?,
|
||||
project: Project,
|
||||
elements: Array<out PsiElement>,
|
||||
initialTargetDirectory: PsiDirectory?,
|
||||
moveCallback: MoveCallback?
|
||||
) {
|
||||
fun closeDialog() {
|
||||
moveDialog?.close(DialogWrapper.CANCEL_EXIT_CODE)
|
||||
}
|
||||
|
||||
project.executeCommand(MoveHandler.REFACTORING_NAME) {
|
||||
val selectedDir = if (moveDialog != null) moveDialog.targetDirectory else initialTargetDirectory
|
||||
val updatePackageDirective = (moveDialog as? KotlinAwareMoveFilesOrDirectoriesDialog)?.updatePackageDirective
|
||||
|
||||
try {
|
||||
val choice = if (elements.size > 1 || elements[0] is PsiDirectory) intArrayOf(-1) else null
|
||||
val elementsToMove = elements
|
||||
.filterNot {
|
||||
it is PsiFile
|
||||
&& runWriteAction { CopyFilesOrDirectoriesHandler.checkFileExist(selectedDir, choice, it, it.name, "Move") }
|
||||
}
|
||||
.sortedWith( // process Kotlin files first so that light classes are updated before changing references in Java files
|
||||
java.util.Comparator { o1, o2 ->
|
||||
when {
|
||||
o1 is KtElement && o2 !is KtElement -> -1
|
||||
o1 !is KtElement && o2 is KtElement -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
elementsToMove.forEach {
|
||||
MoveFilesOrDirectoriesUtil.checkMove(it, selectedDir!!)
|
||||
if (it is KtFile && it.isInJavaSourceRoot()) {
|
||||
it.updatePackageDirective = updatePackageDirective
|
||||
}
|
||||
}
|
||||
|
||||
val enableSearchReferences = elements.any { ProjectRootsUtil.isInProjectSource(it) }
|
||||
|
||||
if (elementsToMove.isNotEmpty()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val processor = if (elementsToMove.all { it is KtFile } && selectedDir != null) {
|
||||
MoveFilesWithDeclarationsProcessor(
|
||||
project,
|
||||
elementsToMove as List<KtFile>,
|
||||
selectedDir,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
moveCallback,
|
||||
Runnable(::closeDialog)
|
||||
)
|
||||
}
|
||||
else {
|
||||
MoveFilesOrDirectoriesProcessor(
|
||||
project,
|
||||
elementsToMove.toTypedArray(),
|
||||
selectedDir,
|
||||
enableSearchReferences,
|
||||
false,
|
||||
false,
|
||||
moveCallback,
|
||||
Runnable(::closeDialog)
|
||||
)
|
||||
}
|
||||
processor.run()
|
||||
}
|
||||
else {
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
catch (e: IncorrectOperationException) {
|
||||
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.message, "refactoring.moveFile", project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mostly copied from MoveFilesOrDirectoriesUtil.doMove()
|
||||
fun moveFilesOrDirectories(
|
||||
project: Project,
|
||||
elements: Array<PsiElement>,
|
||||
targetElement: PsiElement?,
|
||||
moveCallback: MoveCallback? = null
|
||||
) {
|
||||
elements.forEach { if (it !is PsiFile && it !is PsiDirectory) throw IllegalArgumentException("unexpected element type: " + it) }
|
||||
|
||||
val targetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, targetElement)
|
||||
if (targetElement != null && targetDirectory == null) return
|
||||
|
||||
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements)
|
||||
|
||||
fun doRun(moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?) {
|
||||
invokeMoveFilesOrDirectoriesRefactoring(moveDialog, project, elements, initialTargetDirectory, moveCallback)
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
doRun(null)
|
||||
return
|
||||
}
|
||||
|
||||
with(KotlinAwareMoveFilesOrDirectoriesDialog(project, ::doRun)) {
|
||||
setData(elements, initialTargetDirectory, "refactoring.moveFile")
|
||||
show()
|
||||
}
|
||||
}
|
||||
@@ -16,24 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.move
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.progress.ProgressManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
|
||||
import com.intellij.refactoring.move.MoveCallback
|
||||
import com.intellij.refactoring.move.MoveHandler
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor
|
||||
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
|
||||
import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions
|
||||
import com.intellij.refactoring.move.moveMembers.MoveMemberHandler
|
||||
import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||
import com.intellij.refactoring.util.MoveRenameUsageInfo
|
||||
import com.intellij.refactoring.util.NonCodeUsageInfo
|
||||
import com.intellij.usageView.UsageInfo
|
||||
@@ -46,19 +35,12 @@ import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.codeInsight.KotlinFileReferencesResolver
|
||||
import org.jetbrains.kotlin.idea.codeInsight.shorten.addDelayedImportRequest
|
||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
|
||||
import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveFilesWithDeclarationsProcessor
|
||||
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
@@ -66,6 +48,8 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isAncestorOf
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
@@ -98,13 +82,15 @@ data class ContainerChangeInfo(val oldContainer: ContainerInfo, val newContainer
|
||||
|
||||
fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo: ContainerChangeInfo): List<UsageInfo> {
|
||||
val usages = ArrayList<UsageInfo>()
|
||||
lazilyProcessInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) }
|
||||
processInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) }
|
||||
return usages
|
||||
}
|
||||
|
||||
fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
private typealias UsageInfoFactory = (KtSimpleNameExpression) -> UsageInfo?
|
||||
|
||||
fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
|
||||
containerChangeInfo: ContainerChangeInfo,
|
||||
body: (originalRefExpr: KtSimpleNameExpression, usageFactory: (KtSimpleNameExpression) -> UsageInfo?) -> Unit
|
||||
body: (originalRefExpr: KtSimpleNameExpression, usageFactory: UsageInfoFactory) -> Unit
|
||||
) {
|
||||
val file = containingFile as? KtFile ?: return
|
||||
|
||||
@@ -121,11 +107,10 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
}
|
||||
}
|
||||
|
||||
fun processReference(refExpr: KtSimpleNameExpression, bindingContext: BindingContext): ((KtSimpleNameExpression) -> UsageInfo?)? {
|
||||
fun processReference(refExpr: KtSimpleNameExpression, bindingContext: BindingContext): (UsageInfoFactory)? {
|
||||
val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, refExpr]?.getImportableDescriptor() ?: return null
|
||||
val containingDescriptor = descriptor.containingDeclaration ?: return null
|
||||
|
||||
var declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null
|
||||
val callableKind = (descriptor as? CallableMemberDescriptor)?.kind
|
||||
if (callableKind != null && callableKind != CallableMemberDescriptor.Kind.DECLARATION) return null
|
||||
|
||||
@@ -135,7 +120,7 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null
|
||||
|
||||
val isCallable = descriptor is CallableDescriptor
|
||||
val isExtension = isCallable && declaration.isExtensionDeclaration()
|
||||
val isExtension = isCallable && (descriptor as CallableDescriptor).extensionReceiverParameter != null
|
||||
val isCallableReference = isCallableReference(refExpr.mainReference)
|
||||
|
||||
if (isCallable) {
|
||||
@@ -143,7 +128,7 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
if (isExtension && containingDescriptor is ClassDescriptor) {
|
||||
val dispatchReceiver = refExpr.getResolvedCall(bindingContext)?.dispatchReceiver
|
||||
val implicitClass = (dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor
|
||||
if (DescriptorUtils.isCompanionObject(implicitClass)) {
|
||||
if (implicitClass?.isCompanionObject ?: false) {
|
||||
return ::ImplicitCompanionAsDispatchReceiverUsageInfo
|
||||
}
|
||||
if (dispatchReceiver != null || containingDescriptor.kind != ClassKind.OBJECT) return null
|
||||
@@ -156,14 +141,14 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration is KtObjectDeclaration
|
||||
&& declaration.isCompanion()
|
||||
var declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null
|
||||
|
||||
if (descriptor.isCompanionObject()
|
||||
&& bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, refExpr] != null) {
|
||||
declaration = declaration.containingClassOrObject ?: declaration
|
||||
declaration = (declaration as KtObjectDeclaration).containingClassOrObject ?: declaration
|
||||
}
|
||||
|
||||
val fqName = DescriptorUtils.getFqName(descriptor)
|
||||
if (!fqName.isSafe) return null
|
||||
if (!DescriptorUtils.getFqName(descriptor).isSafe) return null
|
||||
|
||||
val (oldContainer, newContainer) = containerChangeInfo
|
||||
|
||||
@@ -179,16 +164,15 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
.firstOrNull()
|
||||
|
||||
if (isExtension || containerFqName != null || isImported(descriptor)) return {
|
||||
createMoveUsageInfoIfPossible(it.mainReference, declaration, false, this@lazilyProcessInternalReferencesToUpdateOnPackageNameChange)
|
||||
createMoveUsageInfoIfPossible(it.mainReference, declaration, false, true)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
val referenceToContext = KotlinFileReferencesResolver.resolve(file = file, elements = listOf(this))
|
||||
|
||||
for ((refExpr, bindingContext) in referenceToContext) {
|
||||
if (refExpr !is KtSimpleNameExpression || refExpr.parent is KtThisExpression) continue
|
||||
val bindingContext = analyzeFully()
|
||||
forEachDescendantOfType<KtReferenceExpression> { refExpr ->
|
||||
if (refExpr !is KtSimpleNameExpression || refExpr.parent is KtThisExpression) return@forEachDescendantOfType
|
||||
|
||||
processReference(refExpr, bindingContext)?.let { body(refExpr, it) }
|
||||
}
|
||||
@@ -196,8 +180,8 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
|
||||
|
||||
class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee)
|
||||
|
||||
interface InternableUsage {
|
||||
val scope: PsiElement?
|
||||
interface KotlinMoveUsage {
|
||||
val isInternal: Boolean
|
||||
|
||||
fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo?
|
||||
}
|
||||
@@ -208,10 +192,10 @@ class UnqualifiableMoveRenameUsageInfo(
|
||||
referencedElement: PsiElement,
|
||||
val originalFile: PsiFile,
|
||||
val addImportToOriginalFile: Boolean,
|
||||
override val scope: PsiElement?
|
||||
): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), InternableUsage {
|
||||
override val isInternal: Boolean
|
||||
): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), KotlinMoveUsage {
|
||||
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
|
||||
return UnqualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, scope)
|
||||
return UnqualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, isInternal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,11 +203,11 @@ class QualifiableMoveRenameUsageInfo(
|
||||
element: PsiElement,
|
||||
reference: PsiReference,
|
||||
referencedElement: PsiElement,
|
||||
override val scope: PsiElement?
|
||||
override val isInternal: Boolean
|
||||
): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false),
|
||||
InternableUsage {
|
||||
KotlinMoveUsage {
|
||||
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
|
||||
return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, scope)
|
||||
return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, isInternal)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,17 +215,15 @@ fun createMoveUsageInfoIfPossible(
|
||||
reference: PsiReference,
|
||||
referencedElement: PsiElement,
|
||||
addImportToOriginalFile: Boolean,
|
||||
scope: PsiElement?
|
||||
isInternal: Boolean
|
||||
): UsageInfo? {
|
||||
val element = reference.element
|
||||
if (element.getStrictParentOfType<KtSuperExpression>() != null) return null
|
||||
|
||||
return when (getReferenceKind(reference, referencedElement)) {
|
||||
ReferenceKind.QUALIFIABLE -> QualifiableMoveRenameUsageInfo(
|
||||
element, reference, referencedElement, scope
|
||||
element, reference, referencedElement, isInternal
|
||||
)
|
||||
ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo(
|
||||
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, scope
|
||||
element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
@@ -256,6 +238,8 @@ private enum class ReferenceKind {
|
||||
private fun getReferenceKind(reference: PsiReference, referencedElement: PsiElement): ReferenceKind {
|
||||
val element = reference.element as? KtSimpleNameExpression ?: return ReferenceKind.QUALIFIABLE
|
||||
|
||||
if (element.getStrictParentOfType<KtSuperExpression>() != null) return ReferenceKind.IRRELEVANT
|
||||
|
||||
if ((referencedElement.namedUnwrappedElement as? KtDeclaration)?.isExtensionDeclaration() ?: false
|
||||
&& reference.element.getNonStrictParentOfType<KtImportDirective>() == null) return ReferenceKind.UNQUALIFIABLE
|
||||
|
||||
@@ -315,7 +299,7 @@ private fun updateJavaReference(reference: PsiReferenceExpression, oldElement: P
|
||||
return false
|
||||
}
|
||||
|
||||
private 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(
|
||||
usage: UsageInfo,
|
||||
@@ -337,7 +321,7 @@ private fun postProcessMoveUsage(
|
||||
is MoveRenameUsageInfo -> {
|
||||
val oldElement = usage.referencedElement!!
|
||||
val newElement = mapToNewOrThis(oldElement, oldToNewElementsMapping)
|
||||
val reference = usage.reference ?: (usage.element as? KtSimpleNameExpression)?.mainReference
|
||||
val reference = (usage.element as? KtSimpleNameExpression)?.mainReference ?: usage.reference
|
||||
processReference(reference, newElement, shorteningMode, oldElement)
|
||||
}
|
||||
}
|
||||
@@ -346,7 +330,6 @@ private fun postProcessMoveUsage(
|
||||
private fun processReference(reference: PsiReference?, newElement: PsiElement, shorteningMode: ShorteningMode, oldElement: PsiElement) {
|
||||
try {
|
||||
when {
|
||||
reference is PsiMultiReference -> reference.references.forEach { processReference(it, newElement, shorteningMode, oldElement) }
|
||||
reference is KtSimpleNameReference -> reference.bindToElement(newElement, shorteningMode)
|
||||
reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> return
|
||||
else -> reference?.bindToElement(newElement)
|
||||
@@ -399,115 +382,18 @@ fun postProcessMoveUsages(
|
||||
|
||||
var KtFile.updatePackageDirective: Boolean? by UserDataProperty(Key.create("UPDATE_PACKAGE_DIRECTIVE"))
|
||||
|
||||
fun invokeMoveFilesOrDirectoriesRefactoring(
|
||||
moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?,
|
||||
project: Project,
|
||||
elements: Array<out PsiElement>,
|
||||
initialTargetDirectory: PsiDirectory?,
|
||||
moveCallback: MoveCallback?
|
||||
) {
|
||||
fun closeDialog() {
|
||||
moveDialog?.close(DialogWrapper.CANCEL_EXIT_CODE)
|
||||
}
|
||||
|
||||
project.executeCommand(MoveHandler.REFACTORING_NAME) {
|
||||
val selectedDir = if (moveDialog != null) moveDialog.targetDirectory else initialTargetDirectory
|
||||
val updatePackageDirective = (moveDialog as? KotlinAwareMoveFilesOrDirectoriesDialog)?.updatePackageDirective
|
||||
|
||||
try {
|
||||
val choice = if (elements.size > 1 || elements[0] is PsiDirectory) intArrayOf(-1) else null
|
||||
val elementsToMove = elements
|
||||
.filterNot {
|
||||
it is PsiFile
|
||||
&& runWriteAction { CopyFilesOrDirectoriesHandler.checkFileExist(selectedDir, choice, it, it.name, "Move") }
|
||||
}
|
||||
.sortedWith( // process Kotlin files first so that light classes are updated before changing references in Java files
|
||||
Comparator { o1, o2 ->
|
||||
when {
|
||||
o1 is KtElement && o2 !is KtElement -> -1
|
||||
o1 !is KtElement && o2 is KtElement -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
elementsToMove.forEach {
|
||||
MoveFilesOrDirectoriesUtil.checkMove(it, selectedDir!!)
|
||||
if (it is KtFile && it.isInJavaSourceRoot()) {
|
||||
it.updatePackageDirective = updatePackageDirective
|
||||
}
|
||||
}
|
||||
|
||||
val enableSearchReferences = elements.any { ProjectRootsUtil.isInProjectSource(it) }
|
||||
|
||||
if (elementsToMove.isNotEmpty()) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val processor = if (elementsToMove.all { it is KtFile } && selectedDir != null) {
|
||||
MoveFilesWithDeclarationsProcessor(
|
||||
project,
|
||||
elementsToMove as List<KtFile>,
|
||||
selectedDir,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
moveCallback,
|
||||
Runnable(::closeDialog)
|
||||
)
|
||||
}
|
||||
else {
|
||||
MoveFilesOrDirectoriesProcessor(
|
||||
project,
|
||||
elementsToMove.toTypedArray(),
|
||||
selectedDir,
|
||||
enableSearchReferences,
|
||||
false,
|
||||
false,
|
||||
moveCallback,
|
||||
Runnable(::closeDialog)
|
||||
)
|
||||
}
|
||||
processor.run()
|
||||
}
|
||||
else {
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
catch (e: IncorrectOperationException) {
|
||||
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.message, "refactoring.moveFile", project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mostly copied from MoveFilesOrDirectoriesUtil.doMove()
|
||||
fun moveFilesOrDirectories(
|
||||
project: Project,
|
||||
elements: Array<PsiElement>,
|
||||
targetElement: PsiElement?,
|
||||
moveCallback: MoveCallback? = null
|
||||
) {
|
||||
elements.forEach { if (it !is PsiFile && it !is PsiDirectory) throw IllegalArgumentException("unexpected element type: " + it) }
|
||||
|
||||
val targetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, targetElement)
|
||||
if (targetElement != null && targetDirectory == null) return
|
||||
|
||||
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements)
|
||||
|
||||
fun doRun(moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?) {
|
||||
invokeMoveFilesOrDirectoriesRefactoring(moveDialog, project, elements, initialTargetDirectory, moveCallback)
|
||||
}
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
doRun(null)
|
||||
return
|
||||
}
|
||||
|
||||
with(KotlinAwareMoveFilesOrDirectoriesDialog(project, ::doRun)) {
|
||||
setData(elements, initialTargetDirectory, "refactoring.moveFile")
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirectOuter: Boolean) : UsageInfo(element) {
|
||||
open fun reportConflictIfAny(conflicts: MultiMap<PsiElement, String>): Boolean {
|
||||
val element = element ?: return false
|
||||
|
||||
if (isIndirectOuter) {
|
||||
conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}")
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
class ExplicitThis(
|
||||
expression: KtThisExpression,
|
||||
isIndirectOuter: Boolean
|
||||
@@ -523,6 +409,24 @@ sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirect
|
||||
) : OuterInstanceReferenceUsageInfo(callElement, isIndirectOuter) {
|
||||
val callElement: KtElement?
|
||||
get() = element as? KtElement
|
||||
|
||||
override fun reportConflictIfAny(conflicts: MultiMap<PsiElement, String>): Boolean {
|
||||
if (super.reportConflictIfAny(conflicts)) return true
|
||||
|
||||
val fullCall = callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: return false
|
||||
return when {
|
||||
fullCall is KtQualifiedExpression -> {
|
||||
conflicts.putValue(fullCall, "Qualified call won't be processed: ${fullCall.text}")
|
||||
true
|
||||
}
|
||||
|
||||
isDoubleReceiver -> {
|
||||
conflicts.putValue(fullCall, "Member extension call won't be processed: ${fullCall.text}")
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,7 +446,7 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo
|
||||
val descriptor = context[BindingContext.REFERENCE_TARGET, element.instanceReference]
|
||||
val isIndirect = when {
|
||||
descriptor == outerClassDescriptor -> false
|
||||
DescriptorUtils.isAncestor(descriptor, outerClassDescriptor, true) -> true
|
||||
descriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> true
|
||||
else -> return null
|
||||
}
|
||||
OuterInstanceReferenceUsageInfo.ExplicitThis(element, isIndirect)
|
||||
@@ -552,15 +456,15 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo
|
||||
val dispatchReceiver = resolvedCall.dispatchReceiver as? ImplicitReceiver
|
||||
val extensionReceiver = resolvedCall.extensionReceiver as? ImplicitReceiver
|
||||
var isIndirect = false
|
||||
val isDoubleReceiver = when {
|
||||
dispatchReceiver?.declarationDescriptor == outerClassDescriptor -> extensionReceiver != null
|
||||
extensionReceiver?.declarationDescriptor == outerClassDescriptor -> dispatchReceiver != null
|
||||
val isDoubleReceiver = when (outerClassDescriptor) {
|
||||
dispatchReceiver?.declarationDescriptor -> extensionReceiver != null
|
||||
extensionReceiver?.declarationDescriptor -> dispatchReceiver != null
|
||||
else -> {
|
||||
isIndirect = true
|
||||
when {
|
||||
DescriptorUtils.isAncestor(dispatchReceiver?.declarationDescriptor, outerClassDescriptor, true) ->
|
||||
dispatchReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false ->
|
||||
extensionReceiver != null
|
||||
DescriptorUtils.isAncestor(extensionReceiver?.declarationDescriptor, outerClassDescriptor, true) ->
|
||||
extensionReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false ->
|
||||
dispatchReceiver != null
|
||||
else -> return null
|
||||
}
|
||||
@@ -587,30 +491,7 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo
|
||||
}
|
||||
|
||||
fun collectOuterInstanceReferences(member: KtNamedDeclaration): List<OuterInstanceReferenceUsageInfo> {
|
||||
return SmartList<OuterInstanceReferenceUsageInfo>().apply { traverseOuterInstanceReferences(member, false) { add(it) } }
|
||||
}
|
||||
|
||||
fun OuterInstanceReferenceUsageInfo.reportConflictIfAny(conflicts: MultiMap<PsiElement, String>): Boolean {
|
||||
val element = element ?: return false
|
||||
|
||||
if (isIndirectOuter) {
|
||||
conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}")
|
||||
return true
|
||||
}
|
||||
|
||||
if (this !is OuterInstanceReferenceUsageInfo.ImplicitReceiver) return false
|
||||
|
||||
val fullCall = callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: return false
|
||||
return when {
|
||||
fullCall is KtQualifiedExpression -> {
|
||||
conflicts.putValue(fullCall, "Qualified call won't be processed: ${fullCall.text}")
|
||||
true
|
||||
}
|
||||
|
||||
isDoubleReceiver -> {
|
||||
conflicts.putValue(fullCall, "Call with two implicit receivers won't be processed: ${fullCall.text}")
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
val result = SmartList<OuterInstanceReferenceUsageInfo>()
|
||||
traverseOuterInstanceReferences(member, false) { result += it }
|
||||
return result
|
||||
}
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
Call with two implicit receivers won't be processed: bar()
|
||||
Call with two implicit receivers won't be processed: foo()
|
||||
Member extension call won't be processed: bar()
|
||||
Member extension call won't be processed: foo()
|
||||
Qualified call won't be processed: 1.foo()
|
||||
@@ -295,7 +295,7 @@ enum class MoveAction {
|
||||
|
||||
val moveTarget = config.getNullableString("targetPackage")?.let { packageName ->
|
||||
val targetSourceRootPath = config["targetSourceRoot"]?.asString
|
||||
val packageWrapper = PackageWrapper(mainFile.getManager(), packageName)
|
||||
val packageWrapper = PackageWrapper(mainFile.manager, packageName)
|
||||
val moveDestination: MoveDestination = targetSourceRootPath?.let {
|
||||
AutocreatingSingleSourceRootMoveDestination(packageWrapper, rootDir.findFileByRelativePath(it)!!)
|
||||
} ?: MultipleRootsMoveDestination(packageWrapper)
|
||||
@@ -313,8 +313,8 @@ enum class MoveAction {
|
||||
KotlinMoveTargetForExistingElement(PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(filePath)!!) as KtFile)
|
||||
}
|
||||
|
||||
val options = MoveDeclarationsDescriptor(elementsToMove, moveTarget, MoveDeclarationsDelegate.TopLevel)
|
||||
MoveKotlinDeclarationsProcessor(mainFile.getProject(), options).run()
|
||||
val descriptor = MoveDeclarationsDescriptor(project, elementsToMove, moveTarget, MoveDeclarationsDelegate.TopLevel)
|
||||
MoveKotlinDeclarationsProcessor(descriptor).run()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -357,8 +357,8 @@ enum class MoveAction {
|
||||
createKotlinFile(fileName, targetDir, targetPackageFqName.asString())
|
||||
}
|
||||
}
|
||||
val descriptor = MoveDeclarationsDescriptor(listOf(elementToMove), moveTarget, delegate)
|
||||
MoveKotlinDeclarationsProcessor(project, descriptor).run()
|
||||
val descriptor = MoveDeclarationsDescriptor(project, listOf(elementToMove), moveTarget, delegate)
|
||||
MoveKotlinDeclarationsProcessor(descriptor).run()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user