Move: Retain imports when moving top-level declaration

#KT-5049 Fixed
This commit is contained in:
Alexey Sedunov
2014-05-27 14:51:08 +04:00
parent df413c0b47
commit ab5e0c8c9c
21 changed files with 210 additions and 16 deletions
@@ -68,9 +68,9 @@ public fun FqName.plusOneSegment(fullFQN: FqName): FqName? {
return child(fullFQN.tail(this).pathSegments().first!!)
}
public fun FqName.isImported(importPath: ImportPath): Boolean {
public fun FqName.isImported(importPath: ImportPath, skipAliasedImports: Boolean = true): Boolean {
return when {
importPath.hasAlias() -> false
skipAliasedImports && importPath.hasAlias() -> false
importPath.isAllUnder() && !isRoot() -> importPath.fqnPart() == this.parent()
else -> importPath.fqnPart() == this
}
@@ -181,7 +181,7 @@ public class MoveKotlinFileHandler : MoveFileHandler() {
val packageNameInfo = file.getAndRemoveCopyableUserData(PACKAGE_NAME_INFO_KEY)
if (packageNameInfo == null) return
file.updateInternalReferencesOnPackageNameChange(packageNameInfo)
file.updateInternalReferencesOnPackageNameChange(packageNameInfo, updateImportedReferences = false)
val packageRef = file.getPackageDirective()?.getLastReferenceExpression()?.getReference() as? JetSimpleNameReference
packageRef?.bindToFqName(packageNameInfo.newPackageName)
@@ -243,7 +243,11 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio
val newPackageFqName = (targetFile as JetFile).getPackageFqName()
val packageNameInfo = PackageNameInfo(file!!.getPackageFqName(), newPackageFqName)
declaration.updateInternalReferencesOnPackageNameChange(packageNameInfo, ShorteningMode.NO_SHORTENING)
declaration.updateInternalReferencesOnPackageNameChange(
packageNameInfo,
updateImportedReferences = true,
shorteningMode = ShorteningMode.NO_SHORTENING
)
val newElement = targetFile.add(declaration) as JetNamedDeclaration
declaration.delete()
@@ -27,24 +27,33 @@ import org.jetbrains.jet.lang.resolve.name.FqName
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.plugin.references.JetSimpleNameReference.ShorteningMode
import com.intellij.psi.PsiClass
import org.jetbrains.jet.asJava.KotlinLightClass
import org.jetbrains.jet.plugin.JetFileType
import org.jetbrains.jet.lang.psi.JetClassOrObject
import org.jetbrains.jet.lang.psi.JetNamedDeclaration
import org.jetbrains.jet.plugin.imports.canBeReferencedViaImport
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
import org.jetbrains.jet.lang.psi.psiUtil.isInsideOf
import org.jetbrains.jet.lang.psi.psiUtil.isAncestor
import java.util.Collections
import org.jetbrains.jet.lang.resolve.name.isImported
public class PackageNameInfo(val oldPackageName: FqName, val newPackageName: FqName)
public fun JetElement.updateInternalReferencesOnPackageNameChange(
packageNameInfo: PackageNameInfo, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING
packageNameInfo: PackageNameInfo,
updateImportedReferences: Boolean,
shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING
) {
val file = getContainingFile() as? JetFile
if (file == null) return
val importPaths =
if (updateImportedReferences) {
file.getImportDirectives().map { it.getImportPath() }.filterNotNull()
}
else Collections.emptyList()
fun isImportedName(fqName: FqName): Boolean =
updateImportedReferences && importPaths.any { fqName.isImported(it, false) }
val referenceToContext = JetFileReferencesResolver.resolve(file = file, elements = listOf(this), visitReceivers = false)
for ((refExpr, bindingContext) in referenceToContext) {
@@ -58,16 +67,19 @@ public fun JetElement.updateInternalReferencesOnPackageNameChange(
val declaration = DescriptorToDeclarationUtil.getDeclaration(file, descriptor, bindingContext)
if (declaration == null || isAncestor(declaration, true)) continue
val fqName = DescriptorUtils.getFqName(descriptor)
if (!fqName.isSafe()) continue
val fqNameSafe = fqName.toSafe()
val packageName = DescriptorUtils.getParentOfType(
descriptor, javaClass<PackageFragmentDescriptor>(), false
)?.let { DescriptorUtils.getFqName(it).toSafe() }
when (packageName) {
packageNameInfo.oldPackageName,
packageNameInfo.newPackageName -> {
val fqName = DescriptorUtils.getFqName(descriptor)
if (fqName.isSafe()) {
(refExpr.getReference() as? JetSimpleNameReference)?.bindToFqName(fqName.toSafe(), shorteningMode)
}
when {
packageName == packageNameInfo.oldPackageName,
packageName == packageNameInfo.newPackageName,
isImportedName(fqNameSafe) -> {
(refExpr.getReference() as? JetSimpleNameReference)?.bindToFqName(fqNameSafe, shorteningMode)
}
}
}
@@ -0,0 +1,15 @@
package first
import second.A
import third.B
import third.D
import fourth.X
class Test {
val a = A()
val b = B()
val d_ = D()
val c = B.C()
val x = X()
val y = X.Y()
}
@@ -0,0 +1,7 @@
package fourth
class X {
class Y {
}
}
@@ -0,0 +1,10 @@
package second
import third.B
import fourth.X.Y
import third.D as D_
import fourth.*
import third.B.*
class A
@@ -0,0 +1,11 @@
package third
public class B {
class C {
}
}
public class D {
}
@@ -0,0 +1,7 @@
package fourth
class X {
class Y {
}
}
@@ -0,0 +1,18 @@
package second
import third.B
import fourth.X.Y
import third.D as D_
import fourth.*
import third.B.*
class A
class <caret>Test {
val a = A()
val b = B()
val d_ = D_()
val c = C()
val x = X()
val y = Y()
}
@@ -0,0 +1,11 @@
package third
public class B {
class C {
}
}
public class D {
}
@@ -0,0 +1,5 @@
{
"mainFile": "second/A.kt",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetPackage": "first"
}
@@ -0,0 +1,15 @@
package first
import second.A
import third.B
import third.D
import fourth.X
fun test() {
val a = A()
val b = B()
val d_ = D()
val c = B.C()
val x = X()
val y = X.Y()
}
@@ -0,0 +1,7 @@
package fourth
class X {
class Y {
}
}
@@ -0,0 +1,10 @@
package second
import third.B
import fourth.X.Y
import third.D as D_
import fourth.*
import third.B.*
class A
@@ -0,0 +1,11 @@
package third
public class B {
class C {
}
}
public class D {
}
@@ -0,0 +1,7 @@
package fourth
class X {
class Y {
}
}
@@ -0,0 +1,18 @@
package second
import third.B
import fourth.X.Y
import third.D as D_
import fourth.*
import third.B.*
class A
fun <caret>test() {
val a = A()
val b = B()
val d_ = D_()
val c = C()
val x = X()
val y = Y()
}
@@ -0,0 +1,11 @@
package third
public class B {
class C {
}
}
public class D {
}
@@ -0,0 +1,5 @@
{
"mainFile": "second/A.kt",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetPackage": "first"
}
@@ -191,6 +191,16 @@ public class JetMoveTestGenerated extends AbstractJetMoveTest {
doTest("idea/testData/refactoring/move/kotlin/movePackage/movePackage/movePackage.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/moveClassWithImportsRetained/moveClassWithImportsRetained.test")
public void testKotlin_moveTopLevelDeclarations_misc_moveClassWithImportsRetained_MoveClassWithImportsRetained() throws Exception {
doTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveClassWithImportsRetained/moveClassWithImportsRetained.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/moveFunctionWithImportsRetained/moveFunctionWithImportsRetained.test")
public void testKotlin_moveTopLevelDeclarations_misc_moveFunctionWithImportsRetained_MoveFunctionWithImportsRetained() throws Exception {
doTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveFunctionWithImportsRetained/moveFunctionWithImportsRetained.test");
}
@TestMetadata("kotlin/moveTopLevelDeclarations/moveClassToFile/moveClassToFile.test")
public void testKotlin_moveTopLevelDeclarations_moveClassToFile_MoveClassToFile() throws Exception {
doTest("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/moveClassToFile/moveClassToFile.test");