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.kdoc.psi.api.KDocElement
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
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.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
@@ -170,9 +167,11 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
|||||||
if (!reference.canBeResolvedViaImport(descriptor, bindingContext)) continue
|
if (!reference.canBeResolvedViaImport(descriptor, bindingContext)) continue
|
||||||
|
|
||||||
val fqName = descriptor.importableFqName!!
|
val fqName = descriptor.importableFqName!!
|
||||||
|
|
||||||
val kind = KotlinReferenceData.Kind.fromDescriptor(descriptor) ?: continue
|
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 originalFqName = FqName(refData.fqName)
|
||||||
val name = originalFqName.shortName()
|
val name = originalFqName.shortName()
|
||||||
|
|
||||||
if (refData.kind == KotlinReferenceData.Kind.EXTENSION_FUNCTION) {
|
if (!refData.isQualifiable) {
|
||||||
if (fileResolutionScope.findFunction(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
if (refData.kind == KotlinReferenceData.Kind.FUNCTION) {
|
||||||
return null // already imported
|
if (fileResolutionScope.findFunction(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
||||||
|
return null // already imported
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (refData.kind == KotlinReferenceData.Kind.PROPERTY) {
|
||||||
else if (refData.kind == KotlinReferenceData.Kind.EXTENSION_PROPERTY) {
|
if (fileResolutionScope.findVariable(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
||||||
if (fileResolutionScope.findVariable(name, NoLookupLocation.FROM_IDE) { it.importableFqName == originalFqName } != null) {
|
return null // already imported
|
||||||
return null // already imported
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +313,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
|||||||
for ((reference, refData) in referencesToRestore) {
|
for ((reference, refData) in referencesToRestore) {
|
||||||
val fqName = FqName(refData.fqName)
|
val fqName = FqName(refData.fqName)
|
||||||
|
|
||||||
if (!refData.kind.isExtension()) {
|
if (refData.isQualifiable) {
|
||||||
if (reference is KtSimpleNameReference) {
|
if (reference is KtSimpleNameReference) {
|
||||||
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.element, file)
|
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.element, file)
|
||||||
bindingRequests.add(BindingRequest(pointer, fqName))
|
bindingRequests.add(BindingRequest(pointer, fqName))
|
||||||
@@ -320,9 +321,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
|||||||
else if (reference is KDocReference) {
|
else if (reference is KDocReference) {
|
||||||
descriptorsToImport.addAll(findImportableDescriptors(fqName, file))
|
descriptorsToImport.addAll(findImportableDescriptors(fqName, file))
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
if (refData.kind.isExtension()) {
|
|
||||||
descriptorsToImport.addIfNotNull(findCallableToImport(fqName, file))
|
descriptorsToImport.addIfNotNull(findCallableToImport(fqName, file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,9 +336,6 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor<KotlinReference
|
|||||||
performDelayedRefactoringRequests(file.project)
|
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> {
|
private fun findImportableDescriptors(fqName: FqName, file: KtFile): Collection<DeclarationDescriptor> {
|
||||||
return file.resolveImportReference(fqName).filterNot {
|
return file.resolveImportReference(fqName).filterNot {
|
||||||
/*TODO: temporary hack until we don't have ability to insert qualified reference into root package*/
|
/*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 com.intellij.codeInsight.editorActions.TextBlockTransferableData
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
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.getImportableDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
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.awt.datatransfer.DataFlavor
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
@@ -56,34 +63,23 @@ class KotlinReferenceData(
|
|||||||
var startOffset: Int,
|
var startOffset: Int,
|
||||||
var endOffset: Int,
|
var endOffset: Int,
|
||||||
val fqName: String,
|
val fqName: String,
|
||||||
|
val isQualifiable: Boolean,
|
||||||
val kind: KotlinReferenceData.Kind
|
val kind: KotlinReferenceData.Kind
|
||||||
) : Cloneable, Serializable {
|
) : Cloneable, Serializable {
|
||||||
|
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
CLASS,
|
CLASS,
|
||||||
PACKAGE,
|
PACKAGE,
|
||||||
NON_EXTENSION_CALLABLE,
|
FUNCTION,
|
||||||
EXTENSION_FUNCTION,
|
PROPERTY;
|
||||||
EXTENSION_PROPERTY;
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromDescriptor(descriptor: DeclarationDescriptor): KotlinReferenceData.Kind? {
|
fun fromDescriptor(descriptor: DeclarationDescriptor) = when (descriptor.getImportableDescriptor()) {
|
||||||
return when (descriptor.getImportableDescriptor()) {
|
is ClassDescriptor -> CLASS
|
||||||
is ClassDescriptor ->
|
is PackageViewDescriptor -> PACKAGE
|
||||||
KotlinReferenceData.Kind.CLASS
|
is FunctionDescriptor -> FUNCTION
|
||||||
|
is PropertyDescriptor -> PROPERTY
|
||||||
is PackageViewDescriptor ->
|
else -> null
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,5 +108,19 @@ class KotlinReferenceData(
|
|||||||
null
|
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");
|
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")
|
@TestMetadata("TopLevelProperty.kt")
|
||||||
public void testTopLevelProperty() throws Exception {
|
public void testTopLevelProperty() throws Exception {
|
||||||
runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");
|
runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");
|
||||||
@@ -619,6 +644,31 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
|||||||
runTest("idea/testData/copyPaste/imports/ThisReference.kt");
|
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")
|
@TestMetadata("TopLevelProperty.kt")
|
||||||
public void testTopLevelProperty() throws Exception {
|
public void testTopLevelProperty() throws Exception {
|
||||||
runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");
|
runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user