Copy/Paste: Add imports for non-qualifiable callable references
#KT-23407 Fixed
This commit is contained in:
+16
-20
@@ -49,10 +49,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.elementsInRange
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
@@ -170,9 +167,11 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
||||
if (!reference.canBeResolvedViaImport(descriptor, bindingContext)) continue
|
||||
|
||||
val fqName = descriptor.importableFqName!!
|
||||
|
||||
val kind = KotlinReferenceData.Kind.fromDescriptor(descriptor) ?: continue
|
||||
add(KotlinReferenceData(element.range.start - startOffset, element.range.end - startOffset, fqName.asString(), kind))
|
||||
val isQualifiable = KotlinReferenceData.isQualifiable(element, descriptor)
|
||||
val relativeStart = element.range.start - startOffset
|
||||
val relativeEnd = element.range.end - startOffset
|
||||
add(KotlinReferenceData(relativeStart, relativeEnd, fqName.asString(), isQualifiable, kind))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,14 +260,16 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
||||
val originalFqName = FqName(refData.fqName)
|
||||
val name = originalFqName.shortName()
|
||||
|
||||
if (refData.kind == KotlinReferenceData.Kind.EXTENSION_FUNCTION) {
|
||||
if (fileResolutionScope.findFunction(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
||||
return null // already imported
|
||||
if (!refData.isQualifiable) {
|
||||
if (refData.kind == KotlinReferenceData.Kind.FUNCTION) {
|
||||
if (fileResolutionScope.findFunction(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
||||
return null // already imported
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (refData.kind == KotlinReferenceData.Kind.EXTENSION_PROPERTY) {
|
||||
if (fileResolutionScope.findVariable(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
||||
return null // already imported
|
||||
else if (refData.kind == KotlinReferenceData.Kind.PROPERTY) {
|
||||
if (fileResolutionScope.findVariable(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
||||
return null // already imported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +313,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
||||
for ((reference, refData) in referencesToRestore) {
|
||||
val fqName = FqName(refData.fqName)
|
||||
|
||||
if (!refData.kind.isExtension()) {
|
||||
if (refData.isQualifiable) {
|
||||
if (reference is KtSimpleNameReference) {
|
||||
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.element, file)
|
||||
bindingRequests.add(BindingRequest(pointer, fqName))
|
||||
@@ -320,9 +321,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
||||
else if (reference is KDocReference) {
|
||||
descriptorsToImport.addAll(findImportableDescriptors(fqName, file))
|
||||
}
|
||||
}
|
||||
|
||||
if (refData.kind.isExtension()) {
|
||||
} else {
|
||||
descriptorsToImport.addIfNotNull(findCallableToImport(fqName, file))
|
||||
}
|
||||
}
|
||||
@@ -337,9 +336,6 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
||||
performDelayedRefactoringRequests(file.project)
|
||||
}
|
||||
|
||||
private fun KotlinReferenceData.Kind.isExtension()
|
||||
= this == KotlinReferenceData.Kind.EXTENSION_FUNCTION || this == KotlinReferenceData.Kind.EXTENSION_PROPERTY
|
||||
|
||||
private fun findImportableDescriptors(fqName: FqName, file: KtFile): Collection<DeclarationDescriptor> {
|
||||
return file.resolveImportReference(fqName).filterNot {
|
||||
/*TODO: temporary hack until we don't have ability to insert qualified reference into root package*/
|
||||
|
||||
@@ -18,8 +18,15 @@ package org.jetbrains.kotlin.idea.codeInsight
|
||||
|
||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
|
||||
import java.awt.datatransfer.DataFlavor
|
||||
import java.io.Serializable
|
||||
|
||||
@@ -56,34 +63,23 @@ class KotlinReferenceData(
|
||||
var startOffset: Int,
|
||||
var endOffset: Int,
|
||||
val fqName: String,
|
||||
val isQualifiable: Boolean,
|
||||
val kind: KotlinReferenceData.Kind
|
||||
) : Cloneable, Serializable {
|
||||
|
||||
enum class Kind {
|
||||
CLASS,
|
||||
PACKAGE,
|
||||
NON_EXTENSION_CALLABLE,
|
||||
EXTENSION_FUNCTION,
|
||||
EXTENSION_PROPERTY;
|
||||
FUNCTION,
|
||||
PROPERTY;
|
||||
|
||||
companion object {
|
||||
fun fromDescriptor(descriptor: DeclarationDescriptor): KotlinReferenceData.Kind? {
|
||||
return when (descriptor.getImportableDescriptor()) {
|
||||
is ClassDescriptor ->
|
||||
KotlinReferenceData.Kind.CLASS
|
||||
|
||||
is PackageViewDescriptor ->
|
||||
KotlinReferenceData.Kind.PACKAGE
|
||||
|
||||
is FunctionDescriptor ->
|
||||
if (descriptor.isExtension) KotlinReferenceData.Kind.EXTENSION_FUNCTION else KotlinReferenceData.Kind.NON_EXTENSION_CALLABLE
|
||||
|
||||
is PropertyDescriptor ->
|
||||
if (descriptor.isExtension) KotlinReferenceData.Kind.EXTENSION_PROPERTY else KotlinReferenceData.Kind.NON_EXTENSION_CALLABLE
|
||||
|
||||
else ->
|
||||
null
|
||||
}
|
||||
fun fromDescriptor(descriptor: DeclarationDescriptor) = when (descriptor.getImportableDescriptor()) {
|
||||
is ClassDescriptor -> CLASS
|
||||
is PackageViewDescriptor -> PACKAGE
|
||||
is FunctionDescriptor -> FUNCTION
|
||||
is PropertyDescriptor -> PROPERTY
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,5 +108,19 @@ class KotlinReferenceData(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun isQualifiable(refElement: KtElement, descriptor: DeclarationDescriptor): Boolean {
|
||||
refElement.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference }?.let {
|
||||
val receiverExpression = it.receiverExpression
|
||||
|
||||
if (receiverExpression != null) {
|
||||
val lhs = it.analyze(BodyResolveMode.PARTIAL)[BindingContext.DOUBLE_COLON_LHS, receiverExpression]
|
||||
if (lhs is DoubleColonLHS.Expression) return false
|
||||
}
|
||||
return descriptor.containingDeclaration is ClassifierDescriptor
|
||||
}
|
||||
|
||||
return !descriptor.isExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
import completion.p23381.funReference
|
||||
|
||||
fun pasteHere() {
|
||||
::funReference
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
completion.p23381.funReference
|
||||
@@ -0,0 +1,9 @@
|
||||
package completion.p23381
|
||||
|
||||
fun funReference() {}
|
||||
|
||||
class InputImpl {
|
||||
fun context() {
|
||||
<selection>::funReference</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
fun pasteHere() {
|
||||
<caret>
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
import completion.p23381.A
|
||||
import completion.p23381.funReference
|
||||
|
||||
fun pasteHere() {
|
||||
A()::funReference
|
||||
}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
completion.p23381.A
|
||||
completion.p23381.funReference
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package completion.p23381
|
||||
|
||||
class A
|
||||
|
||||
fun A.funReference() {}
|
||||
|
||||
class InputImpl {
|
||||
fun context() {
|
||||
<selection>A()::funReference</selection>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
fun pasteHere() {
|
||||
<caret>
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
import completion.p23381.A
|
||||
import completion.p23381.funReference
|
||||
|
||||
fun pasteHere() {
|
||||
A::funReference
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
completion.p23381.A
|
||||
completion.p23381.funReference
|
||||
@@ -0,0 +1,11 @@
|
||||
package completion.p23381
|
||||
|
||||
class A
|
||||
|
||||
fun A.funReference() {}
|
||||
|
||||
class InputImpl {
|
||||
fun context() {
|
||||
<selection>A::funReference</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
fun pasteHere() {
|
||||
<caret>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
import completion.p23381.A
|
||||
|
||||
fun pasteHere() {
|
||||
A()::funReference
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
completion.p23381.A
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package completion.p23381
|
||||
|
||||
class A {
|
||||
fun funReference() {}
|
||||
}
|
||||
|
||||
class InputImpl {
|
||||
fun context() {
|
||||
<selection>A()::funReference</selection>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
fun pasteHere() {
|
||||
<caret>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
import completion.p23381.A
|
||||
|
||||
fun pasteHere() {
|
||||
A::funReference
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
completion.p23381.A
|
||||
@@ -0,0 +1,11 @@
|
||||
package completion.p23381
|
||||
|
||||
class A {
|
||||
fun funReference() {}
|
||||
}
|
||||
|
||||
class InputImpl {
|
||||
fun context() {
|
||||
<selection>A::funReference</selection>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package completion.p23381.apx2
|
||||
|
||||
fun pasteHere() {
|
||||
<caret>
|
||||
}
|
||||
+50
@@ -306,6 +306,31 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
||||
runTest("idea/testData/copyPaste/imports/ThisReference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelCallableRef.kt")
|
||||
public void testTopLevelCallableRef() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelCallableRef.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelExtensionCallableRefWithExpressionLHS.kt")
|
||||
public void testTopLevelExtensionCallableRefWithExpressionLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelExtensionCallableRefWithTypeLHS.kt")
|
||||
public void testTopLevelExtensionCallableRefWithTypeLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelMemberCallableRefWithExpressionLHS.kt")
|
||||
public void testTopLevelMemberCallableRefWithExpressionLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelMemberCallableRefWithTypeLHS.kt")
|
||||
public void testTopLevelMemberCallableRefWithTypeLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");
|
||||
@@ -619,6 +644,31 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
||||
runTest("idea/testData/copyPaste/imports/ThisReference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelCallableRef.kt")
|
||||
public void testTopLevelCallableRef() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelCallableRef.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelExtensionCallableRefWithExpressionLHS.kt")
|
||||
public void testTopLevelExtensionCallableRefWithExpressionLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelExtensionCallableRefWithTypeLHS.kt")
|
||||
public void testTopLevelExtensionCallableRefWithTypeLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelMemberCallableRefWithExpressionLHS.kt")
|
||||
public void testTopLevelMemberCallableRefWithExpressionLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelMemberCallableRefWithTypeLHS.kt")
|
||||
public void testTopLevelMemberCallableRefWithTypeLHS() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TopLevelProperty.kt")
|
||||
public void testTopLevelProperty() throws Exception {
|
||||
runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");
|
||||
|
||||
Reference in New Issue
Block a user