Move: Nested classes support

#KT-9027 In Progress
This commit is contained in:
Alexey Sedunov
2016-01-12 20:41:02 +03:00
parent 0304bd1dc1
commit d662b02e95
159 changed files with 2588 additions and 197 deletions
+2 -2
View File
@@ -330,7 +330,7 @@
order="before moveJavaFileOrDir"/> order="before moveJavaFileOrDir"/>
<refactoring.moveHandler <refactoring.moveHandler
id="kotlin.moveTopLevelDeclarations" id="kotlin.moveTopLevelDeclarations"
implementation="org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsHandler" implementation="org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsHandler"
order="before kotlin.moveFilesOrDirectories"/> order="before kotlin.moveFilesOrDirectories"/>
<moveFileHandler implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinFileHandler" /> <moveFileHandler implementation="org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinFileHandler" />
<refactoring.moveDirectoryWithClassesHelper <refactoring.moveDirectoryWithClassesHelper
@@ -1059,7 +1059,7 @@
</intentionAction> </intentionAction>
<intentionAction> <intentionAction>
<className>org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention</className> <className>org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention</className>
<category>Kotlin</category> <category>Kotlin</category>
</intentionAction> </intentionAction>
@@ -45,7 +45,8 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
import org.jetbrains.kotlin.idea.refactoring.getQualifiedTypeArgumentList import org.jetbrains.kotlin.idea.refactoring.getQualifiedTypeArgumentList
import org.jetbrains.kotlin.idea.refactoring.move.PackageNameInfo 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.lazilyProcessInternalReferencesToUpdateOnPackageNameChange
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
@@ -167,7 +168,7 @@ class KotlinInlineValHandler : InlineActionHandler() {
val targetPackages = referenceExpressions.mapNotNullTo(LinkedHashSet()) { (it.containingFile as? KtFile)?.packageFqName } val targetPackages = referenceExpressions.mapNotNullTo(LinkedHashSet()) { (it.containingFile as? KtFile)?.packageFqName }
for (targetPackage in targetPackages) { for (targetPackage in targetPackages) {
if (targetPackage == file.packageFqName) continue if (targetPackage == file.packageFqName) continue
val packageNameInfo = PackageNameInfo(file.packageFqName, targetPackage.toUnsafe()) val packageNameInfo = ContainerChangeInfo(ContainerInfo.Package(file.packageFqName), ContainerInfo.Package(targetPackage))
initializer.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory -> initializer.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory ->
val infos = expr.internalUsageInfos val infos = expr.internalUsageInfos
?: LinkedHashMap<FqName, (KtSimpleNameExpression) -> UsageInfo?>() ?: LinkedHashMap<FqName, (KtSimpleNameExpression) -> UsageInfo?>()
@@ -16,47 +16,47 @@
package org.jetbrains.kotlin.idea.refactoring.move package org.jetbrains.kotlin.idea.refactoring.move
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.psi.PsiClass import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtQualifiedExpression import org.jetbrains.kotlin.idea.references.matchesTarget
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import java.util.ArrayList import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import com.intellij.psi.PsiWhiteSpace import java.util.*
import org.jetbrains.kotlin.idea.references.mainReference
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 receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression val receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression ?: return
val outerClassRef = when (receiver) { val outerClassRefExpr = when (receiver) {
is KtCallExpression -> receiver.calleeExpression is KtCallExpression -> receiver.calleeExpression
is KtQualifiedExpression -> receiver.getQualifiedElementSelector() is KtQualifiedExpression -> receiver.getQualifiedElementSelector()
else -> null else -> null
} as? KtSimpleNameExpression } as? KtSimpleNameExpression
if (outerClassRef?.mainReference?.resolve() != outerClass) return val outerClassRef = outerClassRefExpr?.mainReference ?: return
if (!outerClassRef.matchesTarget(outerClass)) return
val outerCall = outerClassRef!!.parent as? KtCallExpression ?: return
val psiFactory = KtPsiFactory(usage.project) val psiFactory = KtPsiFactory(usage.project)
val argumentList = innerCall.valueArgumentList val argumentList = innerCall.valueArgumentList
if (argumentList != null) { if (argumentList != null) {
val newArguments = ArrayList<String>() val newArguments = ArrayList<String>()
newArguments.add(outerCall.text!!) newArguments.add(receiver.text!!)
argumentList.arguments.mapTo(newArguments) { it.text!! } argumentList.arguments.mapTo(newArguments) { it.text!! }
argumentList.replace(psiFactory.createCallArguments(newArguments.joinToString(prefix = "(", postfix = ")"))) argumentList.replace(psiFactory.createCallArguments(newArguments.joinToString(prefix = "(", postfix = ")")))
} }
else { else {
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("(${outerCall.text})"), anchor) innerCall.addAfter(psiFactory.createCallArguments("(${receiver.text})"), anchor)
} }
} }
} }
} }
@@ -18,14 +18,11 @@ package org.jetbrains.kotlin.idea.refactoring.move.changePackage
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.refactoring.PackageWrapper
import org.jetbrains.kotlin.idea.codeInsight.shorten.runWithElementsToShortenIsEmptyIgnored import org.jetbrains.kotlin.idea.codeInsight.shorten.runWithElementsToShortenIsEmptyIgnored
import org.jetbrains.kotlin.idea.refactoring.move.PackageNameInfo import org.jetbrains.kotlin.idea.refactoring.move.ContainerChangeInfo
import org.jetbrains.kotlin.idea.refactoring.move.ContainerInfo
import org.jetbrains.kotlin.idea.refactoring.move.getInternalReferencesToUpdateOnPackageNameChange import org.jetbrains.kotlin.idea.refactoring.move.getInternalReferencesToUpdateOnPackageNameChange
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.KotlinMoveTarget import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsOptions
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.Mover
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -39,26 +36,28 @@ class KotlinChangePackageRefactoring(val file: KtFile) {
val packageDirective = file.packageDirective ?: return val packageDirective = file.packageDirective ?: return
val currentFqName = packageDirective.fqName val currentFqName = packageDirective.fqName
val declarationProcessor = MoveKotlinTopLevelDeclarationsProcessor( val declarationProcessor = MoveKotlinDeclarationsProcessor(
project, project,
MoveKotlinTopLevelDeclarationsOptions( MoveDeclarationsDescriptor(
elementsToMove = file.declarations.filterIsInstance<KtNamedDeclaration>(), elementsToMove = file.declarations.filterIsInstance<KtNamedDeclaration>(),
moveTarget = object: KotlinMoveTarget { moveTarget = object: KotlinMoveTarget {
override val packageWrapper = PackageWrapper(file.manager, newFqName.asString()) override val targetContainerFqName = newFqName
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile as? KtFile
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null override fun getTargetPsiIfExists(originalPsi: PsiElement) = null
override fun verify(file: PsiFile) = null override fun verify(file: PsiFile) = null
}, },
delegate = MoveDeclarationsDelegate.TopLevel,
updateInternalReferences = false updateInternalReferences = false
), ),
Mover.Idle // we don't need to move any declarations physically Mover.Idle // we don't need to move any declarations physically
) )
val declarationUsages = declarationProcessor.findUsages().toList() val declarationUsages = declarationProcessor.findUsages().toList()
val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(PackageNameInfo(currentFqName, newFqName.toUnsafe())) val changeInfo = ContainerChangeInfo(ContainerInfo.Package(currentFqName), ContainerInfo.Package(newFqName))
val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(changeInfo)
project.executeWriteCommand("Change file's package to '${newFqName.asString()}'") { project.executeWriteCommand("Change file's package to '${newFqName.asString()}'") {
packageDirective.fqName = newFqName packageDirective.fqName = newFqName
@@ -14,56 +14,50 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager
import com.intellij.refactoring.PackageWrapper
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.getOrPutNullable import org.jetbrains.kotlin.utils.getOrPutNullable
import java.util.HashMap import java.util.*
interface KotlinMoveTarget { interface KotlinMoveTarget {
val packageWrapper: PackageWrapper? val targetContainerFqName: FqName?
fun getOrCreateTargetPsi(originalPsi: PsiElement): PsiFile? fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement?
fun getTargetPsiIfExists(originalPsi: PsiElement): PsiFile? fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement?
// Check possible errors and return corresponding message, or null if no errors are detected // Check possible errors and return corresponding message, or null if no errors are detected
fun verify(file: PsiFile): String? fun verify(file: PsiFile): String?
} }
object EmptyKotlinMoveTarget: KotlinMoveTarget { object EmptyKotlinMoveTarget: KotlinMoveTarget {
override val packageWrapper: PackageWrapper? get() = null override val targetContainerFqName = null
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null override fun getTargetPsiIfExists(originalPsi: PsiElement) = null
override fun verify(file: PsiFile) = null override fun verify(file: PsiFile) = null
} }
class KotlinMoveTargetForExistingFile(val targetFile: KtFile): KotlinMoveTarget { class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMoveTarget {
override val packageWrapper: PackageWrapper? = targetFile.packageFqName.asString().let { packageName -> override val targetContainerFqName = targetElement.getContainingKtFile().packageFqName
PackageWrapper(PsiManager.getInstance(targetFile.project), packageName)
}
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetFile override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetElement
override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetFile override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetElement
// 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 KotlinMoveTargetForDeferredFile( class KotlinMoveTargetForDeferredFile(
project: Project, override val targetContainerFqName: FqName,
private val packageFqName: FqName, private val createFile: (KtFile) -> KtFile?
private val createFile: (KtFile) -> KtFile?): KotlinMoveTarget { ): KotlinMoveTarget {
private val createdFiles = HashMap<KtFile, KtFile?>() private val createdFiles = HashMap<KtFile, KtFile?>()
override val packageWrapper: PackageWrapper = PackageWrapper(PsiManager.getInstance(project), packageFqName.asString()) override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? {
override fun getOrCreateTargetPsi(originalPsi: PsiElement): PsiFile? {
val originalFile = originalPsi.containingFile as? KtFile ?: return null val originalFile = originalPsi.containingFile as? KtFile ?: return null
return createdFiles.getOrPutNullable(originalFile) { createFile(originalFile) } return createdFiles.getOrPutNullable(originalFile) { createFile(originalFile) }
} }
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.codeInsight.intention.LowPriorityAction import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.codeInsight.navigation.NavigationUtil import com.intellij.codeInsight.navigation.NavigationUtil
@@ -27,7 +27,7 @@ import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
import org.jetbrains.kotlin.idea.quickfix.moveCaret import org.jetbrains.kotlin.idea.quickfix.moveCaret
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -86,12 +86,13 @@ class MoveDeclarationToSeparateFileIntention :
} }
return return
} }
val moveTarget = KotlinMoveTargetForDeferredFile(project, packageName) { val moveTarget = KotlinMoveTargetForDeferredFile(packageName) {
createKotlinFile(targetFileName, directory, packageName.asString()) createKotlinFile(targetFileName, directory, packageName.asString())
} }
val moveOptions = MoveKotlinTopLevelDeclarationsOptions( val moveOptions = MoveDeclarationsDescriptor(
elementsToMove = listOf(element), elementsToMove = listOf(element),
moveTarget = moveTarget, moveTarget = moveTarget,
delegate = MoveDeclarationsDelegate.TopLevel,
searchInCommentsAndStrings = false, searchInCommentsAndStrings = false,
searchInNonCode = false, searchInNonCode = false,
updateInternalReferences = true, updateInternalReferences = true,
@@ -102,6 +103,6 @@ class MoveDeclarationToSeparateFileIntention :
FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset) FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset)
} }
) )
MoveKotlinTopLevelDeclarationsProcessor(project, moveOptions).run() MoveKotlinDeclarationsProcessor(project, moveOptions).run()
} }
} }
@@ -0,0 +1,180 @@
/*
* Copyright 2010-2016 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.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
import com.intellij.usageView.UsageInfo
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
sealed class MoveDeclarationsDelegate {
abstract fun getOriginalContainerFqName(descriptor: MoveDeclarationsDescriptor, originalFile: KtFile): FqName
abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo
abstract fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo>
abstract fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>)
abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration)
abstract fun preprocessUsages(project: Project, usages: List<UsageInfo>)
object TopLevel : MoveDeclarationsDelegate() {
override fun getOriginalContainerFqName(descriptor: MoveDeclarationsDescriptor, originalFile: KtFile) = originalFile.packageFqName
override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo {
return ContainerChangeInfo(ContainerInfo.Package(originalDeclaration.getContainingKtFile().packageFqName),
ContainerInfo.Package(moveTarget.targetContainerFqName!!))
}
override fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> = emptyList()
override fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
}
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
}
override fun preprocessUsages(project: Project, usages: List<UsageInfo>) {
}
}
class NestedClass(
val newClassName: String? = null,
val outerInstanceParameterName: String? = null
) : MoveDeclarationsDelegate() {
override fun getOriginalContainerFqName(descriptor: MoveDeclarationsDescriptor, originalFile: KtFile): FqName {
return descriptor.elementsToMove.first().containingClassOrObject!!.fqName!!
}
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!!)
}
return ContainerChangeInfo(originalInfo, newInfo)
}
override fun findUsages(descriptor: MoveDeclarationsDescriptor): List<UsageInfo> {
val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass ?: return emptyList()
return collectOuterInstanceReferences(classToMove)
}
override fun collectConflicts(usages: MutableList<UsageInfo>, conflicts: MultiMap<PsiElement, String>) {
val usageIterator = usages.iterator()
while (usageIterator.hasNext()) {
val usage = usageIterator.next();
val element = usage.element ?: continue
if (usage is ImplicitCompanionAsDispatchReceiverUsageInfo) {
conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}")
usageIterator.remove()
continue
}
if (usage !is OuterInstanceReferenceUsageInfo) continue
if (usage.isIndirectOuter) {
conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}")
usageIterator.remove()
}
if (usage !is OuterInstanceReferenceUsageInfo.ImplicitReceiver) continue
val fullCall = usage.callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: continue
when {
fullCall is KtQualifiedExpression -> {
conflicts.putValue(
fullCall,
"Qualified call won't be processed: ${fullCall.text}"
)
usageIterator.remove()
}
usage.isDoubleReceiver -> {
conflicts.putValue(
fullCall,
"Call with two implicit receivers won't be processed: ${fullCall.text}"
)
usageIterator.remove()
}
}
}
}
override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
with(originalDeclaration) {
newClassName?.let { setName(it) }
if (this is KtClass) {
if ((descriptor.moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement !is KtClassOrObject) {
if (hasModifier(KtTokens.INNER_KEYWORD)) removeModifier(KtTokens.INNER_KEYWORD)
if (hasModifier(KtTokens.PROTECTED_KEYWORD)) removeModifier(KtTokens.PROTECTED_KEYWORD)
}
if (outerInstanceParameterName != null) {
val type = (containingClassOrObject!!.resolveToDescriptor() as ClassDescriptor).defaultType
val parameter = KtPsiFactory(project)
.createParameter("private val $outerInstanceParameterName: ${IdeDescriptorRenderers.SOURCE_CODE.renderType(type)}")
createPrimaryConstructorParameterListIfAbsent().addParameter(parameter)
}
}
}
}
override fun preprocessUsages(project: Project, usages: List<UsageInfo>) {
if (outerInstanceParameterName == null) return
val psiFactory = KtPsiFactory(project)
val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName)
for (usage in usages) {
val referencedNestedClass = (usage as? MoveRenameUsageInfo)?.referencedElement?.unwrapped as? KtClassOrObject
val outerClass = referencedNestedClass?.containingClassOrObject
val lightOuterClass = outerClass?.toLightClass()
if (lightOuterClass != null) {
MoveInnerClassUsagesHandler.EP_NAME
.forLanguage(usage.element!!.language)
?.correctInnerClassUsage(usage, lightOuterClass)
}
when (usage) {
is OuterInstanceReferenceUsageInfo.ExplicitThis -> {
usage.expression?.replace(newOuterInstanceRef)
}
is OuterInstanceReferenceUsageInfo.ImplicitReceiver -> {
usage.callElement?.let { it.replace(psiFactory.createExpressionByPattern("$0.$1", outerInstanceParameterName, it)) }
}
}
}
}
}
}
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.actionSystem.LangDataKeys import com.intellij.openapi.actionSystem.LangDataKeys
@@ -32,11 +32,11 @@ import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackages
import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.canRefactor import org.jetbrains.kotlin.idea.refactoring.canRefactor
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import java.util.* import java.util.*
class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() { class MoveKotlinDeclarationsHandler : MoveHandlerDelegate() {
private fun getSourceDirectories(elements: Array<out PsiElement>) = elements.mapTo(LinkedHashSet()) { it.containingFile?.parent } private fun getSourceDirectories(elements: Array<out PsiElement>) = elements.mapTo(LinkedHashSet()) { it.containingFile?.parent }
private fun doMoveWithCheck( private fun doMoveWithCheck(
@@ -14,8 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.ide.util.EditorHelper
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
@@ -25,16 +26,17 @@ import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.move.MoveCallback import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesUtil
import com.intellij.refactoring.rename.RenameUtil import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.refactoring.util.RefactoringUIUtil import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.refactoring.util.TextOccurrencesUtil import com.intellij.refactoring.util.TextOccurrencesUtil
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewBundle
import com.intellij.usageView.UsageViewDescriptor import com.intellij.usageView.UsageViewDescriptor
import com.intellij.usageView.UsageViewUtil import com.intellij.usageView.UsageViewUtil
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
import com.intellij.util.VisibilityUtil import com.intellij.util.VisibilityUtil
import com.intellij.util.containers.MultiMap import com.intellij.util.containers.MultiMap
import gnu.trove.THashMap import gnu.trove.THashMap
@@ -54,9 +56,8 @@ import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.search.projectScope import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtModifierListOwner import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.isInsideOf import org.jetbrains.kotlin.psi.psiUtil.isInsideOf
import org.jetbrains.kotlin.psi.psiUtil.isPrivate import org.jetbrains.kotlin.psi.psiUtil.isPrivate
@@ -64,58 +65,64 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.kotlin.utils.keysToMap
import java.util.* import java.util.*
interface Mover: (KtNamedDeclaration, KtFile) -> KtNamedDeclaration { interface Mover: (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
object Default: Mover { object Default : Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetFile: KtFile): KtNamedDeclaration { override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration {
val newElement = targetFile.add(originalElement) as KtNamedDeclaration return when (targetContainer) {
originalElement.deleteSingle() is KtFile -> targetContainer.add(originalElement) as KtNamedDeclaration
return newElement is KtClassOrObject -> targetContainer.addDeclaration(originalElement) as KtNamedDeclaration
else -> error("Unexpected element: ${targetContainer.getElementTextWithContext()}")
}.apply { originalElement.deleteSingle() }
} }
} }
object Idle: Mover { object Idle : Mover {
override fun invoke(originalElement: KtNamedDeclaration, targetFile: KtFile) = originalElement override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement) = originalElement
} }
} }
class MoveKotlinTopLevelDeclarationsOptions( class MoveDeclarationsDescriptor(
val elementsToMove: Collection<KtNamedDeclaration>, val elementsToMove: Collection<KtNamedDeclaration>,
val moveTarget: KotlinMoveTarget, val moveTarget: KotlinMoveTarget,
val delegate: MoveDeclarationsDelegate,
val searchInCommentsAndStrings: Boolean = true, val searchInCommentsAndStrings: Boolean = true,
val searchInNonCode: Boolean = true, val searchInNonCode: Boolean = true,
val updateInternalReferences: Boolean = true, val updateInternalReferences: Boolean = true,
val deleteSourceFiles: Boolean = false, val deleteSourceFiles: Boolean = false,
val moveCallback: MoveCallback? = null val moveCallback: MoveCallback? = null,
val openInEditor: Boolean = false
) )
class MoveKotlinTopLevelDeclarationsProcessor( class MoveKotlinDeclarationsProcessor(
val project: Project, val project: Project,
val options: MoveKotlinTopLevelDeclarationsOptions, val descriptor: MoveDeclarationsDescriptor,
val mover: Mover = Mover.Default) : BaseRefactoringProcessor(project) { val mover: Mover = Mover.Default) : BaseRefactoringProcessor(project) {
companion object { companion object {
private val REFACTORING_NAME: String = KotlinRefactoringBundle.message("refactoring.move.top.level.declarations") private val REFACTORING_NAME: String = KotlinRefactoringBundle.message("refactoring.move.top.level.declarations")
} }
private var nonCodeUsages: Array<NonCodeUsageInfo>? = null private var nonCodeUsages: Array<NonCodeUsageInfo>? = null
private val elementsToMove = options.elementsToMove.filter { e -> e.containingFile != options.moveTarget.getTargetPsiIfExists(e) } private val elementsToMove = descriptor.elementsToMove.filter { e -> e.parent != descriptor.moveTarget.getTargetPsiIfExists(e) }
private val kotlinToLightElementsBySourceFile = elementsToMove private val kotlinToLightElementsBySourceFile = elementsToMove
.groupBy { it.getContainingKtFile() } .groupBy { it.getContainingKtFile() }
.mapValues { it.value.keysToMap { it.toLightElements() } } .mapValues { it.value.keysToMap { it.toLightElements() } }
private val conflicts = MultiMap<PsiElement, String>() private val conflicts = MultiMap<PsiElement, String>()
override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor { override fun createUsageViewDescriptor(usages: Array<out UsageInfo>): UsageViewDescriptor {
return MoveMultipleElementsViewDescriptor( val targetContainerFqName = descriptor.moveTarget.targetContainerFqName?.let {
elementsToMove.toTypedArray(), if (it.isRoot) UsageViewBundle.message("default.package.presentable.name") else it.asString()
MoveClassesOrPackagesUtil.getPackageName(options.moveTarget.packageWrapper) }
) return MoveMultipleElementsViewDescriptor(elementsToMove.toTypedArray(), targetContainerFqName)
} }
private val usagesToProcessBeforeMove = SmartList<UsageInfo>()
public override fun findUsages(): Array<UsageInfo> { public override fun findUsages(): Array<UsageInfo> {
val newPackageName = options.moveTarget.packageWrapper?.qualifiedName ?: "" val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: ""
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableList<UsageInfo>) { fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableList<UsageInfo>) {
kotlinToLightElements.values.flatMap { it }.flatMapTo(result) { lightElement -> kotlinToLightElements.values.flatMap { it }.flatMapTo(result) { lightElement ->
val newFqName = StringUtil.getQualifiedName(newPackageName, lightElement.name) val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
val foundReferences = HashSet<PsiReference>() val foundReferences = HashSet<PsiReference>()
val projectScope = lightElement.project.projectScope() val projectScope = lightElement.project.projectScope()
@@ -133,8 +140,8 @@ class MoveKotlinTopLevelDeclarationsProcessor(
TextOccurrencesUtil.findNonCodeUsages( TextOccurrencesUtil.findNonCodeUsages(
lightElement, lightElement,
name, name,
options.searchInCommentsAndStrings, descriptor.searchInCommentsAndStrings,
options.searchInNonCode, descriptor.searchInNonCode,
newFqName, newFqName,
results results
) )
@@ -169,8 +176,9 @@ class MoveKotlinTopLevelDeclarationsProcessor(
val container = element.getUsageContext() val container = element.getUsageContext()
if (!declarationToContainers.getOrPut(declaration) { HashSet<PsiElement>() }.add(container)) continue if (!declarationToContainers.getOrPut(declaration) { HashSet<PsiElement>() }.add(container)) continue
// todo: ok for now, will be replaced by proper visibility analysis
val currentPackage = element.containingFile?.containingDirectory?.getPackage() val currentPackage = element.containingFile?.containingDirectory?.getPackage()
if (currentPackage?.qualifiedName == newPackageName) continue if (currentPackage?.qualifiedName == newContainerName) continue
conflicts.putValue( conflicts.putValue(
declaration, declaration,
@@ -184,7 +192,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
} }
fun collectConflictsInDeclarations() { fun collectConflictsInDeclarations() {
if (newPackageName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return if (newContainerName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return
val declarationToReferenceTargets = HashMap<KtNamedDeclaration, MutableSet<PsiElement>>() val declarationToReferenceTargets = HashMap<KtNamedDeclaration, MutableSet<PsiElement>>()
for (declaration in elementsToMove) { for (declaration in elementsToMove) {
@@ -207,8 +215,9 @@ class MoveKotlinTopLevelDeclarationsProcessor(
if (!declarationToReferenceTargets.getOrPut(declaration) { HashSet<PsiElement>() }.add(refTarget)) continue if (!declarationToReferenceTargets.getOrPut(declaration) { HashSet<PsiElement>() }.add(refTarget)) continue
// todo: ok for now, will be replaced by proper visibility analysis
val currentPackage = declaration.containingFile?.containingDirectory?.getPackage() val currentPackage = declaration.containingFile?.containingDirectory?.getPackage()
if (currentPackage?.qualifiedName == newPackageName) continue if (currentPackage?.qualifiedName == newContainerName) continue
conflicts.putValue( conflicts.putValue(
declaration, declaration,
@@ -224,14 +233,29 @@ class MoveKotlinTopLevelDeclarationsProcessor(
val usages = ArrayList<UsageInfo>() val usages = ArrayList<UsageInfo>()
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) { for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
// No need to find and process usages if package is not changed kotlinToLightElements.keys.forEach {
if (sourceFile.packageFqName.asString() == newPackageName) return UsageInfo.EMPTY_ARRAY if (descriptor.updateInternalReferences) {
val packageNameInfo = descriptor.delegate.getContainerChangeInfo(it, descriptor.moveTarget)
val (usagesToProcessLater, usagesToProcessEarly) = it
.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
.partition { it is MoveRenameUsageInfoForExtension }
usages.addAll(usagesToProcessLater)
usagesToProcessBeforeMove.addAll(usagesToProcessEarly)
}
}
// No need to find and process usages if package is not changed
val originalContainerName = descriptor.delegate.getOriginalContainerFqName(descriptor, sourceFile).asString()
if (originalContainerName == newContainerName) return UsageInfo.EMPTY_ARRAY
usages += descriptor.delegate.findUsages(descriptor)
collectUsages(kotlinToLightElements, usages) collectUsages(kotlinToLightElements, usages)
collectConflictsInUsages(usages) collectConflictsInUsages(usages)
collectConflictsInDeclarations() collectConflictsInDeclarations()
descriptor.delegate.collectConflicts(usages, conflicts)
} }
descriptor.delegate.collectConflicts(usagesToProcessBeforeMove, conflicts)
return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray()) return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray())
} }
@@ -241,27 +265,15 @@ class MoveKotlinTopLevelDeclarationsProcessor(
} }
override fun performRefactoring(usages: Array<out UsageInfo>) { override fun performRefactoring(usages: Array<out UsageInfo>) {
fun moveDeclaration( fun moveDeclaration(declaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): KtNamedDeclaration? {
declaration: KtNamedDeclaration,
moveTarget: KotlinMoveTarget,
usagesToProcessAfterMove: MutableList<UsageInfo>
): KtNamedDeclaration? {
val file = declaration.containingFile as? KtFile val file = declaration.containingFile as? KtFile
assert(file != null) { "${declaration.javaClass}: ${declaration.text}" } assert(file != null) { "${declaration.javaClass}: ${declaration.text}" }
val targetFile = moveTarget.getOrCreateTargetPsi(declaration) as? KtFile val targetContainer = moveTarget.getOrCreateTargetPsi(declaration)
?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.text}") ?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.text}")
if (options.updateInternalReferences) { descriptor.delegate.preprocessDeclaration(descriptor, declaration)
val packageNameInfo = PackageNameInfo(file!!.packageFqName, targetFile.packageFqName.toUnsafe()) val newElement = mover(declaration, targetContainer)
val (usagesToProcessLater, usagesToProcessNow) = declaration
.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
.partition { it is MoveRenameUsageInfoForExtension }
postProcessMoveUsages(usagesToProcessNow, shorteningMode = ShorteningMode.NO_SHORTENING)
usagesToProcessAfterMove.addAll(usagesToProcessLater)
}
val newElement = mover(declaration, targetFile)
newElement.addToShorteningWaitSet() newElement.addToShorteningWaitSet()
@@ -271,6 +283,10 @@ class MoveKotlinTopLevelDeclarationsProcessor(
try { try {
val usageList = usages.toArrayList() val usageList = usages.toArrayList()
descriptor.delegate.preprocessUsages(project, usageList)
postProcessMoveUsages(usagesToProcessBeforeMove, shorteningMode = ShorteningMode.NO_SHORTENING)
val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>( val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>(
object: TObjectHashingStrategy<PsiElement> { object: TObjectHashingStrategy<PsiElement> {
override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean { override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean {
@@ -293,7 +309,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
) )
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) { for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
for ((oldDeclaration, oldLightElements) in kotlinToLightElements) { for ((oldDeclaration, oldLightElements) in kotlinToLightElements) {
val newDeclaration = moveDeclaration(oldDeclaration, options.moveTarget, usageList) val newDeclaration = moveDeclaration(oldDeclaration, descriptor.moveTarget)
if (newDeclaration == null) { if (newDeclaration == null) {
for (oldElement in oldLightElements) { for (oldElement in oldLightElements) {
oldToNewElementsMapping[oldElement] = oldElement oldToNewElementsMapping[oldElement] = oldElement
@@ -307,9 +323,13 @@ class MoveKotlinTopLevelDeclarationsProcessor(
for ((oldElement, newElement) in oldLightElements.asSequence().zip(newDeclaration.toLightElements().asSequence())) { for ((oldElement, newElement) in oldLightElements.asSequence().zip(newDeclaration.toLightElements().asSequence())) {
oldToNewElementsMapping[oldElement] = newElement oldToNewElementsMapping[oldElement] = newElement
} }
if (descriptor.openInEditor) {
EditorHelper.openInEditor(newDeclaration)
}
} }
if (options.deleteSourceFiles) { if (descriptor.deleteSourceFiles) {
sourceFile.delete() sourceFile.delete()
} }
} }
@@ -324,7 +344,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
override fun performPsiSpoilingRefactoring() { override fun performPsiSpoilingRefactoring() {
nonCodeUsages?.let { nonCodeUsages -> RenameUtil.renameNonCodeUsages(myProject, nonCodeUsages) } nonCodeUsages?.let { nonCodeUsages -> RenameUtil.renameNonCodeUsages(myProject, nonCodeUsages) }
options.moveCallback?.refactoringCompleted() descriptor.moveCallback?.refactoringCompleted()
} }
fun execute(usages: List<UsageInfo>) { fun execute(usages: List<UsageInfo>) {
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui
import com.intellij.ide.util.DirectoryUtil import com.intellij.ide.util.DirectoryUtil
import com.intellij.ide.util.PropertiesComponent import com.intellij.ide.util.PropertiesComponent
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog"> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.MoveKotlinTopLevelDeclarationsDialog">
<grid id="1a398" binding="mainPanel" layout-manager="BorderLayout" hgap="0" vgap="0"> <grid id="1a398" binding="mainPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
<constraints> <constraints>
<xy x="10" y="10" width="472" height="414"/> <xy x="10" y="10" width="472" height="414"/>
@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui; package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui;
import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentAdapter;
@@ -69,7 +69,7 @@ import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable;
import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt; import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.*; import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*;
import org.jetbrains.kotlin.idea.util.application.ApplicationUtilsKt; 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;
@@ -533,7 +533,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
return new KotlinMoveTargetForDeferredFile( return new KotlinMoveTargetForDeferredFile(
myProject,
new FqName(getTargetPackage()), new FqName(getTargetPackage()),
new Function1<KtFile, KtFile>() { new Function1<KtFile, KtFile>() {
@Override @Override
@@ -556,7 +555,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return null; return null;
} }
return new KotlinMoveTargetForExistingFile(jetFile); return new KotlinMoveTargetForExistingElement(jetFile);
} }
File targetDir = targetFile.getParentFile(); File targetDir = targetFile.getParentFile();
@@ -570,7 +569,6 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
} }
return new KotlinMoveTargetForDeferredFile( return new KotlinMoveTargetForDeferredFile(
myProject,
new FqName(psiPackage.getQualifiedName()), new FqName(psiPackage.getQualifiedName()),
new Function1<KtFile, KtFile>() { new Function1<KtFile, KtFile>() {
@Override @Override
@@ -757,10 +755,18 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
deleteSourceFile = ret == Messages.YES; deleteSourceFile = ret == Messages.YES;
} }
MoveKotlinTopLevelDeclarationsOptions options = new MoveKotlinTopLevelDeclarationsOptions( MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor(
elementsToMove, target, isSearchInComments(), isSearchInNonJavaFiles(), true, deleteSourceFile, moveCallback elementsToMove,
target,
MoveDeclarationsDelegate.TopLevel.INSTANCE,
isSearchInComments(),
isSearchInNonJavaFiles(),
true,
deleteSourceFile,
moveCallback,
false
); );
invokeRefactoring(new MoveKotlinTopLevelDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE)); invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE));
} }
catch (IncorrectOperationException e) { catch (IncorrectOperationException e) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject); CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject);
@@ -27,7 +27,7 @@ import com.intellij.usageView.UsageInfo
import com.intellij.util.Function import com.intellij.util.Function
import org.jetbrains.kotlin.idea.core.getPackage import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish import org.jetbrains.kotlin.idea.refactoring.invokeOnceOnCommandFinish
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveKotlinTopLevelDeclarationsProcessor import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor
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 java.util.ArrayList import java.util.ArrayList
@@ -37,13 +37,13 @@ class KotlinMoveDirectoryWithClassesHelper : MoveDirectoryWithClassesHelper() {
private class FileUsagesWrapper( private class FileUsagesWrapper(
val psiFile: PsiFile, val psiFile: PsiFile,
val usages: List<UsageInfo>, val usages: List<UsageInfo>,
val moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor? val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
) : UsageInfo(psiFile) ) : UsageInfo(psiFile)
private class MoveContext( private class MoveContext(
val newParent: PsiDirectory, val newParent: PsiDirectory,
val internalUsages: MoveKotlinFileHandler.InternalUsagesWrapper, val internalUsages: MoveKotlinFileHandler.InternalUsagesWrapper,
val moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor? val moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor?
) )
private val fileHandler = MoveKotlinFileHandler() private val fileHandler = MoveKotlinFileHandler()
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.idea.core.getPackage
import org.jetbrains.kotlin.idea.core.packageMatchesDirectory import org.jetbrains.kotlin.idea.core.packageMatchesDirectory
import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly
import org.jetbrains.kotlin.idea.refactoring.move.* import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.* import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedDeclaration
@@ -42,48 +42,50 @@ class MoveKotlinFileHandler : MoveFileHandler() {
// so that it can be kept in the transition map // so that it can be kept in the transition map
private class MoveContext( private class MoveContext(
psiManager: PsiManager, psiManager: PsiManager,
val declarationMoveProcessor: MoveKotlinTopLevelDeclarationsProcessor val declarationMoveProcessor: MoveKotlinDeclarationsProcessor
): LightElement(psiManager, KotlinLanguage.INSTANCE) { ): LightElement(psiManager, KotlinLanguage.INSTANCE) {
override fun toString() = "" override fun toString() = ""
} }
private fun KtFile.getPackageNameInfo(newParent: PsiDirectory?, clearUserData: Boolean): PackageNameInfo? { private fun KtFile.getPackageNameInfo(newParent: PsiDirectory?, clearUserData: Boolean): ContainerChangeInfo? {
val shouldUpdatePackageDirective = updatePackageDirective ?: packageMatchesDirectory() val shouldUpdatePackageDirective = updatePackageDirective ?: packageMatchesDirectory()
updatePackageDirective = if (clearUserData) null else shouldUpdatePackageDirective updatePackageDirective = if (clearUserData) null else shouldUpdatePackageDirective
if (!shouldUpdatePackageDirective) return null if (!shouldUpdatePackageDirective) return null
val oldPackageName = packageFqName val oldPackageName = packageFqName
val newPackage = newParent?.getPackage() ?: return PackageNameInfo(oldPackageName, UNKNOWN_PACKAGE_FQ_NAME) val newPackage = newParent?.getPackage() ?: return ContainerChangeInfo(ContainerInfo.Package(oldPackageName),
ContainerInfo.UnknownPackage)
val newPackageName = FqNameUnsafe(newPackage.qualifiedName) val newPackageName = FqNameUnsafe(newPackage.qualifiedName)
if (oldPackageName.asString() == newPackageName.asString()) return null if (oldPackageName.asString() == newPackageName.asString()) return null
if (newPackageName != UNKNOWN_PACKAGE_FQ_NAME && !newPackageName.hasIdentifiersOnly()) return null if (!newPackageName.hasIdentifiersOnly()) return null
return PackageNameInfo(oldPackageName, newPackageName) return ContainerChangeInfo(ContainerInfo.Package(oldPackageName), ContainerInfo.Package(newPackageName.toSafe()))
} }
fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory?): MoveKotlinTopLevelDeclarationsProcessor? { fun initMoveProcessor(psiFile: PsiFile, newParent: PsiDirectory?): MoveKotlinDeclarationsProcessor? {
if (psiFile !is KtFile) return null if (psiFile !is KtFile) return null
val packageNameInfo = psiFile.getPackageNameInfo(newParent, false) ?: return null val packageNameInfo = psiFile.getPackageNameInfo(newParent, false) ?: return null
val project = psiFile.project val project = psiFile.project
val newPackageName = packageNameInfo.newPackageName val newPackage = packageNameInfo.newContainer
val moveTarget = when (newPackageName) { val moveTarget = when (newPackage) {
UNKNOWN_PACKAGE_FQ_NAME -> EmptyKotlinMoveTarget ContainerInfo.UnknownPackage -> EmptyKotlinMoveTarget
else -> KotlinMoveTargetForDeferredFile(project, newPackageName.toSafe()) { else -> KotlinMoveTargetForDeferredFile(newPackage.fqName!!) {
MoveFilesOrDirectoriesUtil.doMoveFile(psiFile, newParent) MoveFilesOrDirectoriesUtil.doMoveFile(psiFile, newParent)
newParent?.findFile(psiFile.name) as? KtFile newParent?.findFile(psiFile.name) as? KtFile
} }
} }
val declarationMoveProcessor = MoveKotlinTopLevelDeclarationsProcessor( val declarationMoveProcessor = MoveKotlinDeclarationsProcessor(
project, project,
MoveKotlinTopLevelDeclarationsOptions( MoveDeclarationsDescriptor(
elementsToMove = psiFile.declarations.filterIsInstance<KtNamedDeclaration>(), elementsToMove = psiFile.declarations.filterIsInstance<KtNamedDeclaration>(),
moveTarget = moveTarget, moveTarget = moveTarget,
delegate = MoveDeclarationsDelegate.TopLevel,
updateInternalReferences = false updateInternalReferences = false
), ),
Mover.Idle Mover.Idle
@@ -126,9 +128,7 @@ class MoveKotlinFileHandler : MoveFileHandler() {
if (file !is KtFile) return if (file !is KtFile) return
val newDirectory = file.parent ?: return val newDirectory = file.parent ?: return
val packageNameInfo = file.getPackageNameInfo(newDirectory, true) ?: return val packageNameInfo = file.getPackageNameInfo(newDirectory, true) ?: return
val newPackageName = packageNameInfo.newPackageName file.packageDirective?.fqName = packageNameInfo.newContainer.fqName!!
assert(newPackageName.isSafe) { newPackageName }
file.packageDirective?.fqName = newPackageName.toSafe()
} }
override fun retargetUsages(usageInfos: List<UsageInfo>?, oldToNewMap: Map<PsiElement, PsiElement>) { override fun retargetUsages(usageInfos: List<UsageInfo>?, oldToNewMap: Map<PsiElement, PsiElement>) {
@@ -136,7 +136,7 @@ class MoveKotlinFileHandler : MoveFileHandler() {
retargetUsages(usageInfos, moveContext.declarationMoveProcessor) retargetUsages(usageInfos, moveContext.declarationMoveProcessor)
} }
fun retargetUsages(usageInfos: List<UsageInfo>?, moveDeclarationsProcessor: MoveKotlinTopLevelDeclarationsProcessor) { fun retargetUsages(usageInfos: List<UsageInfo>?, moveDeclarationsProcessor: MoveKotlinDeclarationsProcessor) {
postProcessMoveUsages(usageInfos?.firstIsInstanceOrNull<InternalUsagesWrapper>()?.usages ?: emptyList()) postProcessMoveUsages(usageInfos?.firstIsInstanceOrNull<InternalUsagesWrapper>()?.usages ?: emptyList())
moveDeclarationsProcessor.project.runWithElementsToShortenIsEmptyIgnored { moveDeclarationsProcessor.project.runWithElementsToShortenIsEmptyIgnored {
usageInfos?.let { moveDeclarationsProcessor.execute(it) } usageInfos?.let { moveDeclarationsProcessor.execute(it) }
@@ -22,6 +22,7 @@ import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.RefactoringSettings import com.intellij.refactoring.RefactoringSettings
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
@@ -36,17 +37,20 @@ import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import com.intellij.util.IncorrectOperationException import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinFileType 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.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.codeInsight.KotlinFileReferencesResolver import org.jetbrains.kotlin.idea.codeInsight.KotlinFileReferencesResolver
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.refactoring.fqName.isImported import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot
import org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
@@ -60,22 +64,48 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils 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.getImportableDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
val UNKNOWN_PACKAGE_FQ_NAME = FqNameUnsafe("org.jetbrains.kotlin.idea.refactoring.move.<unknown-package>") val UNKNOWN_PACKAGE_FQ_NAME = FqNameUnsafe("org.jetbrains.kotlin.idea.refactoring.move.<unknown-package>")
class PackageNameInfo(val oldPackageName: FqName, val newPackageName: FqNameUnsafe) sealed class ContainerInfo() {
abstract val fqName: FqName?
abstract fun matches(descriptor: DeclarationDescriptor): Boolean
fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo: PackageNameInfo): List<UsageInfo> { object UnknownPackage : ContainerInfo() {
override val fqName = null
override fun matches(descriptor: DeclarationDescriptor) = descriptor is PackageViewDescriptor
}
class Package(override val fqName: FqName): ContainerInfo() {
override fun matches(descriptor: DeclarationDescriptor): Boolean {
return descriptor is PackageFragmentDescriptor && descriptor.fqName == fqName
}
}
class Class(override val fqName: FqName) : ContainerInfo() {
override fun matches(descriptor: DeclarationDescriptor): Boolean {
return descriptor is ClassDescriptor && descriptor.importableFqName == fqName
}
}
}
data class ContainerChangeInfo(val oldContainer: ContainerInfo, val newContainer: ContainerInfo)
fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo: ContainerChangeInfo): List<UsageInfo> {
val usages = ArrayList<UsageInfo>() val usages = ArrayList<UsageInfo>()
lazilyProcessInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) } lazilyProcessInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) }
return usages return usages
} }
fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
packageNameInfo: PackageNameInfo, containerChangeInfo: ContainerChangeInfo,
body: (originalRefExpr: KtSimpleNameExpression, usageFactory: (KtSimpleNameExpression) -> UsageInfo?) -> Unit body: (originalRefExpr: KtSimpleNameExpression, usageFactory: (KtSimpleNameExpression) -> UsageInfo?) -> Unit
) { ) {
val file = containingFile as? KtFile ?: return val file = containingFile as? KtFile ?: return
@@ -99,46 +129,61 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null
// Special case for enum entry superclass references (they have empty text and don't need to be processed by the refactoring) // Special case for enum entry superclass references (they have empty text and don't need to be processed by the refactoring)
if (refExpr.textRange.isEmpty) { if (refExpr.textRange.isEmpty) return null
return null
} if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null
val isCallable = descriptor is CallableDescriptor val isCallable = descriptor is CallableDescriptor
val isExtension = isCallable && declaration.isExtensionDeclaration() val isExtension = isCallable && declaration.isExtensionDeclaration()
if (isCallable && !isExtension) { if (isCallable) {
val containingDescriptor = descriptor.containingDeclaration val containingDescriptor = descriptor.containingDeclaration
if (refExpr.getReceiverExpression() != null) { if (isExtension && containingDescriptor is ClassDescriptor) {
return fun(refExpr: KtSimpleNameExpression): UsageInfo? { val implicitClass = (refExpr.getResolvedCall(bindingContext)?.dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor
val receiver = refExpr.getReceiverExpression() ?: return null if (DescriptorUtils.isCompanionObject(implicitClass)) {
val receiverRef = receiver.getQualifiedElementSelector() as? KtSimpleNameExpression ?: return null return { ImplicitCompanionAsDispatchReceiverUsageInfo(it) }
if (bindingContext[BindingContext.QUALIFIER, receiverRef] == null) return null
return processReference(receiverRef, bindingContext)?.invoke(receiverRef)
} }
return null
}
if (!isExtension) {
if (refExpr.getReceiverExpression() != null) {
return fun(refExpr: KtSimpleNameExpression): UsageInfo? {
val receiver = refExpr.getReceiverExpression() ?: return null
val receiverRef = receiver.getQualifiedElementSelector() as? KtSimpleNameExpression ?: return null
if (bindingContext[BindingContext.QUALIFIER, receiverRef] == null) return null
return processReference(receiverRef, bindingContext)?.invoke(receiverRef)
}
}
if (!(containingDescriptor is PackageFragmentDescriptor
|| containingDescriptor is ClassDescriptor && containingDescriptor.kind == ClassKind.OBJECT)) return null
} }
if (containingDescriptor !is PackageFragmentDescriptor) return null
} }
val fqName = DescriptorUtils.getFqName(descriptor) val fqName = DescriptorUtils.getFqName(descriptor)
if (!fqName.isSafe) return null if (!fqName.isSafe) return null
val packageName = DescriptorUtils.getParentOfType(descriptor, PackageFragmentDescriptor::class.java, false)?.let { val (oldContainer, newContainer) = containerChangeInfo
DescriptorUtils.getFqName(it).toSafe()
}
val oldPackageName = packageNameInfo.oldPackageName val containerFqName = descriptor
val newPackageName = packageNameInfo.newPackageName .parents
.mapNotNull {
when {
oldContainer.matches(it) -> oldContainer.fqName
newContainer.matches(it) -> newContainer.fqName
else -> null
}
}
.firstOrNull()
fun doCreateUsageInfo(refExpr: KtSimpleNameExpression): UsageInfo? { fun doCreateUsageInfo(refExpr: KtSimpleNameExpression): UsageInfo? {
if (isAncestor(declaration, false)) { if (isAncestor(declaration, false)) {
if (descriptor.importableFqName == null) return null if (descriptor.importableFqName == null) return null
if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null
if (isUnqualifiedExtensionReference(refExpr.mainReference, declaration)) return null if (isUnqualifiedExtensionReference(refExpr.mainReference, declaration)) return null
if (packageName == null || !newPackageName.isSafe) return null if (containerFqName == null || newContainer is ContainerInfo.UnknownPackage) return null
return fqName.asString().let { return fqName.asString().let {
val prefix = packageName.asString() val prefix = containerFqName.asString()
val prefixOffset = it.indexOf(prefix) val prefixOffset = it.indexOf(prefix)
val newFqName = FqName(it.replaceRange(prefixOffset..prefixOffset + prefix.length - 1, newPackageName.asString())) val newFqName = FqName(it.replaceRange(prefixOffset..prefixOffset + prefix.length - 1, newContainer.fqName!!.asString()))
MoveRenameSelfUsageInfo(refExpr.mainReference, declaration, newFqName) MoveRenameSelfUsageInfo(refExpr.mainReference, declaration, newFqName)
} }
} }
@@ -146,11 +191,8 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
return createMoveUsageInfoIfPossible(refExpr.mainReference, declaration, false) return createMoveUsageInfoIfPossible(refExpr.mainReference, declaration, false)
} }
if (!isExtension && if (isExtension || containerFqName != null || isImported(descriptor)) return ::doCreateUsageInfo
packageName != oldPackageName && return null
packageName?.asString() != newPackageName.asString() &&
!isImported(descriptor)) return null
return ::doCreateUsageInfo
} }
val referenceToContext = KotlinFileReferencesResolver.resolve(file = file, elements = listOf(this)) val referenceToContext = KotlinFileReferencesResolver.resolve(file = file, elements = listOf(this))
@@ -163,6 +205,8 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
} }
} }
class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee)
class MoveRenameUsageInfoForExtension( class MoveRenameUsageInfoForExtension(
element: PsiElement, element: PsiElement,
reference: PsiReference, reference: PsiReference,
@@ -276,7 +320,7 @@ fun postProcessMoveUsages(usages: List<UsageInfo>,
val nonCodeUsages = ArrayList<NonCodeUsageInfo>() val nonCodeUsages = ArrayList<NonCodeUsageInfo>()
for (usage in sortedUsages) { usageLoop@ for (usage in sortedUsages) {
when (usage) { when (usage) {
is NonCodeUsageInfo -> { is NonCodeUsageInfo -> {
nonCodeUsages.add(usage) nonCodeUsages.add(usage)
@@ -295,21 +339,17 @@ fun postProcessMoveUsages(usages: List<UsageInfo>,
is MoveRenameUsageInfo -> { is MoveRenameUsageInfo -> {
val oldElement = usage.referencedElement!! val oldElement = usage.referencedElement!!
val newElement = counterpart(oldElement) val newElement = counterpart(oldElement)
usage.reference?.let { val reference = usage.reference ?: (usage.element as? KtSimpleNameExpression)?.mainReference
try { try {
if (it is KtSimpleNameReference) { when {
it.bindToElement(newElement, shorteningMode) reference is KtSimpleNameReference -> reference.bindToElement(newElement, shorteningMode)
} reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> continue@usageLoop
else if (it is PsiReferenceExpression && updateJavaReference(it, oldElement, newElement)) { else -> reference?.bindToElement(newElement)
}
else {
it.bindToElement(newElement)
}
}
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented
} }
} }
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented
}
} }
} }
} }
@@ -388,3 +428,86 @@ fun moveFilesOrDirectories(
show() show()
} }
} }
sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirectOuter: Boolean) : UsageInfo(element) {
class ExplicitThis(
expression: KtThisExpression,
isIndirectOuter: Boolean
) : OuterInstanceReferenceUsageInfo(expression, isIndirectOuter) {
val expression: KtThisExpression?
get() = element as? KtThisExpression
}
class ImplicitReceiver(
callElement: KtElement,
isIndirectOuter: Boolean,
val isDoubleReceiver: Boolean
) : OuterInstanceReferenceUsageInfo(callElement, isIndirectOuter) {
val callElement: KtElement?
get() = element as? KtElement
}
}
@JvmOverloads
fun traverseOuterInstanceReferences(innerClass: KtClass, stopAtFirst: Boolean, body: (OuterInstanceReferenceUsageInfo) -> Unit = {}): Boolean {
if (!innerClass.isInner()) return false
val context = innerClass.analyzeFully()
val innerClassDescriptor = innerClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
val outerClassDescriptor = innerClassDescriptor.containingDeclaration as? ClassDescriptor ?: return false
var found = false
innerClass.accept(
object : PsiRecursiveElementWalkingVisitor() {
private fun getOuterInstanceReference(element: PsiElement): OuterInstanceReferenceUsageInfo? {
return when (element) {
is KtThisExpression -> {
val descriptor = context[BindingContext.REFERENCE_TARGET, element.instanceReference]
val isIndirect = when {
descriptor == outerClassDescriptor -> false
DescriptorUtils.isAncestor(descriptor, outerClassDescriptor, true) -> true
else -> return null
}
OuterInstanceReferenceUsageInfo.ExplicitThis(element, isIndirect)
}
is KtSimpleNameExpression -> {
val resolvedCall = element.getResolvedCall(context) ?: return null
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
else -> {
isIndirect = true
when {
DescriptorUtils.isAncestor(dispatchReceiver?.declarationDescriptor, outerClassDescriptor, true) ->
extensionReceiver != null
DescriptorUtils.isAncestor(extensionReceiver?.declarationDescriptor, outerClassDescriptor, true) ->
dispatchReceiver != null
else -> return 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)
}
}
)
return found
}
fun collectOuterInstanceReferences(innerClass: KtClass): List<OuterInstanceReferenceUsageInfo> {
return SmartList<OuterInstanceReferenceUsageInfo>().apply { traverseOuterInstanceReferences(innerClass, false) { add(it) } }
}
@@ -1,4 +1,4 @@
{ {
"mainFile": "source/test.kt", "mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention" "intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention"
} }
@@ -1,4 +1,4 @@
{ {
"mainFile": "source/test.kt", "mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention" "intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention"
} }
@@ -1,5 +1,5 @@
{ {
"mainFile": "source/test.kt", "mainFile": "source/test.kt",
"intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention", "intentionClass": "org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention",
"isApplicable": "false" "isApplicable": "false"
} }
@@ -0,0 +1,14 @@
package test
class A {
inner class OuterOuterY
inner class B {
inner class C {
fun test() {
OuterOuterY()
this@A.OuterOuterY()
}
}
}
}
@@ -0,0 +1,14 @@
package test
class A {
inner class OuterOuterY
inner class B {
inner class <caret>C {
fun test() {
OuterOuterY()
this@A.OuterOuterY()
}
}
}
}
@@ -0,0 +1,2 @@
Indirect outer instances won't be extracted: OuterOuterY()
Indirect outer instances won't be extracted: this@A
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "a",
"withRuntime": "true"
}
@@ -0,0 +1,26 @@
package test
class C(private val b: A.B) {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
b.OuterY()
b.outerFoo(b.outerBar)
b.OuterY()
b.outerFoo(b.outerBar)
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,42 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class B {
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.C;
class Test {
C foo() {
return new C(new A().new B());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.C;
class Test {
C foo() {
return new C(new A().new B());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.C;
class Test {
C foo() {
return new C(new A().new B());
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.C
fun foo(): C {
return C(A().B())
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.C
fun foo2(): C {
return C(A().B())
}
@@ -0,0 +1,9 @@
package test2
import test.A
import test.A.B
import test.C
fun foo2(): C {
return C(A().B())
}
@@ -0,0 +1,66 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class B {
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
inner class <caret>C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
OuterY()
outerFoo(outerBar)
this@B.OuterY()
this@B.outerFoo(this@B.outerBar)
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B.C foo() {
return new A().new B().new C();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B.C;
class Test {
C foo() {
return new A().new B().new C();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B;
class Test {
B.C foo() {
return new A().new B().new C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B.C {
return A().B().C()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B.C
fun foo2(): C {
return A().B().C()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo2(): B.C {
return A().B().C()
}
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "b",
"withRuntime": "true"
}
@@ -0,0 +1,20 @@
package test
class C {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,36 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class B {
}
}
@@ -0,0 +1,9 @@
package test2;
import test.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.C
fun foo(): C {
return C()
}
@@ -0,0 +1,7 @@
package test2
import test.C
fun foo(): C {
return C()
}
@@ -0,0 +1,8 @@
package test2
import test.A.B
import test.C
fun foo(): C {
return C()
}
@@ -0,0 +1,54 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class B {
class <caret>C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B.C foo() {
return new A.B.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B;
class Test {
B.C foo() {
return new B.C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B.C {
return A.B.C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B.C
fun foo(): C {
return C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B
fun foo(): B.C {
return B.C()
}
@@ -0,0 +1,5 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"withRuntime": "true"
}
@@ -0,0 +1,20 @@
package test
class B {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,34 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo(): B {
return B()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo2(): B {
return B()
}
@@ -0,0 +1,52 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class <caret>B {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B foo() {
return new A().new B();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B {
return A().B()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo2(): B {
return A().B()
}
@@ -0,0 +1,5 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"withRuntime": "true"
}
@@ -0,0 +1,26 @@
package test
class B(private val a: A) {
fun test() {
A.X()
A.Companion.Y()
A.foo(A.bar)
//1.extFoo(1.extBar) // conflict
a.OuterY()
a.outerFoo(a.outerBar)
a.OuterY()
a.outerFoo(a.outerBar)
A.O.Y()
A.O.foo(A.O.bar)
with (A.O) {
A.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
@@ -0,0 +1,40 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new B(new A());
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.B;
class Test {
B foo() {
return new B(new A());
}
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo(): B {
return B(A())
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.B
fun foo2(): B {
return B(A())
}
@@ -0,0 +1,64 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
inner class OuterY
fun outerFoo(n: Int) {}
val outerBar = 1
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
inner class <caret>B {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
OuterY()
outerFoo(outerBar)
this@A.OuterY()
this@A.outerFoo(this@A.outerBar)
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B foo() {
return new A().new B();
}
}
@@ -0,0 +1,10 @@
package test2;
import test.A;
import test.A.B;
class Test {
B foo() {
return new A().new B();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B {
return A().B()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo2(): B {
return A().B()
}
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "a",
"withRuntime": "true"
}
@@ -0,0 +1,19 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A() {
fun X.bar() {}
}
class X {
fun Int.foo() {}
inner class Y {
fun test() {
1.foo()
with(1) { foo() }
with(A()) { bar() }
}
}
}
@@ -0,0 +1,19 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A() {
fun X.bar() {}
}
class X {
fun Int.foo() {}
inner class <caret>Y {
fun test() {
1.foo()
with(1) { foo() }
with(A()) { bar() }
}
}
}
@@ -0,0 +1,3 @@
Call with two implicit receivers won't be processed: bar()
Call with two implicit receivers won't be processed: foo()
Qualified call won't be processed: 1.foo()
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"outerInstanceParameter": "a",
"withRuntime": "true"
}
@@ -0,0 +1,55 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class B {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
}
class C {
fun test() {
B.X()
B.Companion.Y()
B.foo(B.bar)
//1.extFoo(1.extBar) // conflict
B.O.Y()
B.O.foo(B.O.bar)
with (B.O) {
B.Companion.Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.C foo() {
return new A.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.C foo() {
return new A.C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.C {
return A.C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.C
fun foo(): C {
return C()
}
@@ -0,0 +1,8 @@
package test2
import test.A
import test.A.B
fun foo(): A.C {
return A.C()
}
@@ -0,0 +1,54 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class B {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class <caret>C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.B.C foo() {
return new A.B.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B.C;
class Test {
C foo() {
return new C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.B;
class Test {
B.C foo() {
return new B.C();
}
}
@@ -0,0 +1,7 @@
package test2
import test.A
fun foo(): A.B.C {
return A.B.C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B.C
fun foo(): C {
return C()
}
@@ -0,0 +1,7 @@
package test2
import test.A.B
fun foo(): B.C {
return B.C()
}
@@ -0,0 +1,6 @@
{
"mainFile": "test.kt",
"type": "MOVE_KOTLIN_NESTED_CLASS",
"targetClass": "test.A",
"withRuntime": "true"
}
@@ -0,0 +1,55 @@
package test
inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
class A {
class X {
}
companion object {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
object O {
class Y
fun foo(n: Int) {}
val bar = 1
fun Int.extFoo(n: Int) {}
val Int.extBar: Int get() = 1
}
class B {
}
class C {
fun test() {
X()
Y()
foo(bar)
//1.extFoo(1.extBar) // conflict
O.Y()
O.foo(O.bar)
with (O) {
Y()
foo(bar)
1.extFoo(1.extBar)
}
}
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A;
class Test {
A.C foo() {
return new A.C();
}
}
@@ -0,0 +1,9 @@
package test2;
import test.A.C;
class Test {
C foo() {
return new C();
}
}

Some files were not shown because too many files have changed in this diff Show More