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.SAFE -> receiverExpression = callTypeAndReceiver.receiver
is CallTypeAndReceiver.INFIX -> receiverExpression = callTypeAndReceiver.receiver is CallTypeAndReceiver.INFIX -> receiverExpression = callTypeAndReceiver.receiver
is CallTypeAndReceiver.UNARY -> receiverExpression = null // can it happen at all? is CallTypeAndReceiver.UNARY -> receiverExpression = null // can it happen at all?
is CallTypeAndReceiver.UNKNOWN -> return emptyList()
else -> throw RuntimeException() //TODO: see KT-9394 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 import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
public sealed class CallType<TReceiver : JetElement?>(val descriptorKindFilter: DescriptorKindFilter) { public sealed class CallType<TReceiver : JetElement?>(val descriptorKindFilter: DescriptorKindFilter) {
object UNKNOWN : CallType<Nothing?>(DescriptorKindFilter.ALL)
object DEFAULT : CallType<Nothing?>(DescriptorKindFilter.ALL) object DEFAULT : CallType<Nothing?>(DescriptorKindFilter.ALL)
object DOT : CallType<JetExpression>(DescriptorKindFilter.ALL) object DOT : CallType<JetExpression>(DescriptorKindFilter.ALL)
@@ -78,6 +80,7 @@ public sealed class CallTypeAndReceiver<TReceiver : JetElement?, TCallType : Cal
val callType: TCallType, val callType: TCallType,
val receiver: TReceiver val receiver: TReceiver
) { ) {
object UNKNOWN : CallTypeAndReceiver<Nothing?, CallType.UNKNOWN>(CallType.UNKNOWN, null)
object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>(CallType.DEFAULT, null) object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>(CallType.DEFAULT, null)
class DOT(receiver: JetExpression) : CallTypeAndReceiver<JetExpression, CallType.DOT>(CallType.DOT, receiver) class DOT(receiver: JetExpression) : CallTypeAndReceiver<JetExpression, CallType.DOT>(CallType.DOT, receiver)
class SAFE(receiver: JetExpression) : CallTypeAndReceiver<JetExpression, CallType.SAFE>(CallType.SAFE, 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.CompletionResultSet
import com.intellij.codeInsight.completion.CompletionSorter import com.intellij.codeInsight.completion.CompletionSorter
import com.intellij.codeInsight.completion.CompletionType 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.codeInsight.template.TemplateManager
import com.intellij.patterns.PatternCondition import com.intellij.patterns.PatternCondition
import com.intellij.patterns.StandardPatterns 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.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.SMART_COMPLETION_ITEM_PRIORITY_KEY
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion import org.jetbrains.kotlin.idea.completion.smart.SmartCompletion
import org.jetbrains.kotlin.idea.completion.smart.SmartCompletionItemPriority 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.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import java.util.*
class BasicCompletionSession(configuration: CompletionSessionConfiguration, class BasicCompletionSession(configuration: CompletionSessionConfiguration,
parameters: CompletionParameters, parameters: CompletionParameters,
@@ -103,8 +105,9 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
private val smartCompletion = expression?.let { private val smartCompletion = expression?.let {
SmartCompletion( SmartCompletion(
it, resolutionFacade, bindingContext, isVisibleFilter, prefixMatcher, it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, prefixMatcher,
GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver, forBasicCompletion = true GlobalSearchScope.EMPTY_SCOPE, toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver,
isJvmModule, forBasicCompletion = true
) )
} }
@@ -272,9 +275,31 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
private fun completeKeywords() { 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 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 -> KeywordCompletion.complete(expression ?: parameters.getPosition(), keywordsPrefix) { lookupElement ->
val keyword = lookupElement.getLookupString() val keyword = lookupElement.lookupString
if (keyword in keywordsToSkip) return@complete
when (keyword) { when (keyword) {
// if "this" is parsed correctly in the current context - insert it and all this@xxx items // if "this" is parsed correctly in the current context - insert it and all this@xxx items
"this" -> { "this" -> {
@@ -307,20 +332,7 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
"class" -> { "class" -> {
if (callTypeAndReceiver is CallTypeAndReceiver.CALLABLE_REFERENCE) { if (callTypeAndReceiver !is CallTypeAndReceiver.CALLABLE_REFERENCE) { // otherwise it should be handled by KeywordValues
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 {
collector.addElement(lookupElement) 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.codeInsight.ReferenceVariantsHelper
import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.*
import org.jetbrains.kotlin.idea.imports.importableFqName 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.references.mainReference
import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.CallType 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 resolutionFacade = file.getResolutionFacade()
protected val moduleDescriptor = resolutionFacade.moduleDescriptor protected val moduleDescriptor = resolutionFacade.moduleDescriptor
protected val project = position.getProject() protected val project = position.getProject()
protected val isJvmModule = !ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as JetFile)
protected val nameExpression: JetSimpleNameExpression? protected val nameExpression: JetSimpleNameExpression?
protected val expression: JetExpression? protected val expression: JetExpression?
@@ -292,8 +294,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
} }
private fun Collection<CallableDescriptor>.filterShadowedNonImported(): Collection<CallableDescriptor> { 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) .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 = { val contextVariablesProvider = {
nameExpression?.let { nameExpression?.let {
referenceVariantsHelper.getReferenceVariants(it, DescriptorKindFilter.VARIABLES, { true }, CallTypeAndReceiver.DEFAULT) 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>?> { private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<JetType>?> {
if (nameExpression == null) { if (nameExpression == null) {
return CallTypeAndReceiver.DEFAULT to null return CallTypeAndReceiver.UNKNOWN to null
} }
val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression) val callTypeAndReceiver = CallTypeAndReceiver.detect(nameExpression)
@@ -346,7 +347,8 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
is CallTypeAndReceiver.IMPORT_DIRECTIVE, is CallTypeAndReceiver.IMPORT_DIRECTIVE,
is CallTypeAndReceiver.PACKAGE_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 // we don't need to highlight immediate members in these cases
return callTypeAndReceiver to null return callTypeAndReceiver to null
@@ -145,6 +145,8 @@ sealed class ArgumentPositionData(val function: FunctionDescriptor) : ExpectedIn
class ReturnValueAdditionalData(val callable: CallableDescriptor) : ExpectedInfo.AdditionalData class ReturnValueAdditionalData(val callable: CallableDescriptor) : ExpectedInfo.AdditionalData
object WhenEntryAdditionalData : ExpectedInfo.AdditionalData
class ExpectedInfos( class ExpectedInfos(
val bindingContext: BindingContext, val bindingContext: BindingContext,
val resolutionFacade: ResolutionFacade, val resolutionFacade: ResolutionFacade,
@@ -489,10 +491,10 @@ class ExpectedInfos(
val subject = whenExpression.getSubjectExpression() val subject = whenExpression.getSubjectExpression()
if (subject != null) { if (subject != null) {
val subjectType = bindingContext.getType(subject) ?: return null val subjectType = bindingContext.getType(subject) ?: return null
return listOf(ExpectedInfo(subjectType, null, null)) return listOf(ExpectedInfo(subjectType, null, null, additionalData = WhenEntryAdditionalData))
} }
else { 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.* import java.util.*
class InsertHandlerProvider( class InsertHandlerProvider(
private val callType: CallType<*>, private val callType: CallType<*>?,
expectedInfosCalculator: () -> Collection<ExpectedInfo> expectedInfosCalculator: () -> Collection<ExpectedInfo>
) { ) {
private val expectedInfos by lazy(LazyThreadSafetyMode.NONE) { expectedInfosCalculator() } private val expectedInfos by lazy(LazyThreadSafetyMode.NONE) { expectedInfosCalculator() }
public fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> { public fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> {
if (callType == null) {
error("Cannot create InsertHandler when no CallType known")
}
return when (descriptor) { return when (descriptor) {
is FunctionDescriptor -> { is FunctionDescriptor -> {
val needTypeArguments = needTypeArguments(descriptor) 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( class LookupElementFactory(
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<JetType>?, private val receiverTypes: Collection<JetType>?,
private val callType: CallType<*>, private val callType: CallType<*>?,
private val isInStringTemplateAfterDollar: Boolean, private val isInStringTemplateAfterDollar: Boolean,
public val insertHandlerProvider: InsertHandlerProvider, public val insertHandlerProvider: InsertHandlerProvider,
contextVariablesProvider: () -> Collection<VariableDescriptor> contextVariablesProvider: () -> Collection<VariableDescriptor>
@@ -132,7 +132,7 @@ class LookupElementFactory(
} }
override fun handleInsert(context: InsertionContext) { 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) { 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.LookupElement
import com.intellij.codeInsight.lookup.LookupElementWeigher import com.intellij.codeInsight.lookup.LookupElementWeigher
import com.intellij.codeInsight.lookup.WeighingContext import com.intellij.codeInsight.lookup.WeighingContext
import com.intellij.openapi.util.Key
import com.intellij.psi.util.proximity.PsiProximityComparator import com.intellij.psi.util.proximity.PsiProximityComparator
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind 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.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallType import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.FuzzyType 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") { object PriorityWeigher : LookupElementWeigher("kotlin.priority") {
override fun weigh(element: LookupElement, context: WeighingContext) override fun weigh(element: LookupElement, context: WeighingContext)
@@ -184,26 +182,32 @@ class SmartCompletionInBasicWeigher(
private val callType: CallType<*>, private val callType: CallType<*>,
private val resolutionFacade: ResolutionFacade private val resolutionFacade: ResolutionFacade
) : LookupElementWeigher("kotlin.smartInBasic", true, false) { ) : 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 descriptorsToSkip = smartCompletion.descriptorsToSkip
private val expectedInfos = smartCompletion.expectedInfos 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 fun fullMatchWeight(nameSimilarity: Int) = (3L shl 32) + nameSimilarity
private val MATCHED_TRUE_WEIGHT = (3L shl 32) + 2
private val MATCHED_FALSE_WEIGHT = (3L shl 32) + 1
private fun ifNotNullMatchWeight(nameSimilarity: Int) = (2L shl 32) + nameSimilarity private fun ifNotNullMatchWeight(nameSimilarity: Int) = (2L shl 32) + nameSimilarity
private fun smartCompletionItemWeight(nameSimilarity: Int) = (1L 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 NO_MATCH_WEIGHT = 0L
private val DESCRIPTOR_TO_SKIP_WEIGHT = -1L // if descriptor is skipped from smart completion then it's probably irrelevant 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 { override fun weigh(element: LookupElement): Long {
val keywordValueMatched = element.getUserData(KEYWORD_VALUE_MATCHED_KEY)
val smartCompletionPriority = element.getUserData(SMART_COMPLETION_ITEM_PRIORITY_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 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) return smartCompletionItemWeight(element.getUserData(NAME_SIMILARITY_KEY) ?: 0)
} }
@@ -214,28 +218,6 @@ class SmartCompletionInBasicWeigher(
if (expectedInfos.isEmpty()) return NO_MATCH_WEIGHT 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 smartCastCalculator = smartCompletion.smartCastCalculator
val (fuzzyTypes, name) = when (o) { 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.InsertionContext
import com.intellij.codeInsight.completion.OffsetKey import com.intellij.codeInsight.completion.OffsetKey
import com.intellij.codeInsight.completion.PrefixMatcher import com.intellij.codeInsight.completion.PrefixMatcher
import com.intellij.codeInsight.lookup.Lookup import com.intellij.codeInsight.lookup.*
import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.codeInsight.lookup.LookupElementDecorator
import com.intellij.codeInsight.lookup.LookupElementPresentation
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.completion.* 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.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.FuzzyType
@@ -52,12 +50,14 @@ class SmartCompletion(
private val expression: JetExpression, private val expression: JetExpression,
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext, private val bindingContext: BindingContext,
private val moduleDescriptor: ModuleDescriptor,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean, private val visibilityFilter: (DeclarationDescriptor) -> Boolean,
private val prefixMatcher: PrefixMatcher, private val prefixMatcher: PrefixMatcher,
private val inheritorSearchScope: GlobalSearchScope, private val inheritorSearchScope: GlobalSearchScope,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper, private val toFromOriginalFileMapper: ToFromOriginalFileMapper,
private val lookupElementFactory: LookupElementFactory, private val lookupElementFactory: LookupElementFactory,
private val callTypeAndReceiver: CallTypeAndReceiver<*, *>, private val callTypeAndReceiver: CallTypeAndReceiver<*, *>,
private val isJvmModule: Boolean,
private val forBasicCompletion: Boolean = false private val forBasicCompletion: Boolean = false
) { ) {
private val expressionWithType = when (callTypeAndReceiver) { private val expressionWithType = when (callTypeAndReceiver) {
@@ -150,10 +150,11 @@ class SmartCompletion(
} }
private fun filterDescriptor(descriptor: DeclarationDescriptor): Collection<LookupElement> { private fun filterDescriptor(descriptor: DeclarationDescriptor): Collection<LookupElement> {
val callType = callTypeAndReceiver.callType
if (descriptor in descriptorsToSkip) return emptyList() if (descriptor in descriptorsToSkip) return emptyList()
val result = SmartList<LookupElement>() 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) } val infoClassifier = { expectedInfo: ExpectedInfo -> types.classifyExpectedInfo(expectedInfo) }
result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { descriptor -> result.addLookupElements(descriptor, expectedInfos, infoClassifier, noNameSimilarityForReturnItself = callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { descriptor ->
@@ -177,23 +178,53 @@ class SmartCompletion(
val items = ArrayList<LookupElement>() val items = ArrayList<LookupElement>()
val inheritanceSearchers = ArrayList<InheritanceItemsSearcher>() val inheritanceSearchers = ArrayList<InheritanceItemsSearcher>()
if (expectedInfos.isNotEmpty() && callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { if (!forBasicCompletion) { // basic completion adds keyword values on its own
TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion) val keywordValueConsumer = object : KeywordValues.Consumer {
.addTo(items, inheritanceSearchers, expectedInfos) override fun consume(lookupString: String, expectedInfoClassifier: (ExpectedInfo) -> ExpectedInfoClassification, priority: SmartCompletionItemPriority, factory: () -> LookupElement) {
items.addLookupElements(null, expectedInfos, expectedInfoClassifier) {
if (expression is JetSimpleNameExpression) { val lookupElement = factory()
StaticMembers(bindingContext, lookupElementFactory).addToCollection(items, expectedInfos, expression, descriptorsToSkip) 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) 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()) val inheritanceSearcher = if (inheritanceSearchers.isNotEmpty())
@@ -39,9 +39,9 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) { private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) {
expression?.let { expression?.let {
SmartCompletion(it, resolutionFacade, SmartCompletion(it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter,
bindingContext, isVisibleFilter, prefixMatcher, originalSearchScope, prefixMatcher, originalSearchScope, toFromOriginalFileMapper, lookupElementFactory,
toFromOriginalFileMapper, lookupElementFactory, callTypeAndReceiver) callTypeAndReceiver, isJvmModule)
} }
} }
@@ -289,6 +289,7 @@ enum class SmartCompletionItemPriority {
IT, IT,
TRUE, TRUE,
FALSE, FALSE,
CLASS_LITERAL,
THIS, THIS,
DEFAULT, DEFAULT,
NULLABLE, NULLABLE,
+3 -2
View File
@@ -7,10 +7,11 @@ fun f(e1: E, e2: E?, x: Any) {
if (e1 == <caret> if (e1 == <caret>
} }
// WITH_ORDER
// EXIST: { itemText:"e2" }
// EXIST: { lookupString:"A", itemText:"E.A", typeText:"E" } // EXIST: { lookupString:"A", itemText:"E.A", typeText:"E" }
// EXIST: { lookupString:"B", itemText:"E.B", typeText:"E" } // EXIST: { lookupString:"B", itemText:"E.B", typeText:"E" }
// EXIST: { itemText:"e2" } // EXIST: null
// ABSENT: { itemText:"!! e2" } // ABSENT: { itemText:"!! e2" }
// ABSENT: e1 // ABSENT: e1
// ABSENT: x // 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); 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") @TestMetadata("EmptyQualifier1.kt")
public void testEmptyQualifier1() throws Exception { public void testEmptyQualifier1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier1.kt"); String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/smart/callableReference/EmptyQualifier1.kt");