Smart completion: "::functionName" items when value of function type is expected
This commit is contained in:
@@ -43,6 +43,7 @@ import org.jetbrains.jet.plugin.project.ResolveSessionForBodies;
|
||||
import org.jetbrains.jet.plugin.references.JetSimpleNameReference;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
class CompletionSession {
|
||||
@Nullable
|
||||
@@ -133,12 +134,17 @@ class CompletionSession {
|
||||
public void completeSmart() {
|
||||
assert parameters.getCompletionType() == CompletionType.SMART;
|
||||
|
||||
final SmartCompletionData data = CompletionPackage.buildSmartCompletionData(jetReference.getExpression(), getResolveSession());
|
||||
final SmartCompletionData data = CompletionPackage.buildSmartCompletionData(jetReference.getExpression(), getResolveSession(), new Function1<DeclarationDescriptor, Boolean>() {
|
||||
@Override
|
||||
public Boolean invoke(DeclarationDescriptor descriptor) {
|
||||
return isVisibleDescriptor(descriptor);
|
||||
}
|
||||
});
|
||||
if (data != null) {
|
||||
addReferenceVariants(new Function1<DeclarationDescriptor, LookupElement>() {
|
||||
addReferenceVariants(new Function1<DeclarationDescriptor, Iterable<LookupElement>>(){
|
||||
@Override
|
||||
public LookupElement invoke(DeclarationDescriptor descriptor) {
|
||||
return data.toElement(descriptor);
|
||||
public Iterable<LookupElement> invoke(DeclarationDescriptor descriptor) {
|
||||
return data.toElements(descriptor);
|
||||
}
|
||||
});
|
||||
for (LookupElement element : data.getAdditionalElements()) {
|
||||
@@ -242,22 +248,25 @@ class CompletionSession {
|
||||
}
|
||||
|
||||
private void addReferenceVariants(@NotNull final Condition<DeclarationDescriptor> filterCondition) {
|
||||
addReferenceVariants(new Function1<DeclarationDescriptor, LookupElement>() {
|
||||
addReferenceVariants(new Function1<DeclarationDescriptor, Iterable<LookupElement>>(){
|
||||
@Override
|
||||
public LookupElement invoke(DeclarationDescriptor descriptor) {
|
||||
return filterCondition.value(descriptor) ? DescriptorLookupConverter.createLookupElement(getResolveSession(), getExpressionBindingContext(), descriptor) : null;
|
||||
public Iterable<LookupElement> invoke(DeclarationDescriptor descriptor) {
|
||||
return filterCondition.value(descriptor)
|
||||
? Collections.singletonList(
|
||||
DescriptorLookupConverter.createLookupElement(getResolveSession(), getExpressionBindingContext(), descriptor))
|
||||
: Collections.<LookupElement>emptyList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addReferenceVariants(@NotNull Function1<DeclarationDescriptor, LookupElement> filter) {
|
||||
private void addReferenceVariants(@NotNull Function1<DeclarationDescriptor, Iterable<LookupElement>> filter) {
|
||||
Collection<DeclarationDescriptor> descriptors = TipsManager.getReferenceVariants(
|
||||
jetReference.getExpression(), getExpressionBindingContext());
|
||||
|
||||
for (DeclarationDescriptor descriptor : descriptors) {
|
||||
if (descriptor != null && descriptorFilter.value(descriptor)) {
|
||||
LookupElement element = filter.invoke(descriptor);
|
||||
if (element != null) {
|
||||
Iterable<LookupElement> elements = filter.invoke(descriptor);
|
||||
for (LookupElement element : elements) {
|
||||
jetResult.addElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import com.intellij.lang.ASTNode
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope
|
||||
|
||||
trait SmartCompletionData{
|
||||
fun toElement(descriptor: DeclarationDescriptor): LookupElement?
|
||||
fun toElements(descriptor: DeclarationDescriptor): Iterable<LookupElement>
|
||||
val additionalElements: Iterable<LookupElement>
|
||||
}
|
||||
|
||||
@@ -46,7 +46,9 @@ enum class Tail {
|
||||
|
||||
data class ExpectedTypeInfo(val `type`: JetType, val tail: Tail?)
|
||||
|
||||
fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession: ResolveSessionForBodies): SmartCompletionData? {
|
||||
fun buildSmartCompletionData(expression: JetSimpleNameExpression,
|
||||
resolveSession: ResolveSessionForBodies,
|
||||
visibilityFilter: (DeclarationDescriptor) -> Boolean): SmartCompletionData? {
|
||||
val parent = expression.getParent()
|
||||
val expressionWithType: JetExpression;
|
||||
val receiver: JetExpression?
|
||||
@@ -107,12 +109,58 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession
|
||||
}
|
||||
|
||||
return object: SmartCompletionData {
|
||||
override fun toElement(descriptor: DeclarationDescriptor): LookupElement? {
|
||||
if (itemsToSkip.contains(descriptor)) return null
|
||||
val matchedExpectedTypes = expectedTypes.filter { expectedType -> typesOf(descriptor).any { descriptorType -> isSubtypeOf(descriptorType, expectedType.`type`) } }
|
||||
if (matchedExpectedTypes.isEmpty()) return null
|
||||
val tail = mergeTails(matchedExpectedTypes.map { it.tail })
|
||||
return decorateLookupElement(DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor), tail)
|
||||
override fun toElements(descriptor: DeclarationDescriptor): Iterable<LookupElement> {
|
||||
if (itemsToSkip.contains(descriptor)) return listOf()
|
||||
|
||||
val result = ArrayList<LookupElement>()
|
||||
|
||||
run {
|
||||
val matchedExpectedTypes = expectedTypes.filter { expectedType -> typesOf(descriptor).any { descriptorType -> isSubtypeOf(descriptorType, expectedType.`type`) } }
|
||||
if (matchedExpectedTypes.isNotEmpty()) {
|
||||
val tail = mergeTails(matchedExpectedTypes.map { it.tail })
|
||||
result.add(addTailToLookupElement(DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor), tail))
|
||||
}
|
||||
}
|
||||
|
||||
val functionExpectedTypes = expectedTypes.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it.`type`) }
|
||||
if (functionExpectedTypes.isNotEmpty()) {
|
||||
fun functionReferenceLookupElement(descriptor: FunctionDescriptor): LookupElement? {
|
||||
val functionType = functionType(descriptor)
|
||||
if (functionType == null) return null
|
||||
|
||||
val matchedExpectedTypes = functionExpectedTypes.filter { isSubtypeOf(functionType, it.`type`) }
|
||||
if (matchedExpectedTypes.isEmpty()) return null
|
||||
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
|
||||
val text = "::" + (if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName())
|
||||
val lookupElementDecorated = object: LookupElementDecorator<LookupElement>(lookupElement) {
|
||||
override fun getLookupString() = text
|
||||
|
||||
override fun renderElement(presentation: LookupElementPresentation) {
|
||||
super.renderElement(presentation)
|
||||
presentation.setItemText(text)
|
||||
presentation.setTypeText("")
|
||||
}
|
||||
|
||||
override fun handleInsert(context: InsertionContext) {
|
||||
}
|
||||
}
|
||||
|
||||
val tail = mergeTails(matchedExpectedTypes.map { it.tail })
|
||||
return addTailToLookupElement(lookupElementDecorated, tail)
|
||||
}
|
||||
|
||||
if (descriptor is SimpleFunctionDescriptor) {
|
||||
functionReferenceLookupElement(descriptor)?.let { result.add(it) }
|
||||
}
|
||||
else if (descriptor is ClassDescriptor && descriptor.getModality() != Modality.ABSTRACT) {
|
||||
val constructors = descriptor.getConstructors().filter(visibilityFilter)
|
||||
if (constructors.size == 1) { //TODO: this code is to be changed if overloads to start work after ::
|
||||
functionReferenceLookupElement(constructors.single())?.let { result.add(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override val additionalElements = additionalElements
|
||||
@@ -210,6 +258,8 @@ private fun MutableCollection<LookupElement>.addTypeInstantiationItems(expectedT
|
||||
}
|
||||
|
||||
private fun MutableCollection<LookupElement>.addTypeInstantiationItems(jetType: JetType, tail: Tail?, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext) {
|
||||
if (KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(jetType)) return // do not show "object: ..." for function types
|
||||
|
||||
val classifier = jetType.getConstructor().getDeclarationDescriptor()
|
||||
if (!(classifier is ClassDescriptor)) return
|
||||
//TODO: check for constructor's visibility
|
||||
@@ -289,7 +339,7 @@ private fun MutableCollection<LookupElement>.addTypeInstantiationItems(jetType:
|
||||
}
|
||||
}
|
||||
|
||||
val lookupElementWithTail = decorateLookupElement(lookupElementDecorated, tail)
|
||||
val lookupElementWithTail = addTailToLookupElement(lookupElementDecorated, tail)
|
||||
|
||||
if (suppressAutoInsertion) {
|
||||
add(AutoCompletionPolicy.NEVER_AUTOCOMPLETE.applyPolicy(lookupElementWithTail))
|
||||
@@ -315,7 +365,7 @@ private fun MutableCollection<LookupElement>.addThisItems(context: JetExpression
|
||||
val expressionText = if (qualifier == null) "this" else "this@" + qualifier
|
||||
val lookupElement = LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.TEXT.renderType(thisType))
|
||||
val tailType = mergeTails(matchedExpectedTypes.map { it.tail })
|
||||
add(decorateLookupElement(lookupElement, tailType))
|
||||
add(addTailToLookupElement(lookupElement, tailType))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -501,7 +551,7 @@ private fun MutableCollection<LookupElement>.addStaticMembers(classDescriptor: C
|
||||
}
|
||||
|
||||
val tail = mergeTails(descriptorExpectedTypes.map { it.tail })
|
||||
add(decorateLookupElement(lookupElementDecorated, tail))
|
||||
add(addTailToLookupElement(lookupElementDecorated, tail))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -510,7 +560,7 @@ private fun mergeTails(tails: Collection<Tail?>): Tail? {
|
||||
return if (HashSet(tails).size == 1) tails.first() else null
|
||||
}
|
||||
|
||||
private fun decorateLookupElement(lookupElement: LookupElement, tail: Tail?): LookupElement {
|
||||
private fun addTailToLookupElement(lookupElement: LookupElement, tail: Tail?): LookupElement {
|
||||
return when (tail) {
|
||||
null -> lookupElement
|
||||
|
||||
@@ -528,10 +578,16 @@ private fun decorateLookupElement(lookupElement: LookupElement, tail: Tail?): Lo
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSubtypeOf(t: JetType, expectedType: JetType): Boolean{
|
||||
return !t.isError() && JetTypeChecker.INSTANCE.isSubtypeOf(t, expectedType)
|
||||
private fun functionType(function: FunctionDescriptor): JetType? {
|
||||
return KotlinBuiltIns.getInstance().getKFunctionType(function.getAnnotations(),
|
||||
null,
|
||||
function.getValueParameters().map { it.getType() },
|
||||
function.getReturnType() ?: return null,
|
||||
function.getReceiverParameter() != null)
|
||||
}
|
||||
|
||||
private fun isSubtypeOf(t: JetType, expectedType: JetType) = !t.isError() && JetTypeChecker.INSTANCE.isSubtypeOf(t, expectedType)
|
||||
|
||||
private fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
|
||||
|
||||
private fun String?.isNullOrEmpty() = this == null || this.isEmpty()
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fun foo(p: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
fun f(){}
|
||||
|
||||
// ELEMENT: ::f
|
||||
@@ -0,0 +1,9 @@
|
||||
fun foo(p: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(::f)<caret>
|
||||
}
|
||||
|
||||
fun f(){}
|
||||
|
||||
// ELEMENT: ::f
|
||||
@@ -0,0 +1,9 @@
|
||||
fun foo(){}
|
||||
|
||||
class C{}
|
||||
|
||||
val v: Any = <caret>
|
||||
|
||||
// ABSENT: ::foo
|
||||
// ABSENT: ::C
|
||||
// ABSENT: ::Runnable
|
||||
@@ -0,0 +1,14 @@
|
||||
fun foo(p: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
fun f1(){}
|
||||
fun f2(i: Int){}
|
||||
|
||||
// EXIST: { lookupString:"::f1", itemText:"::f1", tailText:"", typeText:"" }
|
||||
// ABSENT: ::f2
|
||||
// ABSENT: ::Unit
|
||||
// ABSENT: ::Nothing
|
||||
// ABSENT: { lookupString:"object" }
|
||||
@@ -0,0 +1,10 @@
|
||||
fun foo(p: () -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
fun f(){}
|
||||
fun f(i: Int){}
|
||||
|
||||
// ABSENT: ::f
|
||||
@@ -0,0 +1,11 @@
|
||||
fun foo(p: () -> Unit)){}
|
||||
fun foo(p: () -> (() -> Unit)){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
fun f(): () -> Unit {}
|
||||
|
||||
// EXIST: ::f
|
||||
// EXIST: f
|
||||
@@ -0,0 +1,11 @@
|
||||
fun foo(p: (() -> Unit)?){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
fun f1(){}
|
||||
fun f2(i: Int){}
|
||||
|
||||
// EXIST: ::f1
|
||||
// ABSENT: ::f2
|
||||
@@ -0,0 +1,9 @@
|
||||
class C(i: Int){}
|
||||
|
||||
fun foo(p: (Int) -> C){}
|
||||
|
||||
fun bar(){
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
// EXIST: ::C
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(p: (() -> Unit) -> Runnable){}
|
||||
|
||||
fun bar(){
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
// EXIST: ::Runnable
|
||||
@@ -0,0 +1,9 @@
|
||||
import java.util.Date
|
||||
|
||||
fun foo(p: (Long) -> Date){}
|
||||
|
||||
fun bar(){
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
// ABSENT: ::Date
|
||||
@@ -0,0 +1,11 @@
|
||||
fun foo(p: String.() -> Unit){}
|
||||
|
||||
fun bar() {
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
fun String.f1(){}
|
||||
fun f2(){}
|
||||
|
||||
// EXIST: ::f1
|
||||
// ABSENT: ::f2
|
||||
@@ -0,0 +1,9 @@
|
||||
abstract class C(i: Int){}
|
||||
|
||||
fun foo(p: (Int) -> C){}
|
||||
|
||||
fun bar(){
|
||||
foo(<caret>)
|
||||
}
|
||||
|
||||
// ABSENT: ::C
|
||||
@@ -51,6 +51,11 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
doTest("idea/testData/completion/smart/AnonymousObjectForJavaInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("AnyExpected.kt")
|
||||
public void testAnyExpected() throws Exception {
|
||||
doTest("idea/testData/completion/smart/AnyExpected.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("AutoCastedType.kt")
|
||||
public void testAutoCastedType() throws Exception {
|
||||
doTest("idea/testData/completion/smart/AutoCastedType.kt");
|
||||
@@ -126,6 +131,41 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
||||
doTest("idea/testData/completion/smart/EnumMembers.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected1.kt")
|
||||
public void testFunctionExpected1() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected3.kt")
|
||||
public void testFunctionExpected3() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected4.kt")
|
||||
public void testFunctionExpected4() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected5.kt")
|
||||
public void testFunctionExpected5() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected6.kt")
|
||||
public void testFunctionExpected6() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected6.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected7.kt")
|
||||
public void testFunctionExpected7() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected7.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionExpected9.kt")
|
||||
public void testFunctionExpected9() throws Exception {
|
||||
doTest("idea/testData/completion/smart/FunctionExpected9.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("InsideIdentifier.kt")
|
||||
public void testInsideIdentifier() throws Exception {
|
||||
doTest("idea/testData/completion/smart/InsideIdentifier.kt");
|
||||
|
||||
+5
@@ -161,6 +161,11 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
|
||||
doTest("idea/testData/completion/handlers/smart/EnumMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionReference.kt")
|
||||
public void testFunctionReference() throws Exception {
|
||||
doTest("idea/testData/completion/handlers/smart/FunctionReference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("JavaEnumMemberInsertsImport.kt")
|
||||
public void testJavaEnumMemberInsertsImport() throws Exception {
|
||||
doTest("idea/testData/completion/handlers/smart/JavaEnumMemberInsertsImport.kt");
|
||||
|
||||
Reference in New Issue
Block a user