A::class and A::class.java supported in smart completion too

This commit is contained in:
Valentin Kipyatkov
2015-10-01 14:04:48 +03:00
parent ec5d1d3a52
commit c0bfca4f89
17 changed files with 265 additions and 161 deletions
@@ -112,6 +112,7 @@ public class ReferenceVariantsHelper(
is CallTypeAndReceiver.SAFE -> receiverExpression = callTypeAndReceiver.receiver
is CallTypeAndReceiver.INFIX -> receiverExpression = callTypeAndReceiver.receiver
is CallTypeAndReceiver.UNARY -> receiverExpression = null // can it happen at all?
is CallTypeAndReceiver.UNKNOWN -> return emptyList()
else -> throw RuntimeException() //TODO: see KT-9394
}
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
public sealed class CallType<TReceiver : JetElement?>(val descriptorKindFilter: DescriptorKindFilter) {
object UNKNOWN : CallType<Nothing?>(DescriptorKindFilter.ALL)
object DEFAULT : CallType<Nothing?>(DescriptorKindFilter.ALL)
object DOT : CallType<JetExpression>(DescriptorKindFilter.ALL)
@@ -78,6 +80,7 @@ public sealed class CallTypeAndReceiver<TReceiver : JetElement?, TCallType : Cal
val callType: TCallType,
val receiver: TReceiver
) {
object UNKNOWN : CallTypeAndReceiver<Nothing?, CallType.UNKNOWN>(CallType.UNKNOWN, null)
object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>(CallType.DEFAULT, null)
class DOT(receiver: JetExpression) : CallTypeAndReceiver<JetExpression, CallType.DOT>(CallType.DOT, receiver)
class SAFE(receiver: JetExpression) : CallTypeAndReceiver<JetExpression, CallType.SAFE>(CallType.SAFE, receiver)
@@ -20,7 +20,7 @@ import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.codeInsight.completion.CompletionResultSet
import com.intellij.codeInsight.completion.CompletionSorter
import com.intellij.codeInsight.completion.CompletionType
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.template.TemplateManager
import com.intellij.patterns.PatternCondition
import com.intellij.patterns.StandardPatterns
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.completion.smart.ExpectedInfoClassification
import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority
@@ -47,6 +48,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
class BasicCompletionSession(configuration: CompletionSessionConfiguration,
parameters: CompletionParameters,
@@ -103,8 +105,9 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
private val smartCompletion = expression?.let {
SmartCompletion(
it, resolutionFacade, bindingContext, isVisibleFilter, prefixMatcher,
GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver, forBasicCompletion = true
it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, prefixMatcher,
GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver,
isJvmModule, forBasicCompletion = true
)
}
@@ -272,9 +275,31 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
}
private fun completeKeywords() {
val keywordsToSkip = HashSet<String>()
val keywordValueConsumer = object : KeywordValues.Consumer {
override fun consume(lookupString: String, expectedInfoClassifier: (ExpectedInfo) -> ExpectedInfoClassification, priority: SmartCompletionItemPriority, factory: () -> LookupElement) {
keywordsToSkip.add(lookupString)
val lookupElement = factory()
val matched = expectedInfos.any {
val classification = expectedInfoClassifier(it)
assert(!classification.makeNotNullable) { "Nullable keyword values not supported" }
classification.isMatch()
}
if (matched) {
lookupElement.putUserData(SmartCompletionInBasicWeigher.KEYWORD_VALUE_MATCHED_KEY, matched)
lookupElement.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, priority)
}
collector.addElement(lookupElement)
}
}
KeywordValues.process(keywordValueConsumer, callTypeAndReceiver, bindingContext, resolutionFacade, moduleDescriptor, isJvmModule)
val keywordsPrefix = prefix.substringBefore('@') // if there is '@' in the prefix - use shorter prefix to not loose 'this' etc
KeywordCompletion.complete(expression ?: parameters.getPosition(), keywordsPrefix) { lookupElement ->
val keyword = lookupElement.getLookupString()
val keyword = lookupElement.lookupString
if (keyword in keywordsToSkip) return@complete
when (keyword) {
// if "this" is parsed correctly in the current context - insert it and all this@xxx items
"this" -> {
@@ -307,20 +332,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
}
"class" -> {
if (callTypeAndReceiver is CallTypeAndReceiver.CALLABLE_REFERENCE) {
if (callTypeAndReceiver.receiver != null) {
collector.addElement(lookupElement)
if (!ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as JetFile)) {
val element = LookupElementBuilder.create(KeywordLookupObject(), "class.java")
.withPresentableText("class")
.withTailText(".java")
.bold()
collector.addElement(element)
}
}
}
else {
if (callTypeAndReceiver !is CallTypeAndReceiver.CALLABLE_REFERENCE) { // otherwise it should be handled by KeywordValues
collector.addElement(lookupElement)
}
}
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.CallType
@@ -74,6 +75,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
protected val resolutionFacade = file.getResolutionFacade()
protected val moduleDescriptor = resolutionFacade.moduleDescriptor
protected val project = position.getProject()
protected val isJvmModule = !ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as JetFile)
protected val nameExpression: JetSimpleNameExpression?
protected val expression: JetExpression?
@@ -292,8 +294,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
}
private fun Collection<CallableDescriptor>.filterShadowedNonImported(): Collection<CallableDescriptor> {
val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression!!)
return ShadowedDeclarationsFilter(bindingContext, resolutionFacade, nameExpression, callTypeAndReceiver)
return ShadowedDeclarationsFilter(bindingContext, resolutionFacade, nameExpression!!, callTypeAndReceiver)
.filterNonImported(this, referenceVariants)
}
@@ -305,7 +306,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
)
}
private fun createLookupElementFactory(callType: CallType<*>, receiverTypes: Collection<JetType>?): LookupElementFactory {
private fun createLookupElementFactory(callType: CallType<*>?, receiverTypes: Collection<JetType>?): LookupElementFactory {
val contextVariablesProvider = {
nameExpression?.let {
referenceVariantsHelper.getReferenceVariants(it, DescriptorKindFilter.VARIABLES, { true }, CallTypeAndReceiver.DEFAULT)
@@ -321,7 +322,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<JetType>?> {
if (nameExpression == null) {
return CallTypeAndReceiver.DEFAULT to null
return CallTypeAndReceiver.UNKNOWN to null
}
val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression)
@@ -346,7 +347,8 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
is CallTypeAndReceiver.IMPORT_DIRECTIVE,
is CallTypeAndReceiver.PACKAGE_DIRECTIVE,
is CallTypeAndReceiver.TYPE ->
is CallTypeAndReceiver.TYPE,
is CallTypeAndReceiver.UNKNOWN ->
// we don't need to highlight immediate members in these cases
return callTypeAndReceiver to null
@@ -145,6 +145,8 @@ sealed class ArgumentPositionData(val function: FunctionDescriptor) : ExpectedIn
class ReturnValueAdditionalData(val callable: CallableDescriptor) : ExpectedInfo.AdditionalData
object WhenEntryAdditionalData : ExpectedInfo.AdditionalData
class ExpectedInfos(
val bindingContext: BindingContext,
val resolutionFacade: ResolutionFacade,
@@ -489,10 +491,10 @@ class ExpectedInfos(
val subject = whenExpression.getSubjectExpression()
if (subject != null) {
val subjectType = bindingContext.getType(subject) ?: return null
return listOf(ExpectedInfo(subjectType, null, null))
return listOf(ExpectedInfo(subjectType, null, null, additionalData = WhenEntryAdditionalData))
}
else {
return listOf(ExpectedInfo(resolutionFacade.moduleDescriptor.builtIns.booleanType, null, null))
return listOf(ExpectedInfo(resolutionFacade.moduleDescriptor.builtIns.booleanType, null, null, additionalData = WhenEntryAdditionalData))
}
}
@@ -27,12 +27,16 @@ import org.jetbrains.kotlin.types.JetType
import java.util.*
class InsertHandlerProvider(
private val callType: CallType<*>,
private val callType: CallType<*>?,
expectedInfosCalculator: () -> Collection<ExpectedInfo>
) {
private val expectedInfos by lazy(LazyThreadSafetyMode.NONE) { expectedInfosCalculator() }
public fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> {
if (callType == null) {
error("Cannot create InsertHandler when no CallType known")
}
return when (descriptor) {
is FunctionDescriptor -> {
val needTypeArguments = needTypeArguments(descriptor)
@@ -0,0 +1,114 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.completion.smart.ExpectedInfoClassification
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority
import org.jetbrains.kotlin.idea.completion.smart.classifyExpectedInfo
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.types.JetTypeImpl
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.TypeSubstitutor
import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
object KeywordValues {
interface Consumer {
fun consume(
lookupString: String,
expectedInfoClassifier: (ExpectedInfo) -> ExpectedInfoClassification,
priority: SmartCompletionItemPriority,
factory: () -> LookupElement)
}
fun process(
consumer: Consumer,
callTypeAndReceiver: CallTypeAndReceiver<*, *>,
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade,
moduleDescriptor: ModuleDescriptor,
isJvmModule: Boolean
) {
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
val booleanInfoClassifier = classifier@ { info: ExpectedInfo ->
// no sense in true or false as when entry
if (info.additionalData is WhenEntryAdditionalData) return@classifier ExpectedInfoClassification.noMatch
if (info.fuzzyType?.type?.isBooleanOrNullableBoolean() ?: false)
ExpectedInfoClassification.match(TypeSubstitutor.EMPTY)
else
ExpectedInfoClassification.noMatch
}
consumer.consume("true", booleanInfoClassifier, SmartCompletionItemPriority.TRUE) {
LookupElementBuilder.create(KeywordLookupObject(), "true").bold()
}
consumer.consume("false", booleanInfoClassifier, SmartCompletionItemPriority.FALSE) {
LookupElementBuilder.create(KeywordLookupObject(), "false").bold()
}
val nullClassifier = { info: ExpectedInfo ->
if (info.fuzzyType != null && info.fuzzyType!!.type.isMarkedNullable())
ExpectedInfoClassification.match(TypeSubstitutor.EMPTY)
else
ExpectedInfoClassification.noMatch
}
consumer.consume("null", nullClassifier, SmartCompletionItemPriority.NULL) {
LookupElementBuilder.create(KeywordLookupObject(), "null").bold()
}
}
if (callTypeAndReceiver is CallTypeAndReceiver.CALLABLE_REFERENCE && callTypeAndReceiver.receiver != null) {
val qualifierType = bindingContext[BindingContext.TYPE, callTypeAndReceiver.receiver]
if (qualifierType != null) {
val kClassDescriptor = resolutionFacade.getFrontendService(ReflectionTypes::class.java).kClass
val classLiteralType = JetTypeImpl.create(Annotations.EMPTY, kClassDescriptor, false, listOf(TypeProjectionImpl(qualifierType)))
val kClassTypes = listOf(FuzzyType(classLiteralType, emptyList()))
val kClassClassifier = { info: ExpectedInfo -> kClassTypes.classifyExpectedInfo(info) }
consumer.consume("class", kClassClassifier, SmartCompletionItemPriority.CLASS_LITERAL) {
LookupElementBuilder.create(KeywordLookupObject(), "class").bold()
}
if (isJvmModule) {
val javaLangClassDescriptor = resolutionFacade.resolveImportReference(moduleDescriptor, FqName("java.lang.Class"))
.singleOrNull() as? ClassDescriptor
if (javaLangClassDescriptor != null) {
val javaLangClassType = JetTypeImpl.create(Annotations.EMPTY, javaLangClassDescriptor, false, listOf(TypeProjectionImpl(qualifierType)))
val javaClassTypes = listOf(FuzzyType(javaLangClassType, emptyList()))
val javaClassClassifier = { info: ExpectedInfo -> javaClassTypes.classifyExpectedInfo(info) }
consumer.consume("class", javaClassClassifier, SmartCompletionItemPriority.CLASS_LITERAL) {
LookupElementBuilder.create(KeywordLookupObject(), "class.java")
.withPresentableText("class")
.withTailText(".java")
.bold()
}
}
}
}
}
}
}
@@ -45,7 +45,7 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class LookupElementFactory(
private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<JetType>?,
private val callType: CallType<*>,
private val callType: CallType<*>?,
private val isInStringTemplateAfterDollar: Boolean,
public val insertHandlerProvider: InsertHandlerProvider,
contextVariablesProvider: () -> Collection<VariableDescriptor>
@@ -132,7 +132,7 @@ class LookupElementFactory(
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler(callType, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
KotlinFunctionInsertHandler(callType!!, inputTypeArguments, inputValueArguments = false, lambdaInfo = lambdaInfo).handleInsert(context, this)
}
}
@@ -175,7 +175,7 @@ class LookupElementFactory(
}
override fun handleInsert(context: InsertionContext) {
KotlinFunctionInsertHandler(callType, inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this)
KotlinFunctionInsertHandler(callType!!, inputTypeArguments = needTypeArguments, inputValueArguments = false, argumentText = argumentText).handleInsert(context, this)
}
}
@@ -21,6 +21,7 @@ import com.intellij.codeInsight.completion.CompletionWeigher
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementWeigher
import com.intellij.codeInsight.lookup.WeighingContext
import com.intellij.openapi.util.Key
import com.intellij.psi.util.proximity.PsiProximityComparator
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
@@ -33,9 +34,6 @@ import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.isBooleanOrNullableBoolean
import org.jetbrains.kotlin.types.typeUtil.nullability
object PriorityWeigher : LookupElementWeigher("kotlin.priority") {
override fun weigh(element: LookupElement, context: WeighingContext)
@@ -184,26 +182,32 @@ class SmartCompletionInBasicWeigher(
private val callType: CallType<*>,
private val resolutionFacade: ResolutionFacade
) : LookupElementWeigher("kotlin.smartInBasic", true, false) {
companion object {
val KEYWORD_VALUE_MATCHED_KEY = Key<Boolean>("SmartCompletionInBasicWeigher.KEYWORD_VALUE_MATCHED_KEY")
}
private val descriptorsToSkip = smartCompletion.descriptorsToSkip
private val expectedInfos = smartCompletion.expectedInfos
private fun fullMatchWeight(nameSimilarity: Int) = (3L shl 32) + nameSimilarity * 3 // true and false should be in between zero-nameSimilarity and 1-nameSimilarity
private val MATCHED_TRUE_WEIGHT = (3L shl 32) + 2
private val MATCHED_FALSE_WEIGHT = (3L shl 32) + 1
private fun fullMatchWeight(nameSimilarity: Int) = (3L shl 32) + nameSimilarity
private fun ifNotNullMatchWeight(nameSimilarity: Int) = (2L shl 32) + nameSimilarity
private fun smartCompletionItemWeight(nameSimilarity: Int) = (1L shl 32) + nameSimilarity
private val MATCHED_NULL_WEIGHT = 1L
private val NO_MATCH_WEIGHT = 0L
private val DESCRIPTOR_TO_SKIP_WEIGHT = -1L // if descriptor is skipped from smart completion then it's probably irrelevant
override fun weigh(element: LookupElement): Long {
val keywordValueMatched = element.getUserData(KEYWORD_VALUE_MATCHED_KEY)
val smartCompletionPriority = element.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY)
if (keywordValueMatched != null) {
return if (keywordValueMatched) fullMatchWeight(0) else NO_MATCH_WEIGHT
}
if (smartCompletionPriority != null) { // it's an "additional item" came from smart completion, don't match it against expected type
return smartCompletionItemWeight(element.getUserData(NAME_SIMILARITY_KEY) ?: 0)
}
@@ -214,28 +218,6 @@ class SmartCompletionInBasicWeigher(
if (expectedInfos.isEmpty()) return NO_MATCH_WEIGHT
if (o is KeywordLookupObject) {
when (element.lookupString) {
"true", "false" -> {
if (expectedInfos.any { it.fuzzyType?.type?.isBooleanOrNullableBoolean() ?: false }) {
return if (element.lookupString == "true") MATCHED_TRUE_WEIGHT else MATCHED_FALSE_WEIGHT
}
else {
return NO_MATCH_WEIGHT
}
}
"null" -> {
if (expectedInfos.any { it.fuzzyType?.type?.nullability()?.let { it != TypeNullability.NOT_NULL } ?: false }) {
return MATCHED_NULL_WEIGHT
}
else {
return NO_MATCH_WEIGHT
}
}
}
}
val smartCastCalculator = smartCompletion.smartCastCalculator
val (fuzzyTypes, name) = when (o) {
@@ -1,79 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.completion.smart
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementBuilder
import com.intellij.codeInsight.lookup.LookupElementDecorator
import org.jetbrains.kotlin.idea.completion.COMPARISON_TOKENS
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
import org.jetbrains.kotlin.idea.completion.fuzzyType
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) {
var skipTrueFalse = false
val whenCondition = expressionWithType.getParent() as? JetWhenConditionWithExpression
if (whenCondition != null) {
val entry = whenCondition.getParent() as JetWhenEntry
val whenExpression = entry.getParent() as JetWhenExpression
val entries = whenExpression.getEntries()
if (whenExpression.getElseExpression() == null && entry == entries.last() && entries.size() != 1) {
val lookupElement = LookupElementBuilder.create("else").bold().withTailText(" ->")
collection.add(object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("->", spaceBefore = true, spaceAfter = true).handleInsert(context, getDelegate())
}
})
}
if (whenExpression.getSubjectExpression() == null) { // no sense in true or false entries for when with no subject
skipTrueFalse = true
}
}
if (!skipTrueFalse) {
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).singletonList() }
collection.addLookupElements(null, expectedInfos, booleanInfoClassifier) { LookupElementBuilder.create("false").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.FALSE).singletonList() }
}
if (!shouldSkipNull(expressionWithType)) {
val classifier = { info: ExpectedInfo ->
if (info.fuzzyType != null && info.fuzzyType!!.type.isMarkedNullable())
ExpectedInfoClassification.match(TypeSubstitutor.EMPTY)
else
ExpectedInfoClassification.noMatch
}
collection.addLookupElements(null, expectedInfos, classifier) {
LookupElementBuilder.create("null").bold().assignSmartCompletionPriority(SmartCompletionItemPriority.NULL).singletonList()
}
}
}
private fun shouldSkipNull(expressionWithType: JetExpression): Boolean {
val binaryExpression = expressionWithType.parent as? JetBinaryExpression ?: return false
return binaryExpression.operationToken in COMPARISON_TOKENS
}
}
@@ -19,15 +19,13 @@ package org.jetbrains.kotlin.idea.completion.smart
import com.intellij.codeInsight.completion.InsertionContext
import com.intellij.codeInsight.completion.OffsetKey
import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.Lookup
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.codeInsight.lookup.*
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.FuzzyType
@@ -52,12 +50,14 @@ class SmartCompletion(
private val expression: JetExpression,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val moduleDescriptor: ModuleDescriptor,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
private val prefixMatcher: PrefixMatcher,
private val inheritorSearchScope: GlobalSearchScope,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
private val lookupElementFactory: LookupElementFactory,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val isJvmModule: Boolean,
private val forBasicCompletion: Boolean = false
) {
private val expressionWithType = when (callTypeAndReceiver) {
@@ -150,10 +150,11 @@ class SmartCompletion(
}
private fun filterDescriptor(descriptor: DeclarationDescriptor): Collection<LookupElement> {
val callType = callTypeAndReceiver.callType
if (descriptor in descriptorsToSkip) return emptyList()
val result = SmartList<LookupElement>()
val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callTypeAndReceiver.callType, resolutionFacade)
val types = descriptor.fuzzyTypesForSmartCompletion(smartCastCalculator, callType, resolutionFacade)
val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { descriptor ->
@@ -177,23 +178,53 @@ class SmartCompletion(
val items = ArrayList<LookupElement>()
val inheritanceSearchers = ArrayList<InheritanceItemsSearcher>()
if (expectedInfos.isNotEmpty() && callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion)
.addTo(items, inheritanceSearchers, expectedInfos)
if (expression is JetSimpleNameExpression) {
StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip)
if (!forBasicCompletion) { // basic completion adds keyword values on its own
val keywordValueConsumer = object : KeywordValues.Consumer {
override fun consume(lookupString: String, expectedInfoClassifier: (ExpectedInfo) -> ExpectedInfoClassification, priority: SmartCompletionItemPriority, factory: () -> LookupElement) {
items.addLookupElements(null, expectedInfos, expectedInfoClassifier) {
val lookupElement = factory()
lookupElement.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, priority)
listOf(lookupElement)
}
}
}
KeywordValues.process(keywordValueConsumer, callTypeAndReceiver, bindingContext, resolutionFacade, moduleDescriptor, isJvmModule)
}
if (!forBasicCompletion) {
if (expectedInfos.isNotEmpty()) {
if (!forBasicCompletion && (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT || callTypeAndReceiver is CallTypeAndReceiver.UNKNOWN /* after this@ */)) {
items.addThisItems(expression, expectedInfos, smartCastCalculator)
LambdaItems.addToCollection(items, expectedInfos)
KeywordValues.addToCollection(items, expectedInfos, expression)
}
MultipleArgumentsItemProvider(bindingContext, smartCastCalculator).addToCollection(items, expectedInfos, expression)
if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) {
TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion)
.addTo(items, inheritanceSearchers, expectedInfos)
if (expression is JetSimpleNameExpression) {
StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip)
}
if (!forBasicCompletion) {
LambdaItems.addToCollection(items, expectedInfos)
val whenCondition = expressionWithType.parent as? JetWhenConditionWithExpression
if (whenCondition != null) {
val entry = whenCondition.parent as JetWhenEntry
val whenExpression = entry.parent as JetWhenExpression
val entries = whenExpression.entries
if (whenExpression.elseExpression == null && entry == entries.last() && entries.size() != 1) {
val lookupElement = LookupElementBuilder.create("else").bold().withTailText(" ->")
items.add(object: LookupElementDecorator<LookupElement>(lookupElement) {
override fun handleInsert(context: InsertionContext) {
WithTailInsertHandler("->", spaceBefore = true, spaceAfter = true).handleInsert(context, delegate)
}
})
}
}
}
MultipleArgumentsItemProvider(bindingContext, smartCastCalculator).addToCollection(items, expectedInfos, expression)
}
}
val inheritanceSearcher = if (inheritanceSearchers.isNotEmpty())
@@ -39,9 +39,9 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) {
expression?.let {
SmartCompletion(it, resolutionFacade,
bindingContext, isVisibleFilter, prefixMatcher, originalSearchScope,
toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver)
SmartCompletion(it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter,
prefixMatcher, originalSearchScope, toFromOriginalFileMapper, lookupElementFactory,
callTypeAndReceiver, isJvmModule)
}
}
@@ -289,6 +289,7 @@ enum class SmartCompletionItemPriority {
IT,
TRUE,
FALSE,
CLASS_LITERAL,
THIS,
DEFAULT,
NULLABLE,
+3 -2
View File
@@ -7,10 +7,11 @@ fun f(e1: E, e2: E?, x: Any) {
if (e1 == <caret>
}
// WITH_ORDER
// EXIST: { itemText:"e2" }
// EXIST: { lookupString:"A", itemText:"E.A", typeText:"E" }
// EXIST: { lookupString:"B", itemText:"E.B", typeText:"E" }
// EXIST: { itemText:"e2" }
// EXIST: null
// ABSENT: { itemText:"!! e2" }
// ABSENT: e1
// ABSENT: x
// ABSENT: null
@@ -0,0 +1,10 @@
import kotlin.reflect.KClass
fun<T : Any> foo(p: KClass<T>){}
fun bar() {
foo(String::<caret>)
}
// EXIST: { lookupString: "class", itemText: "class", attributes: "bold" }
// NOTHING_ELSE
@@ -0,0 +1,8 @@
fun<T : Any> foo(p: Class<T>){}
fun bar() {
foo(String::<caret>)
}
// EXIST_JAVA_ONLY: { lookupString: "class.java", itemText: "class", tailText: ".java", attributes: "bold" }
// NOTHING_ELSE
@@ -595,6 +595,18 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/smart/callableReference"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("ClassLiteral.kt")
public void testClassLiteral() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/ClassLiteral.kt");
doTest(fileName);
}
@TestMetadata("ClassLiteralDotJava.kt")
public void testClassLiteralDotJava() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/ClassLiteralDotJava.kt");
doTest(fileName);
}
@TestMetadata("EmptyQualifier1.kt")
public void testEmptyQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier1.kt");