diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 032558b8f6b..e53a772908a 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -330,7 +330,7 @@
order="before moveJavaFileOrDir"/>
- org.jetbrains.kotlin.idea.refactoring.move.moveTopLevelDeclarations.MoveDeclarationToSeparateFileIntention
+ org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveDeclarationToSeparateFileIntention
Kotlin
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt
index 27c7d005ac8..6fb495f4f4b 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/inline/KotlinInlineValHandler.kt
@@ -45,7 +45,8 @@ import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.refactoring.addTypeArgumentsIfNeeded
import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
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.postProcessMoveUsages
import org.jetbrains.kotlin.idea.references.mainReference
@@ -167,7 +168,7 @@ class KotlinInlineValHandler : InlineActionHandler() {
val targetPackages = referenceExpressions.mapNotNullTo(LinkedHashSet()) { (it.containingFile as? KtFile)?.packageFqName }
for (targetPackage in targetPackages) {
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 ->
val infos = expr.internalUsageInfos
?: LinkedHashMap UsageInfo?>()
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt
index 40d3504eb0f..b23654cf847 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/MoveJavaInnerClassKotlinUsagesHandler.kt
@@ -16,47 +16,47 @@
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.usageView.UsageInfo
-import com.intellij.psi.PsiClass
-import org.jetbrains.kotlin.psi.KtQualifiedExpression
-import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
+import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.idea.references.matchesTarget
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
-import java.util.ArrayList
+import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
-import com.intellij.psi.util.PsiTreeUtil
-import com.intellij.psi.PsiWhiteSpace
-import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
+import java.util.*
class MoveJavaInnerClassKotlinUsagesHandler: MoveInnerClassUsagesHandler {
override fun correctInnerClassUsage(usage: UsageInfo, outerClass: PsiClass) {
val innerCall = usage.element?.parent as? KtCallExpression ?: return
- val receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression
- val outerClassRef = when (receiver) {
+ val receiver = (innerCall.parent as? KtQualifiedExpression)?.receiverExpression ?: return
+ val outerClassRefExpr = when (receiver) {
is KtCallExpression -> receiver.calleeExpression
is KtQualifiedExpression -> receiver.getQualifiedElementSelector()
else -> null
} as? KtSimpleNameExpression
- if (outerClassRef?.mainReference?.resolve() != outerClass) return
-
- val outerCall = outerClassRef!!.parent as? KtCallExpression ?: return
+ val outerClassRef = outerClassRefExpr?.mainReference ?: return
+ if (!outerClassRef.matchesTarget(outerClass)) return
val psiFactory = KtPsiFactory(usage.project)
val argumentList = innerCall.valueArgumentList
if (argumentList != null) {
val newArguments = ArrayList()
- newArguments.add(outerCall.text!!)
+ newArguments.add(receiver.text!!)
argumentList.arguments.mapTo(newArguments) { it.text!! }
argumentList.replace(psiFactory.createCallArguments(newArguments.joinToString(prefix = "(", postfix = ")")))
}
else {
innerCall.lambdaArguments.firstOrNull()?.let { lambdaArg ->
val anchor = PsiTreeUtil.skipSiblingsBackward(lambdaArg, PsiWhiteSpace::class.java)
- innerCall.addAfter(psiFactory.createCallArguments("(${outerCall.text})"), anchor)
+ innerCall.addAfter(psiFactory.createCallArguments("(${receiver.text})"), anchor)
}
}
}
-}
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt
index 0416dfaf4b0..218d4fc8a2b 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt
@@ -18,14 +18,11 @@ package org.jetbrains.kotlin.idea.refactoring.move.changePackage
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
-import com.intellij.refactoring.PackageWrapper
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.moveTopLevelDeclarations.KotlinMoveTarget
-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.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.name.FqName
@@ -39,26 +36,28 @@ class KotlinChangePackageRefactoring(val file: KtFile) {
val packageDirective = file.packageDirective ?: return
val currentFqName = packageDirective.fqName
- val declarationProcessor = MoveKotlinTopLevelDeclarationsProcessor(
+ val declarationProcessor = MoveKotlinDeclarationsProcessor(
project,
- MoveKotlinTopLevelDeclarationsOptions(
+ MoveDeclarationsDescriptor(
elementsToMove = file.declarations.filterIsInstance(),
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 verify(file: PsiFile) = null
},
+ delegate = MoveDeclarationsDelegate.TopLevel,
updateInternalReferences = false
),
Mover.Idle // we don't need to move any declarations physically
)
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()}'") {
packageDirective.fqName = newFqName
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt
similarity index 65%
rename from idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt
rename to idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt
index 5ecea800b5e..b1649e936e5 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/KotlinMoveTarget.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt
@@ -14,56 +14,50 @@
* 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.PsiFile
-import com.intellij.psi.PsiManager
-import com.intellij.refactoring.PackageWrapper
import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.getOrPutNullable
-import java.util.HashMap
+import java.util.*
interface KotlinMoveTarget {
- val packageWrapper: PackageWrapper?
- fun getOrCreateTargetPsi(originalPsi: PsiElement): PsiFile?
- fun getTargetPsiIfExists(originalPsi: PsiElement): PsiFile?
+ val targetContainerFqName: FqName?
+ fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement?
+ fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement?
// Check possible errors and return corresponding message, or null if no errors are detected
fun verify(file: PsiFile): String?
}
object EmptyKotlinMoveTarget: KotlinMoveTarget {
- override val packageWrapper: PackageWrapper? get() = null
+ override val targetContainerFqName = null
override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null
override fun getTargetPsiIfExists(originalPsi: PsiElement) = null
override fun verify(file: PsiFile) = null
}
-class KotlinMoveTargetForExistingFile(val targetFile: KtFile): KotlinMoveTarget {
- override val packageWrapper: PackageWrapper? = targetFile.packageFqName.asString().let { packageName ->
- PackageWrapper(PsiManager.getInstance(targetFile.project), packageName)
- }
+class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMoveTarget {
+ override val targetContainerFqName = targetElement.getContainingKtFile().packageFqName
- 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
override fun verify(file: PsiFile): String? = null
}
class KotlinMoveTargetForDeferredFile(
- project: Project,
- private val packageFqName: FqName,
- private val createFile: (KtFile) -> KtFile?): KotlinMoveTarget {
+ override val targetContainerFqName: FqName,
+ private val createFile: (KtFile) -> KtFile?
+): KotlinMoveTarget {
private val createdFiles = HashMap()
- override val packageWrapper: PackageWrapper = PackageWrapper(PsiManager.getInstance(project), packageFqName.asString())
-
- override fun getOrCreateTargetPsi(originalPsi: PsiElement): PsiFile? {
+ override fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? {
val originalFile = originalPsi.containingFile as? KtFile ?: return null
return createdFiles.getOrPutNullable(originalFile) { createFile(originalFile) }
}
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveDeclarationToSeparateFileIntention.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt
similarity index 91%
rename from idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveDeclarationToSeparateFileIntention.kt
rename to idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt
index 3665c972609..49ba4beec3a 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveDeclarationToSeparateFileIntention.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt
@@ -14,7 +14,7 @@
* 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.navigation.NavigationUtil
@@ -27,7 +27,7 @@ import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
import org.jetbrains.kotlin.idea.quickfix.moveCaret
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.psi.KtClass
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -86,12 +86,13 @@ class MoveDeclarationToSeparateFileIntention :
}
return
}
- val moveTarget = KotlinMoveTargetForDeferredFile(project, packageName) {
+ val moveTarget = KotlinMoveTargetForDeferredFile(packageName) {
createKotlinFile(targetFileName, directory, packageName.asString())
}
- val moveOptions = MoveKotlinTopLevelDeclarationsOptions(
+ val moveOptions = MoveDeclarationsDescriptor(
elementsToMove = listOf(element),
moveTarget = moveTarget,
+ delegate = MoveDeclarationsDelegate.TopLevel,
searchInCommentsAndStrings = false,
searchInNonCode = false,
updateInternalReferences = true,
@@ -102,6 +103,6 @@ class MoveDeclarationToSeparateFileIntention :
FileEditorManager.getInstance(project).selectedTextEditor?.moveCaret(newDeclaration.startOffset + originalOffset)
}
)
- MoveKotlinTopLevelDeclarationsProcessor(project, moveOptions).run()
+ MoveKotlinDeclarationsProcessor(project, moveOptions).run()
}
}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt
new file mode 100644
index 00000000000..c405b7443df
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt
@@ -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
+ abstract fun collectConflicts(usages: MutableList, conflicts: MultiMap)
+ abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration)
+ abstract fun preprocessUsages(project: Project, usages: List)
+
+ 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 = emptyList()
+
+ override fun collectConflicts(usages: MutableList, conflicts: MultiMap) {
+
+ }
+
+ override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) {
+
+ }
+
+ override fun preprocessUsages(project: Project, usages: List) {
+
+ }
+ }
+
+ 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 {
+ val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass ?: return emptyList()
+ return collectOuterInstanceReferences(classToMove)
+ }
+
+ override fun collectConflicts(usages: MutableList, conflicts: MultiMap) {
+ 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) {
+ 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)) }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsHandler.kt
similarity index 95%
rename from idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
rename to idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsHandler.kt
index 715b563c121..b41e9836b64 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsHandler.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsHandler.kt
@@ -14,7 +14,7 @@
* 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.LangDataKeys
@@ -32,11 +32,11 @@ import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackages
import com.intellij.refactoring.util.CommonRefactoringUtil
import org.jetbrains.kotlin.idea.core.getPackage
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 java.util.*
-class MoveKotlinTopLevelDeclarationsHandler : MoveHandlerDelegate() {
+class MoveKotlinDeclarationsHandler : MoveHandlerDelegate() {
private fun getSourceDirectories(elements: Array) = elements.mapTo(LinkedHashSet()) { it.containingFile?.parent }
private fun doMoveWithCheck(
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt
similarity index 74%
rename from idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt
rename to idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt
index 5722c3777fe..2104746a755 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt
@@ -14,8 +14,9 @@
* 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.util.Ref
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.MoveMultipleElementsViewDescriptor
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
-import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesUtil
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.refactoring.util.TextOccurrencesUtil
import com.intellij.usageView.UsageInfo
+import com.intellij.usageView.UsageViewBundle
import com.intellij.usageView.UsageViewDescriptor
import com.intellij.usageView.UsageViewUtil
import com.intellij.util.IncorrectOperationException
+import com.intellij.util.SmartList
import com.intellij.util.VisibilityUtil
import com.intellij.util.containers.MultiMap
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.references.KtSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.search.projectScope
-import org.jetbrains.kotlin.psi.KtFile
-import org.jetbrains.kotlin.psi.KtModifierListOwner
-import org.jetbrains.kotlin.psi.KtNamedDeclaration
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
import org.jetbrains.kotlin.psi.psiUtil.isInsideOf
import org.jetbrains.kotlin.psi.psiUtil.isPrivate
@@ -64,58 +65,64 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.utils.keysToMap
import java.util.*
-interface Mover: (KtNamedDeclaration, KtFile) -> KtNamedDeclaration {
- object Default: Mover {
- override fun invoke(originalElement: KtNamedDeclaration, targetFile: KtFile): KtNamedDeclaration {
- val newElement = targetFile.add(originalElement) as KtNamedDeclaration
- originalElement.deleteSingle()
- return newElement
+interface Mover: (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
+ object Default : Mover {
+ override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration {
+ return when (targetContainer) {
+ is KtFile -> targetContainer.add(originalElement) as KtNamedDeclaration
+ is KtClassOrObject -> targetContainer.addDeclaration(originalElement) as KtNamedDeclaration
+ else -> error("Unexpected element: ${targetContainer.getElementTextWithContext()}")
+ }.apply { originalElement.deleteSingle() }
}
}
- object Idle: Mover {
- override fun invoke(originalElement: KtNamedDeclaration, targetFile: KtFile) = originalElement
+ object Idle : Mover {
+ override fun invoke(originalElement: KtNamedDeclaration, targetContainer: KtElement) = originalElement
}
}
-class MoveKotlinTopLevelDeclarationsOptions(
+class MoveDeclarationsDescriptor(
val elementsToMove: Collection,
val moveTarget: KotlinMoveTarget,
+ val delegate: MoveDeclarationsDelegate,
val searchInCommentsAndStrings: Boolean = true,
val searchInNonCode: Boolean = true,
val updateInternalReferences: Boolean = true,
val deleteSourceFiles: Boolean = false,
- val moveCallback: MoveCallback? = null
+ val moveCallback: MoveCallback? = null,
+ val openInEditor: Boolean = false
)
-class MoveKotlinTopLevelDeclarationsProcessor(
+class MoveKotlinDeclarationsProcessor(
val project: Project,
- val options: MoveKotlinTopLevelDeclarationsOptions,
+ val descriptor: MoveDeclarationsDescriptor,
val mover: Mover = Mover.Default) : BaseRefactoringProcessor(project) {
companion object {
private val REFACTORING_NAME: String = KotlinRefactoringBundle.message("refactoring.move.top.level.declarations")
}
private var nonCodeUsages: Array? = 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
.groupBy { it.getContainingKtFile() }
.mapValues { it.value.keysToMap { it.toLightElements() } }
private val conflicts = MultiMap()
override fun createUsageViewDescriptor(usages: Array): UsageViewDescriptor {
- return MoveMultipleElementsViewDescriptor(
- elementsToMove.toTypedArray(),
- MoveClassesOrPackagesUtil.getPackageName(options.moveTarget.packageWrapper)
- )
+ val targetContainerFqName = descriptor.moveTarget.targetContainerFqName?.let {
+ if (it.isRoot) UsageViewBundle.message("default.package.presentable.name") else it.asString()
+ }
+ return MoveMultipleElementsViewDescriptor(elementsToMove.toTypedArray(), targetContainerFqName)
}
+ private val usagesToProcessBeforeMove = SmartList()
+
public override fun findUsages(): Array {
- val newPackageName = options.moveTarget.packageWrapper?.qualifiedName ?: ""
+ val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: ""
fun collectUsages(kotlinToLightElements: Map>, result: MutableList) {
kotlinToLightElements.values.flatMap { it }.flatMapTo(result) { lightElement ->
- val newFqName = StringUtil.getQualifiedName(newPackageName, lightElement.name)
+ val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
val foundReferences = HashSet()
val projectScope = lightElement.project.projectScope()
@@ -133,8 +140,8 @@ class MoveKotlinTopLevelDeclarationsProcessor(
TextOccurrencesUtil.findNonCodeUsages(
lightElement,
name,
- options.searchInCommentsAndStrings,
- options.searchInNonCode,
+ descriptor.searchInCommentsAndStrings,
+ descriptor.searchInNonCode,
newFqName,
results
)
@@ -169,8 +176,9 @@ class MoveKotlinTopLevelDeclarationsProcessor(
val container = element.getUsageContext()
if (!declarationToContainers.getOrPut(declaration) { HashSet() }.add(container)) continue
+ // todo: ok for now, will be replaced by proper visibility analysis
val currentPackage = element.containingFile?.containingDirectory?.getPackage()
- if (currentPackage?.qualifiedName == newPackageName) continue
+ if (currentPackage?.qualifiedName == newContainerName) continue
conflicts.putValue(
declaration,
@@ -184,7 +192,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
}
fun collectConflictsInDeclarations() {
- if (newPackageName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return
+ if (newContainerName == UNKNOWN_PACKAGE_FQ_NAME.asString()) return
val declarationToReferenceTargets = HashMap>()
for (declaration in elementsToMove) {
@@ -207,8 +215,9 @@ class MoveKotlinTopLevelDeclarationsProcessor(
if (!declarationToReferenceTargets.getOrPut(declaration) { HashSet() }.add(refTarget)) continue
+ // todo: ok for now, will be replaced by proper visibility analysis
val currentPackage = declaration.containingFile?.containingDirectory?.getPackage()
- if (currentPackage?.qualifiedName == newPackageName) continue
+ if (currentPackage?.qualifiedName == newContainerName) continue
conflicts.putValue(
declaration,
@@ -224,14 +233,29 @@ class MoveKotlinTopLevelDeclarationsProcessor(
val usages = ArrayList()
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
- // No need to find and process usages if package is not changed
- if (sourceFile.packageFqName.asString() == newPackageName) return UsageInfo.EMPTY_ARRAY
+ kotlinToLightElements.keys.forEach {
+ 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)
collectConflictsInUsages(usages)
collectConflictsInDeclarations()
+ descriptor.delegate.collectConflicts(usages, conflicts)
}
+ descriptor.delegate.collectConflicts(usagesToProcessBeforeMove, conflicts)
return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray())
}
@@ -241,27 +265,15 @@ class MoveKotlinTopLevelDeclarationsProcessor(
}
override fun performRefactoring(usages: Array) {
- fun moveDeclaration(
- declaration: KtNamedDeclaration,
- moveTarget: KotlinMoveTarget,
- usagesToProcessAfterMove: MutableList
- ): KtNamedDeclaration? {
+ fun moveDeclaration(declaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): KtNamedDeclaration? {
val file = declaration.containingFile as? KtFile
assert(file != null) { "${declaration.javaClass}: ${declaration.text}" }
- val targetFile = moveTarget.getOrCreateTargetPsi(declaration) as? KtFile
- ?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.text}")
+ val targetContainer = moveTarget.getOrCreateTargetPsi(declaration)
+ ?: throw AssertionError("Couldn't create Kotlin file for: ${declaration.javaClass}: ${declaration.text}")
- if (options.updateInternalReferences) {
- val packageNameInfo = PackageNameInfo(file!!.packageFqName, targetFile.packageFqName.toUnsafe())
- val (usagesToProcessLater, usagesToProcessNow) = declaration
- .getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo)
- .partition { it is MoveRenameUsageInfoForExtension }
- postProcessMoveUsages(usagesToProcessNow, shorteningMode = ShorteningMode.NO_SHORTENING)
- usagesToProcessAfterMove.addAll(usagesToProcessLater)
- }
-
- val newElement = mover(declaration, targetFile)
+ descriptor.delegate.preprocessDeclaration(descriptor, declaration)
+ val newElement = mover(declaration, targetContainer)
newElement.addToShorteningWaitSet()
@@ -271,6 +283,10 @@ class MoveKotlinTopLevelDeclarationsProcessor(
try {
val usageList = usages.toArrayList()
+ descriptor.delegate.preprocessUsages(project, usageList)
+
+ postProcessMoveUsages(usagesToProcessBeforeMove, shorteningMode = ShorteningMode.NO_SHORTENING)
+
val oldToNewElementsMapping = THashMap(
object: TObjectHashingStrategy {
override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean {
@@ -293,7 +309,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
)
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
for ((oldDeclaration, oldLightElements) in kotlinToLightElements) {
- val newDeclaration = moveDeclaration(oldDeclaration, options.moveTarget, usageList)
+ val newDeclaration = moveDeclaration(oldDeclaration, descriptor.moveTarget)
if (newDeclaration == null) {
for (oldElement in oldLightElements) {
oldToNewElementsMapping[oldElement] = oldElement
@@ -307,9 +323,13 @@ class MoveKotlinTopLevelDeclarationsProcessor(
for ((oldElement, newElement) in oldLightElements.asSequence().zip(newDeclaration.toLightElements().asSequence())) {
oldToNewElementsMapping[oldElement] = newElement
}
+
+ if (descriptor.openInEditor) {
+ EditorHelper.openInEditor(newDeclaration)
+ }
}
- if (options.deleteSourceFiles) {
+ if (descriptor.deleteSourceFiles) {
sourceFile.delete()
}
}
@@ -324,7 +344,7 @@ class MoveKotlinTopLevelDeclarationsProcessor(
override fun performPsiSpoilingRefactoring() {
nonCodeUsages?.let { nonCodeUsages -> RenameUtil.renameNonCodeUsages(myProject, nonCodeUsages) }
- options.moveCallback?.refactoringCompleted()
+ descriptor.moveCallback?.refactoringCompleted()
}
fun execute(usages: List) {
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/KotlinAwareMoveFilesOrDirectoriesDialog.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/KotlinAwareMoveFilesOrDirectoriesDialog.kt
similarity index 99%
rename from idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/KotlinAwareMoveFilesOrDirectoriesDialog.kt
rename to idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/KotlinAwareMoveFilesOrDirectoriesDialog.kt
index cb95542d333..10695342ff5 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/KotlinAwareMoveFilesOrDirectoriesDialog.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/KotlinAwareMoveFilesOrDirectoriesDialog.kt
@@ -14,7 +14,7 @@
* 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.PropertiesComponent
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
similarity index 99%
rename from idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
rename to idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
index 82dad3b8336..8f750cbbf14 100644
--- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveTopLevelDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
+++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.form
@@ -1,5 +1,5 @@
-