Moved creation of additional items like special items for functions accepting lambda into LookupElementFactory to provide them in smart completion too

This commit is contained in:
Valentin Kipyatkov
2015-08-26 18:02:14 +03:00
parent 5b3e83d547
commit 363295f6af
18 changed files with 213 additions and 197 deletions
@@ -334,7 +334,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
superClasses superClasses
.map { lookupElementFactory.createLookupElement(it, boldImmediateMembers = false, qualifyNestedClasses = true, includeClassTypeArguments = false) } .map { lookupElementFactory.createLookupElement(it, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) }
.forEach { collector.addElement(it) } .forEach { collector.addElement(it) }
} }
@@ -140,6 +140,13 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected val receiversData: ReferenceVariantsHelper.ReceiversData? = nameExpression?.let { referenceVariantsHelper.getReferenceVariantsReceivers(it) } protected val receiversData: ReferenceVariantsHelper.ReceiversData? = nameExpression?.let { referenceVariantsHelper.getReferenceVariantsReceivers(it) }
private val factoryContext = if (expression?.getParent() is JetSimpleNameStringTemplateEntry)
LookupElementFactory.Context.STRING_TEMPLATE_AFTER_DOLLAR
else if (receiversData?.callType == CallType.INFIX)
LookupElementFactory.Context.INFIX_CALL
else
LookupElementFactory.Context.NORMAL
protected val lookupElementFactory: LookupElementFactory = run { protected val lookupElementFactory: LookupElementFactory = run {
var receiverTypes = emptyList<JetType>() var receiverTypes = emptyList<JetType>()
if (receiversData != null) { if (receiversData != null) {
@@ -161,19 +168,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
} }
} }
LookupElementFactory(resolutionFacade, receiverTypes, { expectedInfos }) LookupElementFactory(resolutionFacade, receiverTypes, factoryContext, inDescriptor, { expectedInfos })
} }
private val collectorContext = if (expression?.getParent() is JetSimpleNameStringTemplateEntry)
LookupElementsCollector.Context.STRING_TEMPLATE_AFTER_DOLLAR
else if (receiversData?.callType == CallType.INFIX)
LookupElementsCollector.Context.INFIX_CALL
else
LookupElementsCollector.Context.NORMAL
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
protected val collector: LookupElementsCollector by lazy { protected val collector: LookupElementsCollector by lazy {
LookupElementsCollector(prefixMatcher, parameters, resultSet, resolutionFacade, lookupElementFactory, createSorter(), inDescriptor, collectorContext) LookupElementsCollector(prefixMatcher, parameters, resultSet, lookupElementFactory, createSorter())
} }
protected val originalSearchScope: GlobalSearchScope = getResolveScope(parameters.getOriginalFile() as JetFile) protected val originalSearchScope: GlobalSearchScope = getResolveScope(parameters.getOriginalFile() as JetFile)
@@ -295,6 +295,7 @@ fun breakOrContinueExpressionItems(position: JetElement, breakOrContinue: String
fun LookupElementFactory.createBackingFieldLookupElement( fun LookupElementFactory.createBackingFieldLookupElement(
property: PropertyDescriptor, property: PropertyDescriptor,
useReceiverTypes: Boolean,
inDescriptor: DeclarationDescriptor, inDescriptor: DeclarationDescriptor,
resolutionFacade: ResolutionFacade resolutionFacade: ResolutionFacade
): LookupElement? { ): LookupElement? {
@@ -312,7 +313,7 @@ fun LookupElementFactory.createBackingFieldLookupElement(
val bindingContext = resolutionFacade.analyze(declaration) val bindingContext = resolutionFacade.analyze(declaration)
if (!bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property]!!) return null if (!bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property]!!) return null
val lookupElement = createLookupElement(property, true) val lookupElement = createLookupElement(property, useReceiverTypes)
return object : LookupElementDecorator<LookupElement>(lookupElement) { return object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = "$" + super.getLookupString() override fun getLookupString() = "$" + super.getLookupString()
override fun getAllLookupStrings() = setOf(getLookupString()) override fun getAllLookupStrings() = setOf(getLookupString())
@@ -335,7 +336,7 @@ fun LookupElementFactory.createLookupElementForType(type: JetType): LookupElemen
} }
else { else {
val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null
val baseLookupElement = createLookupElement(classifier, false, qualifyNestedClasses = true, includeClassTypeArguments = false) val baseLookupElement = createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false)
val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type) val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
@@ -87,7 +86,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
.filter { it.getName().asString() !in documentedParameters } .filter { it.getName().asString() !in documentedParameters }
descriptors.forEach { descriptors.forEach {
collector.addElement(lookupElementFactory.createLookupElement(it, false)) collector.addElement(lookupElementFactory.createLookupElement(it, useReceiverTypes = false))
} }
} }
@@ -108,7 +107,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
} }
scope.getDescriptorsFiltered(nameFilter = descriptorNameFilter).filter(::isApplicable).forEach { scope.getDescriptorsFiltered(nameFilter = descriptorNameFilter).filter(::isApplicable).forEach {
val element = lookupElementFactory.createLookupElement(it, false) val element = lookupElementFactory.createLookupElement(it, useReceiverTypes = false)
collector.addElement(object: LookupElementDecorator<LookupElement>(element) { collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
override fun handleInsert(context: InsertionContext?) { override fun handleInsert(context: InsertionContext?) {
// insert only plain name here, no qualifier/parentheses/etc. // insert only plain name here, no qualifier/parentheses/etc.
@@ -17,11 +17,13 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.InsertHandler import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.* import com.intellij.codeInsight.lookup.*
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer import com.intellij.codeInsight.lookup.impl.LookupCellRenderer
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.util.PlatformIcons import com.intellij.util.PlatformIcons
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.KotlinLightClass import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -62,13 +64,86 @@ public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObj
public class LookupElementFactory( public class LookupElementFactory(
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<JetType>, private val receiverTypes: Collection<JetType>,
private val context: LookupElementFactory.Context,
private val inDescriptor: DeclarationDescriptor?,
expectedInfosCalculator: () -> Collection<ExpectedInfo> expectedInfosCalculator: () -> Collection<ExpectedInfo>
) { ) {
public enum class Context {
NORMAL,
STRING_TEMPLATE_AFTER_DOLLAR,
INFIX_CALL
}
private val expectedInfos by lazy { expectedInfosCalculator() } private val expectedInfos by lazy { expectedInfosCalculator() }
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
val result = SmartList<LookupElement>()
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) {
lookupElement = lookupElement.withBracesSurrounding()
}
result.add(lookupElement)
// add special item for function with one argument of function type with more than one parameter
if (context != Context.INFIX_CALL && descriptor is FunctionDescriptor) {
result.addSpecialFunctionCallElements(descriptor, useReceiverTypes)
}
if (descriptor is PropertyDescriptor && inDescriptor != null) {
var backingFieldElement = createBackingFieldLookupElement(descriptor, useReceiverTypes, inDescriptor, resolutionFacade)
if (backingFieldElement != null) {
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) {
backingFieldElement = backingFieldElement.withBracesSurrounding()
}
result.add(backingFieldElement)
}
}
return result
}
private fun MutableCollection<LookupElement>.addSpecialFunctionCallElements(descriptor: FunctionDescriptor, useReceiverTypes: Boolean) {
val singleParameter = descriptor.valueParameters.singleOrNull() ?: return
val parameterType = singleParameter.type
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
val functionParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
if (functionParameterCount > 1) {
add(createFunctionCallElementWithExplicitLambdaParameters(descriptor, parameterType, useReceiverTypes))
}
}
}
private fun createFunctionCallElementWithExplicitLambdaParameters(descriptor: FunctionDescriptor, parameterType: JetType, useReceiverTypes: Boolean): LookupElement {
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val needTypeArguments = (getDefaultInsertHandler(descriptor) as KotlinFunctionInsertHandler).needTypeArguments
val lambdaInfo = GenerateLambdaInfo(parameterType, true)
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
val tails = presentation.getTailFragments()
presentation.clearTail()
presentation.appendTailText(" " + buildLambdaPresentation(parameterType) + " ", false)
tails.forEach { presentation.appendTailText(it.text, true) }
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true, lambdaInfo = lambdaInfo).handleInsert(context, this)
}
}
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) {
lookupElement = lookupElement.withBracesSurrounding()
}
return lookupElement
}
public fun createLookupElement( public fun createLookupElement(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
boldImmediateMembers: Boolean, useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean = false, qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true includeClassTypeArguments: Boolean = true
): LookupElement { ): LookupElement {
@@ -84,7 +159,7 @@ public class LookupElementFactory(
element.putUserData(CALLABLE_WEIGHT_KEY, weight) // store for use in lookup elements sorting element.putUserData(CALLABLE_WEIGHT_KEY, weight) // store for use in lookup elements sorting
} }
if (boldImmediateMembers) { if (useReceiverTypes) {
element = element.boldIfImmediate(weight) element = element.boldIfImmediate(weight)
} }
return element return element
@@ -19,35 +19,21 @@ package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.openapi.util.TextRange import com.intellij.openapi.util.TextRange
import com.intellij.patterns.ElementPattern import com.intellij.patterns.ElementPattern
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.*
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.types.JetType
import java.util.* import java.util.*
class LookupElementsCollector( class LookupElementsCollector(
private val defaultPrefixMatcher: PrefixMatcher, private val defaultPrefixMatcher: PrefixMatcher,
private val completionParameters: CompletionParameters, private val completionParameters: CompletionParameters,
resultSet: CompletionResultSet, resultSet: CompletionResultSet,
private val resolutionFacade: ResolutionFacade,
private val lookupElementFactory: LookupElementFactory, private val lookupElementFactory: LookupElementFactory,
private val sorter: CompletionSorter, private val sorter: CompletionSorter
private val inDescriptor: DeclarationDescriptor,
private val context: LookupElementsCollector.Context
) { ) {
public enum class Context {
NORMAL,
STRING_TEMPLATE_AFTER_DOLLAR,
INFIX_CALL
}
private val elements = LinkedHashMap<PrefixMatcher, ArrayList<LookupElement>>() private val elements = LinkedHashMap<PrefixMatcher, ArrayList<LookupElement>>()
@@ -89,73 +75,14 @@ class LookupElementsCollector(
public fun addDescriptorElements(descriptor: DeclarationDescriptor, notImported: Boolean = false, withReceiverCast: Boolean = false) { public fun addDescriptorElements(descriptor: DeclarationDescriptor, notImported: Boolean = false, withReceiverCast: Boolean = false) {
run { run {
var lookupElement = lookupElementFactory.createLookupElement(descriptor, true) var lookupElements = lookupElementFactory.createStandardLookupElementsForDescriptor(descriptor, useReceiverTypes = true)
if (withReceiverCast) { if (withReceiverCast) {
lookupElement = lookupElement.withReceiverCast() lookupElements = lookupElements.map { it.withReceiverCast() }
} }
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) { addElements(lookupElements, notImported = notImported)
lookupElement = lookupElement.withBracesSurrounding()
}
addElement(lookupElement, notImported = notImported)
} }
// add special item for function with one argument of function type with more than one parameter
if (context != Context.INFIX_CALL && descriptor is FunctionDescriptor) {
addSpecialFunctionDescriptorElementIfNeeded(descriptor)
}
if (descriptor is PropertyDescriptor) {
var lookupElement = lookupElementFactory.createBackingFieldLookupElement(descriptor, inDescriptor, resolutionFacade)
if (lookupElement != null) {
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) {
lookupElement = lookupElement.withBracesSurrounding()
}
addElement(lookupElement)
}
}
}
private fun addSpecialFunctionDescriptorElementIfNeeded(descriptor: FunctionDescriptor) {
val parameters = descriptor.getValueParameters()
if (parameters.size() == 1) {
val parameterType = parameters.get(0).getType()
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
val parameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
if (parameterCount > 1) {
addSpecialFunctionDescriptorElement(descriptor, parameterType)
}
}
}
}
private fun addSpecialFunctionDescriptorElement(descriptor: FunctionDescriptor, parameterType: JetType) {
var lookupElement = lookupElementFactory.createLookupElement(descriptor, true)
val needTypeArguments = (lookupElementFactory.getDefaultInsertHandler(descriptor) as KotlinFunctionInsertHandler).needTypeArguments
val lambdaInfo = GenerateLambdaInfo(parameterType, true)
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
val tails = presentation.getTailFragments()
presentation.clearTail()
presentation.appendTailText(" " + buildLambdaPresentation(parameterType) + " ", false)
tails.forEach { presentation.appendTailText(it.text, true) }
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true, lambdaInfo = lambdaInfo).handleInsert(context, this)
}
}
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR) {
lookupElement = lookupElement.withBracesSurrounding()
}
addElement(lookupElement)
} }
public fun addElement(element: LookupElement, prefixMatcher: PrefixMatcher = defaultPrefixMatcher, notImported: Boolean = false) { public fun addElement(element: LookupElement, prefixMatcher: PrefixMatcher = defaultPrefixMatcher, notImported: Boolean = false) {
@@ -53,8 +53,9 @@ object PackageDirectiveCompletion {
val bindingContext = resolutionFacade.analyze(expression) val bindingContext = resolutionFacade.analyze(expression)
val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter()) val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter())
val lookupElementFactory = LookupElementFactory(resolutionFacade, emptyList(), LookupElementFactory.Context.NORMAL, null, { emptyList() })
for (variant in variants) { for (variant in variants) {
val lookupElement = LookupElementFactory(resolutionFacade, emptyList(), { emptyList() }).createLookupElement(variant, false) val lookupElement = lookupElementFactory.createLookupElement(variant, useReceiverTypes = false)
if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) { if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(lookupElement) result.addElement(lookupElement)
} }
@@ -170,7 +170,7 @@ class ParameterNameAndTypeCompletion(
private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) { private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) {
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory) override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory)
= lookupElementFactory.createLookupElement(classifier, false, qualifyNestedClasses = true) = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true)
} }
private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) { private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) {
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
object KeywordValues { object KeywordValues {
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, expressionWithType: JetExpression) { public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, expressionWithType: JetExpression) {
@@ -54,8 +55,8 @@ object KeywordValues {
val booleanInfoClassifier = { info: ExpectedInfo -> val booleanInfoClassifier = { info: ExpectedInfo ->
if (info.fuzzyType?.type?.isBooleanOrNullableBoolean() ?: false) ExpectedInfoClassification.match(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.noMatch if (info.fuzzyType?.type?.isBooleanOrNullableBoolean() ?: false) ExpectedInfoClassification.match(TypeSubstitutor.EMPTY) else ExpectedInfoClassification.noMatch
} }
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE) } collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("true").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.TRUE).singletonList() }
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) } collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE).singletonList() }
} }
if (!shouldSkipNull(expressionWithType)) { if (!shouldSkipNull(expressionWithType)) {
@@ -66,7 +67,7 @@ object KeywordValues {
ExpectedInfoClassification.noMatch ExpectedInfoClassification.noMatch
} }
collection.addLookupElements(null, expectedInfos, classifier) { collection.addLookupElements(null, expectedInfos, classifier) {
LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL) LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL).singletonList()
} }
} }
} }
@@ -40,9 +40,9 @@ import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addToStdlib.check import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptySet import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptySet
import java.util.ArrayList import java.util.*
import java.util.HashSet
interface InheritanceItemsSearcher { interface InheritanceItemsSearcher {
fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit) fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit)
@@ -146,13 +146,7 @@ class SmartCompletion(
val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) } val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = receiver == null) { descriptor -> result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = receiver == null) { descriptor ->
lookupElementFactory.createLookupElement(descriptor, bindingContext, true) lookupElementFactory.createLookupElementsInSmartCompletion(descriptor, bindingContext, true)
}
if (descriptor is PropertyDescriptor) {
result.addLookupElements(descriptor, expectedInfos, infoClassifier) { descriptor ->
lookupElementFactory.createBackingFieldLookupElement(descriptor, inDescriptor, resolutionFacade)
}
} }
if (receiver == null) { if (receiver == null) {
@@ -232,7 +226,7 @@ class SmartCompletion(
val types = smartCastCalculator.types(item.receiverParameter).map { FuzzyType(it, emptyList()) } val types = smartCastCalculator.types(item.receiverParameter).map { FuzzyType(it, emptyList()) }
val classifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) } val classifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
addLookupElements(null, expectedInfos, classifier) { addLookupElements(null, expectedInfos, classifier) {
item.createLookupElement().assignSmartCompletionPriority(SmartCompletionItemPriority.THIS) item.createLookupElement().assignSmartCompletionPriority(SmartCompletionItemPriority.THIS).singletonList()
} }
} }
} }
@@ -287,7 +281,7 @@ class SmartCompletion(
val matchedExpectedInfos = functionExpectedInfos.filter { it.matchingSubstitutor(functionType) != null } val matchedExpectedInfos = functionExpectedInfos.filter { it.matchingSubstitutor(functionType) != null }
if (matchedExpectedInfos.isEmpty()) return null if (matchedExpectedInfos.isEmpty()) return null
var lookupElement = lookupElementFactory.createLookupElement(descriptor, bindingContext, true) var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false)
val text = "::" + (if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName()) val text = "::" + (if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration().getName() else descriptor.getName())
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) { lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = text override fun getLookupString() = text
@@ -82,7 +82,7 @@ class StaticMembers(
} }
collection.addLookupElements(descriptor, expectedInfos, classifier) { collection.addLookupElements(descriptor, expectedInfos, classifier) {
descriptor -> createLookupElement(descriptor, classDescriptor) descriptor -> createLookupElements(descriptor, classDescriptor)
} }
} }
@@ -102,46 +102,48 @@ class StaticMembers(
members.forEach(::processMember) members.forEach(::processMember)
} }
private fun createLookupElement(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): LookupElement { private fun createLookupElements(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): Collection<LookupElement> {
val lookupElement = lookupElementFactory.createLookupElement(memberDescriptor, bindingContext, false) val lookupElements = lookupElementFactory.createLookupElementsInSmartCompletion(memberDescriptor, bindingContext, useReceiverTypes = false)
val qualifierPresentation = classDescriptor.getName().asString() val qualifierPresentation = classDescriptor.getName().asString()
val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor) val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor)
return object: LookupElementDecorator<LookupElement>(lookupElement) { return lookupElements.map {
override fun getAllLookupStrings(): Set<String> { object: LookupElementDecorator<LookupElement>(it) {
return setOf(lookupElement.getLookupString(), qualifierPresentation) override fun getAllLookupStrings(): Set<String> {
} return setOf(it.lookupString, qualifierPresentation)
override fun renderElement(presentation: LookupElementPresentation) {
getDelegate().renderElement(presentation)
presentation.setItemText(qualifierPresentation + "." + presentation.getItemText())
val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")"
if (memberDescriptor is FunctionDescriptor) {
presentation.appendTailText(tailText, true)
}
else {
presentation.setTailText(tailText, true)
} }
if (presentation.getTypeText().isNullOrEmpty()) { override fun renderElement(presentation: LookupElementPresentation) {
presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType())) getDelegate().renderElement(presentation)
}
}
override fun handleInsert(context: InsertionContext) { presentation.setItemText(qualifierPresentation + "." + presentation.getItemText())
var text = qualifierText + "." + memberDescriptor.getName().render()
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text) val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")"
context.setTailOffset(context.getStartOffset() + text.length()) if (memberDescriptor is FunctionDescriptor) {
presentation.appendTailText(tailText, true)
}
else {
presentation.setTailText(tailText, true)
}
if (memberDescriptor is FunctionDescriptor) { if (presentation.getTypeText().isNullOrEmpty()) {
getDelegate().handleInsert(context) presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType()))
}
} }
shortenReferences(context, context.getStartOffset(), context.getTailOffset()) override fun handleInsert(context: InsertionContext) {
} var text = qualifierText + "." + memberDescriptor.getName().render()
}.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text)
context.setTailOffset(context.getStartOffset() + text.length())
if (memberDescriptor is FunctionDescriptor) {
getDelegate().handleInsert(context)
}
shortenReferences(context, context.getStartOffset(), context.getTailOffset())
}
}.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
}
} }
} }
@@ -123,7 +123,7 @@ class TypeInstantiationItems(
typeArgs: List<TypeProjection>, typeArgs: List<TypeProjection>,
tail: Tail? tail: Tail?
): LookupElement? { ): LookupElement? {
var lookupElement = lookupElementFactory.createLookupElement(classifier, bindingContext, false) var lookupElement = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false)
if (DescriptorUtils.isNonCompanionObject(classifier)) { if (DescriptorUtils.isNonCompanionObject(classifier)) {
return lookupElement.addTail(tail) return lookupElement.addTail(tail)
@@ -255,10 +255,10 @@ class TypeInstantiationItems(
val samConstructor = scope.getFunctions(`class`.name, NoLookupLocation.FROM_IDE) val samConstructor = scope.getFunctions(`class`.name, NoLookupLocation.FROM_IDE)
.filterIsInstance<SamConstructorDescriptor>() .filterIsInstance<SamConstructorDescriptor>()
.singleOrNull() ?: return .singleOrNull() ?: return
val lookupElement = lookupElementFactory.createLookupElement(samConstructor, bindingContext, false) lookupElementFactory.createLookupElementsInSmartCompletion(samConstructor, bindingContext, useReceiverTypes = false)
.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION) .mapTo(collection) {
.addTail(tail) it.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION).addTail(tail)
collection.add(lookupElement) }
} }
} }
@@ -36,8 +36,7 @@ import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import java.util.ArrayList import java.util.*
import java.util.HashMap
class ArtificialElementInsertHandler( class ArtificialElementInsertHandler(
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{ val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
@@ -155,7 +154,7 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
expectedInfos: Collection<ExpectedInfo>, expectedInfos: Collection<ExpectedInfo>,
infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification, infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification,
noNameSimilarityForReturnItself: Boolean = false, noNameSimilarityForReturnItself: Boolean = false,
lookupElementFactory: (TDescriptor) -> LookupElement? lookupElementFactory: (TDescriptor) -> Collection<LookupElement>
) { ) {
class ItemData(val descriptor: TDescriptor, val itemOptions: ItemOptions) { class ItemData(val descriptor: TDescriptor, val itemOptions: ItemOptions) {
override fun equals(other: Any?) override fun equals(other: Any?)
@@ -163,7 +162,7 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
override fun hashCode() = if (this.descriptor != null) this.descriptor.getOriginal().hashCode() else 0 override fun hashCode() = if (this.descriptor != null) this.descriptor.getOriginal().hashCode() else 0
} }
fun ItemData.createLookupElement() = lookupElementFactory(this.descriptor)?.withOptions(this.itemOptions) fun ItemData.createLookupElements() = lookupElementFactory(this.descriptor).map { it.withOptions(this.itemOptions) }
val matchedInfos = HashMap<ItemData, MutableList<ExpectedInfo>>() val matchedInfos = HashMap<ItemData, MutableList<ExpectedInfo>>()
val makeNullableInfos = HashMap<ItemData, MutableList<ExpectedInfo>>() val makeNullableInfos = HashMap<ItemData, MutableList<ExpectedInfo>>()
@@ -179,20 +178,18 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
if (!matchedInfos.isEmpty()) { if (!matchedInfos.isEmpty()) {
for ((itemData, infos) in matchedInfos) { for ((itemData, infos) in matchedInfos) {
val lookupElement = itemData.createLookupElement() val lookupElements = itemData.createLookupElements()
if (lookupElement != null) { val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) {
val nameSimilarityInfos = if (noNameSimilarityForReturnItself && descriptor is CallableDescriptor) { infos.filter { (it.additionalData as? ReturnValueAdditionalData)?.callable != descriptor } // do not calculate name similarity with function itself in its return
infos.filter { (it.additionalData as? ReturnValueAdditionalData)?.callable != descriptor } // do not calculate name similarity with function itself in its return
}
else
infos
add(lookupElement.addTailAndNameSimilarity(infos, nameSimilarityInfos))
} }
else
infos
lookupElements.mapTo(this) { it.addTailAndNameSimilarity(infos, nameSimilarityInfos) }
} }
} }
else { else {
for ((itemData, infos) in makeNullableInfos) { for ((itemData, infos) in makeNullableInfos) {
addLookupElementsForNullable({ itemData.createLookupElement() }, infos) addLookupElementsForNullable({ itemData.createLookupElements() }, infos)
} }
} }
} }
@@ -204,18 +201,17 @@ private fun <T : DeclarationDescriptor?> T.substituteFixed(substitutor: TypeSubs
return this?.substitute(substitutor) as T return this?.substitute(substitutor) as T
} }
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection<ExpectedInfo>) { private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> Collection<LookupElement>, matchedInfos: Collection<ExpectedInfo>) {
for (element in lookupElementsForNullable(factory)) { fun LookupElement.postProcess(): LookupElement {
add(element.addTailAndNameSimilarity(matchedInfos)) var element = this
element = element.suppressAutoInsertion()
element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE)
element = element.addTailAndNameSimilarity(matchedInfos)
return element
} }
}
private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection<LookupElement> { factory().mapTo(this) {
val result = ArrayList<LookupElement>(2) object: LookupElementDecorator<LookupElement>(it) {
var lookupElement = factory()
if (lookupElement != null) {
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun renderElement(presentation: LookupElementPresentation) { override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation) super.renderElement(presentation)
presentation.setItemText("!! " + presentation.getItemText()) presentation.setItemText("!! " + presentation.getItemText())
@@ -223,15 +219,11 @@ private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection
override fun handleInsert(context: InsertionContext) { override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("!!", spaceBefore = false, spaceAfter = false).handleInsert(context, getDelegate()) WithTailInsertHandler("!!", spaceBefore = false, spaceAfter = false).handleInsert(context, getDelegate())
} }
} }.postProcess()
lookupElement = lookupElement!!.suppressAutoInsertion()
lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE)
result.add(lookupElement!!)
} }
lookupElement = factory() factory().mapTo(this) {
if (lookupElement != null) { object: LookupElementDecorator<LookupElement>(it) {
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement!!) {
override fun renderElement(presentation: LookupElementPresentation) { override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation) super.renderElement(presentation)
presentation.setItemText("?: " + presentation.getItemText()) presentation.setItemText("?: " + presentation.getItemText())
@@ -239,13 +231,8 @@ private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection
override fun handleInsert(context: InsertionContext) { override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("?:", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate()) //TODO: code style WithTailInsertHandler("?:", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate()) //TODO: code style
} }
} }.postProcess()
lookupElement = lookupElement!!.suppressAutoInsertion()
lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE)
result.add(lookupElement!!)
} }
return result
} }
fun functionType(function: FunctionDescriptor): JetType? { fun functionType(function: FunctionDescriptor): JetType? {
@@ -274,22 +261,24 @@ fun functionType(function: FunctionDescriptor): JetType? {
) )
} }
fun LookupElementFactory.createLookupElement( fun LookupElementFactory.createLookupElementsInSmartCompletion(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
bindingContext: BindingContext, bindingContext: BindingContext,
boldImmediateMembers: Boolean useReceiverTypes: Boolean
): LookupElement { ): Collection<LookupElement> {
var element = createLookupElement(descriptor, boldImmediateMembers) return createStandardLookupElementsForDescriptor(descriptor, useReceiverTypes).map {
var element = it
if (descriptor is FunctionDescriptor && descriptor.getValueParameters().isNotEmpty()) { if (descriptor is FunctionDescriptor && descriptor.valueParameters.isNotEmpty()) {
element = element.keepOldArgumentListOnTab() element = element.keepOldArgumentListOnTab()
}
if (descriptor is ValueParameterDescriptor && bindingContext[BindingContext.AUTO_CREATED_IT, descriptor]!!) {
element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.IT)
}
element
} }
if (descriptor is ValueParameterDescriptor && bindingContext[BindingContext.AUTO_CREATED_IT, descriptor]!!) {
element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.IT)
}
return element
} }
enum class SmartCompletionItemPriority { enum class SmartCompletionItemPriority {
@@ -11,3 +11,4 @@ fun foo(){
} }
// ELEMENT: bar // ELEMENT: bar
// TAIL_TEXT: "(p: (Int, String) -> Unit) (sample)"
@@ -11,3 +11,4 @@ fun foo(){
} }
// ELEMENT: bar // ELEMENT: bar
// TAIL_TEXT: "(p: (Int, String) -> Unit) (sample)"
@@ -0,0 +1,6 @@
fun foo(p: (String, Char) -> Unit): String {}
fun v: String = fo<caret>
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) (<root>)", typeText:"String" }
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) (<root>)", typeText:"String" }
+7
View File
@@ -0,0 +1,7 @@
import java.util.Comparator
var v: Comparator<String> = <caret>
// EXIST: { itemText: "object: Comparator<String>{...}" }
// EXIST: { lookupString: "Comparator", itemText: "Comparator", tailText: "(function: (T!, T!) -> Int) (java.util)", typeText: "Comparator<T>" }
// EXIST: { lookupString: "Comparator", itemText: "Comparator", tailText: " { T, T -> ... } (function: (T!, T!) -> Int) (java.util)", typeText: "Comparator<T>" }
@@ -143,6 +143,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("HighOrderFunction.kt")
public void testHighOrderFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/HighOrderFunction.kt");
doTest(fileName);
}
@TestMetadata("IfCondition.kt") @TestMetadata("IfCondition.kt")
public void testIfCondition() throws Exception { public void testIfCondition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/IfCondition.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/IfCondition.kt");
@@ -425,6 +431,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SAMExpected2.kt")
public void testSAMExpected2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SAMExpected2.kt");
doTest(fileName);
}
@TestMetadata("SkipDeclarationsOfType.kt") @TestMetadata("SkipDeclarationsOfType.kt")
public void testSkipDeclarationsOfType() throws Exception { public void testSkipDeclarationsOfType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt");