A::class and A::class.java supported in smart completion too
This commit is contained in:
@@ -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)
|
||||
|
||||
+30
-18
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -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
|
||||
}
|
||||
}
|
||||
+48
-17
@@ -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())
|
||||
|
||||
+3
-3
@@ -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
@@ -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
|
||||
+12
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user