Add import for member: don't suggest if a class with the same name has been imported
#KT-38492 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
329f0227ec
commit
45d60baeb0
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.intentions
|
|||||||
|
|
||||||
import com.intellij.codeInsight.intention.HighPriorityAction
|
import com.intellij.codeInsight.intention.HighPriorityAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
@@ -16,7 +17,6 @@ import org.jetbrains.kotlin.idea.references.mainReference
|
|||||||
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
|
import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors
|
||||||
import org.jetbrains.kotlin.idea.util.ImportDescriptorResult
|
import org.jetbrains.kotlin.idea.util.ImportDescriptorResult
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
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
|
||||||
@@ -33,7 +33,14 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameRefe
|
|||||||
|
|
||||||
if (element.isInImportDirective()) return false
|
if (element.isInImportDirective()) return false
|
||||||
|
|
||||||
val fqName = targetFqName(qualifiedExpression) ?: return false
|
val target = target(qualifiedExpression) ?: return false
|
||||||
|
val fqName = target.importableFqName ?: return false
|
||||||
|
|
||||||
|
val file = element.containingKtFile
|
||||||
|
val project = file.project
|
||||||
|
val dummyFile = KtPsiFactory(project).createAnalyzableFile("Dummy.kt", file.text, file)
|
||||||
|
val helper = ImportInsertHelper.getInstance(project)
|
||||||
|
if (helper.importDescriptor(dummyFile, target) == ImportDescriptorResult.FAIL) return false
|
||||||
|
|
||||||
setTextGetter(KotlinBundle.lazyMessage("add.import.for.0", fqName.asString()))
|
setTextGetter(KotlinBundle.lazyMessage("add.import.for.0", fqName.asString()))
|
||||||
return true
|
return true
|
||||||
@@ -57,18 +64,18 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameRefe
|
|||||||
|
|
||||||
val qualifiedExpressions = file.collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression ->
|
val qualifiedExpressions = file.collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression ->
|
||||||
val selector = qualifiedExpression.getQualifiedElementSelector() as? KtNameReferenceExpression
|
val selector = qualifiedExpression.getQualifiedElementSelector() as? KtNameReferenceExpression
|
||||||
selector?.getReferencedNameAsName() == fqName.shortName() && targetFqName(qualifiedExpression) == fqName
|
selector?.getReferencedNameAsName() == fqName.shortName() && target(qualifiedExpression)?.importableFqName == fqName
|
||||||
}
|
}
|
||||||
val userTypes = file.collectDescendantsOfType<KtUserType> { userType ->
|
val userTypes = file.collectDescendantsOfType<KtUserType> { userType ->
|
||||||
val selector = userType.getQualifiedElementSelector() as? KtNameReferenceExpression
|
val selector = userType.getQualifiedElementSelector() as? KtNameReferenceExpression
|
||||||
selector?.getReferencedNameAsName() == fqName.shortName() && targetFqName(userType) == fqName
|
selector?.getReferencedNameAsName() == fqName.shortName() && target(userType)?.importableFqName == fqName
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: not deep
|
//TODO: not deep
|
||||||
ShortenReferences.DEFAULT.process(qualifiedExpressions + userTypes)
|
ShortenReferences.DEFAULT.process(qualifiedExpressions + userTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun targetFqName(qualifiedElement: KtElement): FqName? {
|
private fun target(qualifiedElement: KtElement): DeclarationDescriptor? {
|
||||||
val nameExpression = qualifiedElement.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null
|
val nameExpression = qualifiedElement.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null
|
||||||
val receiver = nameExpression.getReceiverExpression() ?: return null
|
val receiver = nameExpression.getReceiverExpression() ?: return null
|
||||||
val bindingContext = qualifiedElement.analyze(BodyResolveMode.PARTIAL)
|
val bindingContext = qualifiedElement.analyze(BodyResolveMode.PARTIAL)
|
||||||
@@ -77,6 +84,6 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameRefe
|
|||||||
val targets = nameExpression.mainReference.resolveToDescriptors(bindingContext)
|
val targets = nameExpression.mainReference.resolveToDescriptors(bindingContext)
|
||||||
if (targets.isEmpty()) return null
|
if (targets.isEmpty()) return null
|
||||||
if (!targets.all { it.canBeAddedToImport() }) return null
|
if (!targets.all { it.canBeAddedToImport() }) return null
|
||||||
return targets.map { it.importableFqName }.singleOrNull()
|
return targets.singleOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
package foo.bar
|
||||||
|
|
||||||
|
import foo.bar.A.SUCCESS
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(SUCCESS)
|
||||||
|
bar(B.SUCCESS<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(a: A) {}
|
||||||
|
|
||||||
|
fun bar(b: B) {}
|
||||||
|
|
||||||
|
enum class A {
|
||||||
|
SUCCESS, ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class B {
|
||||||
|
SUCCESS, ERROR
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
package foo.bar
|
||||||
|
|
||||||
|
import foo.bar.A.*
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(SUCCESS)
|
||||||
|
bar(B.SUCCESS<caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(a: A) {}
|
||||||
|
|
||||||
|
fun bar(b: B) {}
|
||||||
|
|
||||||
|
enum class A {
|
||||||
|
SUCCESS, ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class B {
|
||||||
|
SUCCESS, ERROR
|
||||||
|
}
|
||||||
|
|
||||||
@@ -8973,6 +8973,16 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/importMember"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/intentions/importMember"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("AlreadyImportedSameNameClass.kt")
|
||||||
|
public void testAlreadyImportedSameNameClass() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/importMember/AlreadyImportedSameNameClass.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("AlreadyImportedSameNameClass2.kt")
|
||||||
|
public void testAlreadyImportedSameNameClass2() throws Exception {
|
||||||
|
runTest("idea/testData/intentions/importMember/AlreadyImportedSameNameClass2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ClassCallChain.kt")
|
@TestMetadata("ClassCallChain.kt")
|
||||||
public void testClassCallChain() throws Exception {
|
public void testClassCallChain() throws Exception {
|
||||||
runTest("idea/testData/intentions/importMember/ClassCallChain.kt");
|
runTest("idea/testData/intentions/importMember/ClassCallChain.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user