Import members from: suggest on type reference

#KT-29927 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-02-22 14:19:45 +09:00
committed by Dmitry Gridin
parent 3af6b36401
commit 6b5ba272a0
10 changed files with 131 additions and 18 deletions
@@ -25,9 +25,7 @@ import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.references.mainReference
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.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.isInImportDirective import org.jetbrains.kotlin.psi.psiUtil.isInImportDirective
@@ -35,17 +33,15 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier import org.jetbrains.kotlin.resolve.scopes.receivers.ClassQualifier
class ImportAllMembersIntention : SelfTargetingIntention<KtDotQualifiedExpression>( class ImportAllMembersIntention : SelfTargetingIntention<KtElement>(KtElement::class.java, "Import members with '*'"){
KtDotQualifiedExpression::class.java, override fun isApplicableTo(element: KtElement, caretOffset: Int): Boolean {
"Import members with '*'" val receiverExpression = element.receiverExpression() ?: return false
){ if (!receiverExpression.range.containsOffset(caretOffset)) return false
override fun isApplicableTo(element: KtDotQualifiedExpression, caretOffset: Int): Boolean {
if (!element.receiverExpression.range.containsOffset(caretOffset)) return false
val target = target(element) ?: return false val target = target(element, receiverExpression) ?: return false
val targetFqName = target.importableFqName ?: return false val targetFqName = target.importableFqName ?: return false
if (element.isInImportDirective()) return false if (receiverExpression.isInImportDirective()) return false
val file = element.containingKtFile val file = element.containingKtFile
val project = file.project val project = file.project
@@ -58,12 +54,12 @@ class ImportAllMembersIntention : SelfTargetingIntention<KtDotQualifiedExpressio
return true return true
} }
override fun applyTo(element: KtDotQualifiedExpression, editor: Editor?) { override fun applyTo(element: KtElement, editor: Editor?) {
element.importReceiverMembers() element.importReceiverMembers()
} }
companion object { companion object {
fun KtDotQualifiedExpression.importReceiverMembers() { fun KtElement.importReceiverMembers() {
val target = target(this) ?: return val target = target(this) ?: return
val classFqName = target.importableFqName!!.parent() val classFqName = target.importableFqName!!.parent()
@@ -73,19 +69,36 @@ class ImportAllMembersIntention : SelfTargetingIntention<KtDotQualifiedExpressio
val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression val qualifierName = qualifiedExpression.receiverExpression.getQualifiedElementSelector() as? KtNameReferenceExpression
qualifierName?.getReferencedNameAsName() == classFqName.shortName() && target(qualifiedExpression)?.importableFqName?.parent() == classFqName qualifierName?.getReferencedNameAsName() == classFqName.shortName() && target(qualifiedExpression)?.importableFqName?.parent() == classFqName
} }
val userTypes = containingKtFile.collectDescendantsOfType<KtUserType> { userType ->
val receiver = userType.receiverExpression()?.getQualifiedElementSelector() as? KtNameReferenceExpression
receiver?.getReferencedNameAsName() == classFqName.shortName() && target(userType)?.importableFqName?.parent() == classFqName
}
//TODO: not deep //TODO: not deep
ShortenReferences.DEFAULT.process(qualifiedExpressions) ShortenReferences.DEFAULT.process(qualifiedExpressions + userTypes)
} }
private fun target(expression: KtDotQualifiedExpression): DeclarationDescriptor? { private fun target(qualifiedElement: KtElement, receiverExpression: KtExpression): DeclarationDescriptor? {
val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) val bindingContext = qualifiedElement.analyze(BodyResolveMode.PARTIAL)
if (bindingContext[BindingContext.QUALIFIER, expression.receiverExpression] !is ClassQualifier) { if (bindingContext[BindingContext.QUALIFIER, receiverExpression] !is ClassQualifier) {
return null return null
} }
val selector = expression.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null val selector = qualifiedElement.getQualifiedElementSelector() as? KtNameReferenceExpression ?: return null
return selector.mainReference.resolveToDescriptors(bindingContext).firstOrNull() return selector.mainReference.resolveToDescriptors(bindingContext).firstOrNull()
} }
private fun target(qualifiedElement: KtElement): DeclarationDescriptor? {
val receiverExpression = qualifiedElement.receiverExpression() ?: return null
return target(qualifiedElement, receiverExpression)
}
private fun KtElement.receiverExpression(): KtExpression? {
return when (this) {
is KtDotQualifiedExpression -> receiverExpression
is KtUserType -> qualifier?.referenceExpression
else -> null
}
}
} }
} }
@@ -0,0 +1,7 @@
package com.test
sealed class States {
object Loading : States()
object Content : States()
object Error : States()
}
@@ -0,0 +1,7 @@
package com.test
sealed class States {
object Loading : States()
object Content : States()
object Error : States()
}
@@ -0,0 +1,15 @@
// INTENTION_TEXT: "Import members from 'com.test.States'"
import com.test.States
fun foo(s: States) {
when (s) {
is <caret>States.Loading -> {
}
is States.Error -> {
}
is States.Content -> {
}
}
}
fun bar(): States.Loading = States.Loading
@@ -0,0 +1,16 @@
// INTENTION_TEXT: "Import members from 'com.test.States'"
import com.test.States
import com.test.States.*
fun foo(s: States) {
when (s) {
is Loading -> {
}
is Error -> {
}
is Content -> {
}
}
}
fun bar(): Loading = Loading
@@ -0,0 +1,7 @@
package com.test
sealed class States {
object Loading : States()
object Content : States()
object Error : States()
}
@@ -0,0 +1,7 @@
package com.test
sealed class States {
object Loading : States()
object Content : States()
object Error : States()
}
@@ -0,0 +1,15 @@
// INTENTION_TEXT: "Import members from 'com.test.States'"
import com.test.States
fun foo(s: States) {
when (s) {
is States.Loading -> {
}
is States.Error -> {
}
is States.Content -> {
}
}
}
fun bar(): <caret>States.Loading = States.Loading
@@ -0,0 +1,16 @@
// INTENTION_TEXT: "Import members from 'com.test.States'"
import com.test.States
import com.test.States.*
fun foo(s: States) {
when (s) {
is Loading -> {
}
is Error -> {
}
is Content -> {
}
}
}
fun bar(): Loading = Loading
@@ -9534,6 +9534,16 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
runTest("idea/testData/intentions/importAllMembers/StaticJavaMembers.kt"); runTest("idea/testData/intentions/importAllMembers/StaticJavaMembers.kt");
} }
@TestMetadata("TypeReference.kt")
public void testTypeReference() throws Exception {
runTest("idea/testData/intentions/importAllMembers/TypeReference.kt");
}
@TestMetadata("TypeReference2.kt")
public void testTypeReference2() throws Exception {
runTest("idea/testData/intentions/importAllMembers/TypeReference2.kt");
}
@TestMetadata("UnresolvedMember.kt") @TestMetadata("UnresolvedMember.kt")
public void testUnresolvedMember() throws Exception { public void testUnresolvedMember() throws Exception {
runTest("idea/testData/intentions/importAllMembers/UnresolvedMember.kt"); runTest("idea/testData/intentions/importAllMembers/UnresolvedMember.kt");