Copy/Paste: Add imports for non-qualifiable callable references

#KT-23407 Fixed
This commit is contained in:
Alexey Sedunov
2018-06-08 21:40:59 +03:00
parent 694997651a
commit f7cda61b08
23 changed files with 218 additions and 40 deletions
@@ -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>
}
@@ -0,0 +1,8 @@
package completion.p23381.apx2
import completion.p23381.A
import completion.p23381.funReference
fun pasteHere() {
A()::funReference
}
@@ -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>
}
@@ -0,0 +1,8 @@
package completion.p23381.apx2
import completion.p23381.A
import completion.p23381.funReference
fun pasteHere() {
A::funReference
}
@@ -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>
}
@@ -0,0 +1,7 @@
package completion.p23381.apx2
import completion.p23381.A
fun pasteHere() {
A()::funReference
}
@@ -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>
}
@@ -0,0 +1,7 @@
package completion.p23381.apx2
import completion.p23381.A
fun pasteHere() {
A::funReference
}
@@ -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>
}
@@ -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");