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
.map { lookupElementFactory.createLookupElement(it, boldImmediateMembers = false, qualifyNestedClasses = true, includeClassTypeArguments = false) }
.map { lookupElementFactory.createLookupElement(it, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) }
.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) }
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 {
var receiverTypes = emptyList<JetType>()
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
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)
@@ -295,6 +295,7 @@ fun breakOrContinueExpressionItems(position: JetElement, breakOrContinue: String
fun LookupElementFactory.createBackingFieldLookupElement(
property: PropertyDescriptor,
useReceiverTypes: Boolean,
inDescriptor: DeclarationDescriptor,
resolutionFacade: ResolutionFacade
): LookupElement? {
@@ -312,7 +313,7 @@ fun LookupElementFactory.createBackingFieldLookupElement(
val bindingContext = resolutionFacade.analyze(declaration)
if (!bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property]!!) return null
val lookupElement = createLookupElement(property, true)
val lookupElement = createLookupElement(property, useReceiverTypes)
return object : LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = "$" + super.getLookupString()
override fun getAllLookupStrings() = setOf(getLookupString())
@@ -335,7 +336,7 @@ fun LookupElementFactory.createLookupElementForType(type: JetType): LookupElemen
}
else {
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)
@@ -37,7 +37,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
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.getDescriptorsFiltered
@@ -87,7 +86,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
.filter { it.getName().asString() !in documentedParameters }
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 {
val element = lookupElementFactory.createLookupElement(it, false)
val element = lookupElementFactory.createLookupElement(it, useReceiverTypes = false)
collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
override fun handleInsert(context: InsertionContext?) {
// insert only plain name here, no qualifier/parentheses/etc.
@@ -17,11 +17,13 @@
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.*
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.util.PlatformIcons
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
@@ -62,13 +64,86 @@ public data class PackageLookupObject(val fqName: FqName) : DeclarationLookupObj
public class LookupElementFactory(
private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<JetType>,
private val context: LookupElementFactory.Context,
private val inDescriptor: DeclarationDescriptor?,
expectedInfosCalculator: () -> Collection<ExpectedInfo>
) {
public enum class Context {
NORMAL,
STRING_TEMPLATE_AFTER_DOLLAR,
INFIX_CALL
}
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(
descriptor: DeclarationDescriptor,
boldImmediateMembers: Boolean,
useReceiverTypes: Boolean,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true
): LookupElement {
@@ -84,7 +159,7 @@ public class LookupElementFactory(
element.putUserData(CALLABLE_WEIGHT_KEY, weight) // store for use in lookup elements sorting
}
if (boldImmediateMembers) {
if (useReceiverTypes) {
element = element.boldIfImmediate(weight)
}
return element
@@ -19,35 +19,21 @@ package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.openapi.util.TextRange
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.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.completion.handlers.*
import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.types.JetType
import java.util.*
class LookupElementsCollector(
private val defaultPrefixMatcher: PrefixMatcher,
private val completionParameters: CompletionParameters,
resultSet: CompletionResultSet,
private val resolutionFacade: ResolutionFacade,
private val lookupElementFactory: LookupElementFactory,
private val sorter: CompletionSorter,
private val inDescriptor: DeclarationDescriptor,
private val context: LookupElementsCollector.Context
private val sorter: CompletionSorter
) {
public enum class Context {
NORMAL,
STRING_TEMPLATE_AFTER_DOLLAR,
INFIX_CALL
}
private val elements = LinkedHashMap<PrefixMatcher, ArrayList<LookupElement>>()
@@ -89,73 +75,14 @@ class LookupElementsCollector(
public fun addDescriptorElements(descriptor: DeclarationDescriptor, notImported: Boolean = false, withReceiverCast: Boolean = false) {
run {
var lookupElement = lookupElementFactory.createLookupElement(descriptor, true)
var lookupElements = lookupElementFactory.createStandardLookupElementsForDescriptor(descriptor, useReceiverTypes = true)
if (withReceiverCast) {
lookupElement = lookupElement.withReceiverCast()
lookupElements = lookupElements.map { it.withReceiverCast() }
}
if (context == Context.STRING_TEMPLATE_AFTER_DOLLAR && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) {
lookupElement = lookupElement.withBracesSurrounding()
}
addElement(lookupElement, notImported = notImported)
addElements(lookupElements, 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) {
@@ -53,8 +53,9 @@ object PackageDirectiveCompletion {
val bindingContext = resolutionFacade.analyze(expression)
val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter())
val lookupElementFactory = LookupElementFactory(resolutionFacade, emptyList(), LookupElementFactory.Context.NORMAL, null, { emptyList() })
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)) {
result.addElement(lookupElement)
}
@@ -170,7 +170,7 @@ class ParameterNameAndTypeCompletion(
private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) {
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()!!) {
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
object KeywordValues {
public fun addToCollection(collection: MutableCollection<LookupElement>, expectedInfos: Collection<ExpectedInfo>, expressionWithType: JetExpression) {
@@ -54,8 +55,8 @@ object KeywordValues {
val booleanInfoClassifier = { info: ExpectedInfo ->
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("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE) }
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).singletonList() }
}
if (!shouldSkipNull(expressionWithType)) {
@@ -66,7 +67,7 @@ object KeywordValues {
ExpectedInfoClassification.noMatch
}
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.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptySet
import java.util.ArrayList
import java.util.HashSet
import java.util.*
interface InheritanceItemsSearcher {
fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit)
@@ -146,13 +146,7 @@ class SmartCompletion(
val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = receiver == null) { descriptor ->
lookupElementFactory.createLookupElement(descriptor, bindingContext, true)
}
if (descriptor is PropertyDescriptor) {
result.addLookupElements(descriptor, expectedInfos, infoClassifier) { descriptor ->
lookupElementFactory.createBackingFieldLookupElement(descriptor, inDescriptor, resolutionFacade)
}
lookupElementFactory.createLookupElementsInSmartCompletion(descriptor, bindingContext, true)
}
if (receiver == null) {
@@ -232,7 +226,7 @@ class SmartCompletion(
val types = smartCastCalculator.types(item.receiverParameter).map { FuzzyType(it, emptyList()) }
val classifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
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 }
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())
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun getLookupString() = text
@@ -82,7 +82,7 @@ class StaticMembers(
}
collection.addLookupElements(descriptor, expectedInfos, classifier) {
descriptor -> createLookupElement(descriptor, classDescriptor)
descriptor -> createLookupElements(descriptor, classDescriptor)
}
}
@@ -102,46 +102,48 @@ class StaticMembers(
members.forEach(::processMember)
}
private fun createLookupElement(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): LookupElement {
val lookupElement = lookupElementFactory.createLookupElement(memberDescriptor, bindingContext, false)
private fun createLookupElements(memberDescriptor: DeclarationDescriptor, classDescriptor: ClassDescriptor): Collection<LookupElement> {
val lookupElements = lookupElementFactory.createLookupElementsInSmartCompletion(memberDescriptor, bindingContext, useReceiverTypes = false)
val qualifierPresentation = classDescriptor.getName().asString()
val qualifierText = IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classDescriptor)
return object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun getAllLookupStrings(): Set<String> {
return setOf(lookupElement.getLookupString(), 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)
return lookupElements.map {
object: LookupElementDecorator<LookupElement>(it) {
override fun getAllLookupStrings(): Set<String> {
return setOf(it.lookupString, qualifierPresentation)
}
if (presentation.getTypeText().isNullOrEmpty()) {
presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType()))
}
}
override fun renderElement(presentation: LookupElementPresentation) {
getDelegate().renderElement(presentation)
override fun handleInsert(context: InsertionContext) {
var text = qualifierText + "." + memberDescriptor.getName().render()
presentation.setItemText(qualifierPresentation + "." + presentation.getItemText())
context.getDocument().replaceString(context.getStartOffset(), context.getTailOffset(), text)
context.setTailOffset(context.getStartOffset() + text.length())
val tailText = " (" + DescriptorUtils.getFqName(classDescriptor.getContainingDeclaration()) + ")"
if (memberDescriptor is FunctionDescriptor) {
presentation.appendTailText(tailText, true)
}
else {
presentation.setTailText(tailText, true)
}
if (memberDescriptor is FunctionDescriptor) {
getDelegate().handleInsert(context)
if (presentation.getTypeText().isNullOrEmpty()) {
presentation.setTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(classDescriptor.getDefaultType()))
}
}
shortenReferences(context, context.getStartOffset(), context.getTailOffset())
}
}.assignSmartCompletionPriority(SmartCompletionItemPriority.STATIC_MEMBER)
override fun handleInsert(context: InsertionContext) {
var text = qualifierText + "." + memberDescriptor.getName().render()
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>,
tail: Tail?
): LookupElement? {
var lookupElement = lookupElementFactory.createLookupElement(classifier, bindingContext, false)
var lookupElement = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false)
if (DescriptorUtils.isNonCompanionObject(classifier)) {
return lookupElement.addTail(tail)
@@ -255,10 +255,10 @@ class TypeInstantiationItems(
val samConstructor = scope.getFunctions(`class`.name, NoLookupLocation.FROM_IDE)
.filterIsInstance<SamConstructorDescriptor>()
.singleOrNull() ?: return
val lookupElement = lookupElementFactory.createLookupElement(samConstructor, bindingContext, false)
.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION)
.addTail(tail)
collection.add(lookupElement)
lookupElementFactory.createLookupElementsInSmartCompletion(samConstructor, bindingContext, useReceiverTypes = false)
.mapTo(collection) {
it.assignSmartCompletionPriority(SmartCompletionItemPriority.INSTANTIATION).addTail(tail)
}
}
}
@@ -36,8 +36,7 @@ import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import java.util.ArrayList
import java.util.HashMap
import java.util.*
class ArtificialElementInsertHandler(
val textBeforeCaret: String, val textAfterCaret: String, val shortenRefs: Boolean) : InsertHandler<LookupElement>{
@@ -155,7 +154,7 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
expectedInfos: Collection<ExpectedInfo>,
infoClassifier: (ExpectedInfo) -> ExpectedInfoClassification,
noNameSimilarityForReturnItself: Boolean = false,
lookupElementFactory: (TDescriptor) -> LookupElement?
lookupElementFactory: (TDescriptor) -> Collection<LookupElement>
) {
class ItemData(val descriptor: TDescriptor, val itemOptions: ItemOptions) {
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
}
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 makeNullableInfos = HashMap<ItemData, MutableList<ExpectedInfo>>()
@@ -179,20 +178,18 @@ fun<TDescriptor: DeclarationDescriptor?> MutableCollection<LookupElement>.addLoo
if (!matchedInfos.isEmpty()) {
for ((itemData, infos) in matchedInfos) {
val lookupElement = itemData.createLookupElement()
if (lookupElement != null) {
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
}
else
infos
add(lookupElement.addTailAndNameSimilarity(infos, nameSimilarityInfos))
val lookupElements = itemData.createLookupElements()
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
}
else
infos
lookupElements.mapTo(this) { it.addTailAndNameSimilarity(infos, nameSimilarityInfos) }
}
}
else {
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
}
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection<ExpectedInfo>) {
for (element in lookupElementsForNullable(factory)) {
add(element.addTailAndNameSimilarity(matchedInfos))
private fun MutableCollection<LookupElement>.addLookupElementsForNullable(factory: () -> Collection<LookupElement>, matchedInfos: Collection<ExpectedInfo>) {
fun LookupElement.postProcess(): LookupElement {
var element = this
element = element.suppressAutoInsertion()
element = element.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE)
element = element.addTailAndNameSimilarity(matchedInfos)
return element
}
}
private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection<LookupElement> {
val result = ArrayList<LookupElement>(2)
var lookupElement = factory()
if (lookupElement != null) {
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement) {
factory().mapTo(this) {
object: LookupElementDecorator<LookupElement>(it) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setItemText("!! " + presentation.getItemText())
@@ -223,15 +219,11 @@ private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("!!", spaceBefore = false, spaceAfter = false).handleInsert(context, getDelegate())
}
}
lookupElement = lookupElement!!.suppressAutoInsertion()
lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE)
result.add(lookupElement!!)
}.postProcess()
}
lookupElement = factory()
if (lookupElement != null) {
lookupElement = object: LookupElementDecorator<LookupElement>(lookupElement!!) {
factory().mapTo(this) {
object: LookupElementDecorator<LookupElement>(it) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setItemText("?: " + presentation.getItemText())
@@ -239,13 +231,8 @@ private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("?:", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate()) //TODO: code style
}
}
lookupElement = lookupElement!!.suppressAutoInsertion()
lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE)
result.add(lookupElement!!)
}.postProcess()
}
return result
}
fun functionType(function: FunctionDescriptor): JetType? {
@@ -274,22 +261,24 @@ fun functionType(function: FunctionDescriptor): JetType? {
)
}
fun LookupElementFactory.createLookupElement(
fun LookupElementFactory.createLookupElementsInSmartCompletion(
descriptor: DeclarationDescriptor,
bindingContext: BindingContext,
boldImmediateMembers: Boolean
): LookupElement {
var element = createLookupElement(descriptor, boldImmediateMembers)
useReceiverTypes: Boolean
): Collection<LookupElement> {
return createStandardLookupElementsForDescriptor(descriptor, useReceiverTypes).map {
var element = it
if (descriptor is FunctionDescriptor && descriptor.getValueParameters().isNotEmpty()) {
element = element.keepOldArgumentListOnTab()
if (descriptor is FunctionDescriptor && descriptor.valueParameters.isNotEmpty()) {
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 {
@@ -11,3 +11,4 @@ fun foo(){
}
// ELEMENT: bar
// TAIL_TEXT: "(p: (Int, String) -> Unit) (sample)"
@@ -11,3 +11,4 @@ fun foo(){
}
// 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);
}
@TestMetadata("HighOrderFunction.kt")
public void testHighOrderFunction() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/HighOrderFunction.kt");
doTest(fileName);
}
@TestMetadata("IfCondition.kt")
public void testIfCondition() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/IfCondition.kt");
@@ -425,6 +431,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
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")
public void testSkipDeclarationsOfType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SkipDeclarationsOfType.kt");