Move: Search Java usages when top-level declaration moves to new facade

#KT-22747 Fixed
This commit is contained in:
Alexey Sedunov
2018-02-19 15:32:35 +03:00
parent 91fdc0e967
commit a341392524
9 changed files with 64 additions and 9 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.ide.highlighter.JavaFileType
import com.intellij.ide.util.EditorHelper
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref
@@ -23,6 +24,8 @@ import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.PsiReference
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.move.MoveCallback
@@ -41,6 +44,7 @@ import com.intellij.util.containers.MultiMap
import gnu.trove.THashMap
import gnu.trove.TObjectHashingStrategy
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightElements
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToBeShortenedDescendantsToWaitingSet
@@ -49,6 +53,7 @@ import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.idea.search.restrictByFileType
import org.jetbrains.kotlin.idea.util.projectStructure.module
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -153,24 +158,39 @@ class MoveKotlinDeclarationsProcessor(
val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: ""
fun canSkipUsages(element: PsiElement): Boolean {
val ktDeclaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return false
if (ktDeclaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) return false
val (oldContainer, newContainer) = descriptor.delegate.getContainerChangeInfo(ktDeclaration, descriptor.moveTarget)
val targetModule = descriptor.moveTarget.getTargetModule(project) ?: return false
return oldContainer == newContainer && ktDeclaration.module == targetModule
fun getSearchScope(element: PsiElement): GlobalSearchScope? {
val projectScope = project.projectScope()
val ktDeclaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return projectScope
if (ktDeclaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) return projectScope
val moveTarget = descriptor.moveTarget
val (oldContainer, newContainer) = descriptor.delegate.getContainerChangeInfo(ktDeclaration, moveTarget)
val targetModule = moveTarget.getTargetModule(project) ?: return projectScope
if (oldContainer != newContainer || ktDeclaration.module != targetModule) return projectScope
// Check if facade class may change
if (newContainer is ContainerInfo.Package) {
val javaScope = projectScope.restrictByFileType(JavaFileType.INSTANCE)
val currentFile = ktDeclaration.containingKtFile
val newFile = when (moveTarget) {
is KotlinMoveTargetForExistingElement -> moveTarget.targetElement as? KtFile ?: return null
is KotlinMoveTargetForDeferredFile -> return javaScope
else -> return null
}
val currentFacade = currentFile.findFacadeClass()
val newFacade = newFile.findFacadeClass()
return if (currentFacade?.qualifiedName != newFacade?.qualifiedName) javaScope else null
}
return null
}
fun collectUsages(kotlinToLightElements: Map<KtNamedDeclaration, List<PsiNamedElement>>, result: MutableCollection<UsageInfo>) {
kotlinToLightElements.values.flatten().flatMapTo(result) { lightElement ->
if (canSkipUsages(lightElement)) return@flatMapTo emptyList()
val searchScope = getSearchScope(lightElement) ?: return@flatMapTo emptyList()
val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name)
val foundReferences = HashSet<PsiReference>()
val projectScope = project.projectScope()
val results = ReferencesSearch
.search(lightElement, projectScope)
.search(lightElement, searchScope)
.mapNotNullTo(ArrayList()) { ref ->
if (foundReferences.add(ref) && elementsToMove.none { it.isAncestor(ref.element)}) {
createMoveUsageInfoIfPossible(ref, lightElement, true, false)
@@ -0,0 +1,7 @@
package test;
public class Test {
public static void main(String[] args) {
BarKt.foo();
}
}
@@ -0,0 +1,7 @@
package test;
public class Test {
public static void main(String[] args) {
FooKt.foo();
}
}
@@ -0,0 +1,3 @@
package test
fun <caret>foo() {}
@@ -0,0 +1,5 @@
{
"mainFile": "source/foo.kt",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetFile": "source/bar.kt"
}
@@ -601,6 +601,12 @@ public class MoveTestGenerated extends AbstractMoveTest {
doTest(fileName);
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/facadeClassChangeInTheSamePackage/facadeClassChangeInTheSamePackage.test")
public void testKotlin_moveTopLevelDeclarations_misc_facadeClassChangeInTheSamePackage_FacadeClassChangeInTheSamePackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/facadeClassChangeInTheSamePackage/facadeClassChangeInTheSamePackage.test");
doTest(fileName);
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/fakeOverrideInObject/fakeOverrideInObject.test")
public void testKotlin_moveTopLevelDeclarations_misc_fakeOverrideInObject_FakeOverrideInObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/fakeOverrideInObject/fakeOverrideInObject.test");