Performance optimization in completion: search context variables of functional types later
This commit is contained in:
+47
-26
@@ -154,22 +154,30 @@ class BasicCompletionSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun doComplete() {
|
override fun doComplete() {
|
||||||
if (smartCompletion != null) {
|
fun completeWithSmartCompletion(lookupElementFactory: LookupElementFactory) {
|
||||||
val (additionalItems, @Suppress("UNUSED_VARIABLE") inheritanceSearcher) = smartCompletion.additionalItems(lookupElementFactory)
|
if (smartCompletion != null) {
|
||||||
|
val (additionalItems, @Suppress("UNUSED_VARIABLE") inheritanceSearcher) = smartCompletion.additionalItems(lookupElementFactory)
|
||||||
|
|
||||||
// all additional items should have SMART_COMPLETION_ITEM_PRIORITY_KEY to be recognized by SmartCompletionInBasicWeigher
|
// all additional items should have SMART_COMPLETION_ITEM_PRIORITY_KEY to be recognized by SmartCompletionInBasicWeigher
|
||||||
for (item in additionalItems) {
|
for (item in additionalItems) {
|
||||||
if (item.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) == null) {
|
if (item.getUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY) == null) {
|
||||||
item.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.DEFAULT)
|
item.putUserData(SMART_COMPLETION_ITEM_PRIORITY_KEY, SmartCompletionItemPriority.DEFAULT)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
collector.addElements(additionalItems)
|
collector.addElements(additionalItems)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val (imported, notImported) = referenceVariants!!
|
val contextVariableTypesForSmartCompletion = withCollectRequiredContextVariableTypes(::completeWithSmartCompletion)
|
||||||
collector.addDescriptorElements(imported, lookupElementFactory)
|
|
||||||
collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true)
|
fun completeReferenceVariants(lookupElementFactory: LookupElementFactory) {
|
||||||
|
val (imported, notImported) = referenceVariants!!
|
||||||
|
collector.addDescriptorElements(imported, lookupElementFactory)
|
||||||
|
collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
val contextVariableTypesForReferenceVariants = withCollectRequiredContextVariableTypes(::completeReferenceVariants)
|
||||||
|
|
||||||
KEYWORDS_ONLY.doComplete()
|
KEYWORDS_ONLY.doComplete()
|
||||||
|
|
||||||
@@ -188,7 +196,7 @@ class BasicCompletionSession(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
packageNames.forEach { collector.addElement(lookupElementFactory.createLookupElementForPackage(it)) }
|
packageNames.forEach { collector.addElement(basicLookupElementFactory.createLookupElementForPackage(it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
flushToResultSet()
|
flushToResultSet()
|
||||||
@@ -196,22 +204,35 @@ class BasicCompletionSession(
|
|||||||
NamedArgumentCompletion.complete(collector, expectedInfos)
|
NamedArgumentCompletion.complete(collector, expectedInfos)
|
||||||
flushToResultSet()
|
flushToResultSet()
|
||||||
|
|
||||||
completeNonImported()
|
val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position)
|
||||||
flushToResultSet()
|
withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory ->
|
||||||
|
if (contextVariableTypesForSmartCompletion.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
|
||||||
|
completeWithSmartCompletion(lookupElementFactory)
|
||||||
|
}
|
||||||
|
|
||||||
if (position.containingFile is KtCodeFragment) {
|
if (contextVariableTypesForReferenceVariants.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
|
||||||
val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants()
|
val (imported, notImported) = referenceVariantsWithSingleFunctionTypeParameter()!!
|
||||||
if (variantsAndFactory != null) {
|
collector.addDescriptorElements(imported, lookupElementFactory)
|
||||||
val variants = variantsAndFactory.first
|
collector.addDescriptorElements(notImported, lookupElementFactory, notImported = true)
|
||||||
val lookupElementFactory = variantsAndFactory.second
|
}
|
||||||
collector.addDescriptorElements(variants.imported, lookupElementFactory, withReceiverCast = true)
|
|
||||||
collector.addDescriptorElements(variants.notImportedExtensions, lookupElementFactory, withReceiverCast = true, notImported = true)
|
completeNonImported(lookupElementFactory)
|
||||||
flushToResultSet()
|
flushToResultSet()
|
||||||
|
|
||||||
|
if (position.containingFile is KtCodeFragment) {
|
||||||
|
val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants(lookupElementFactory)
|
||||||
|
if (variantsAndFactory != null) {
|
||||||
|
val variants = variantsAndFactory.first
|
||||||
|
@Suppress("NAME_SHADOWING") val lookupElementFactory = variantsAndFactory.second
|
||||||
|
collector.addDescriptorElements(variants.imported, lookupElementFactory, withReceiverCast = true)
|
||||||
|
collector.addDescriptorElements(variants.notImportedExtensions, lookupElementFactory, withReceiverCast = true, notImported = true)
|
||||||
|
flushToResultSet()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun completeNonImported() {
|
private fun completeNonImported(lookupElementFactory: LookupElementFactory) {
|
||||||
if (shouldCompleteTopLevelCallablesFromIndex()) {
|
if (shouldCompleteTopLevelCallablesFromIndex()) {
|
||||||
collector.addDescriptorElements(getTopLevelCallables(), lookupElementFactory, notImported = true)
|
collector.addDescriptorElements(getTopLevelCallables(), lookupElementFactory, notImported = true)
|
||||||
}
|
}
|
||||||
@@ -292,7 +313,7 @@ class BasicCompletionSession(
|
|||||||
"override" -> {
|
"override" -> {
|
||||||
collector.addElement(lookupElement)
|
collector.addElement(lookupElement)
|
||||||
|
|
||||||
OverridesCompletion(collector, lookupElementFactory).complete(position)
|
OverridesCompletion(collector, basicLookupElementFactory).complete(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
"class" -> {
|
"class" -> {
|
||||||
@@ -377,7 +398,7 @@ class BasicCompletionSession(
|
|||||||
KEYWORDS_ONLY.doComplete()
|
KEYWORDS_ONLY.doComplete()
|
||||||
|
|
||||||
if (shouldCompleteParameterNameAndType()) {
|
if (shouldCompleteParameterNameAndType()) {
|
||||||
val parameterNameAndTypeCompletion = ParameterNameAndTypeCompletion(collector, lookupElementFactory, prefixMatcher, resolutionFacade)
|
val parameterNameAndTypeCompletion = ParameterNameAndTypeCompletion(collector, basicLookupElementFactory, prefixMatcher, resolutionFacade)
|
||||||
|
|
||||||
// if we are typing parameter name, restart completion each time we type an upper case letter because new suggestions will appear (previous words can be used as user prefix)
|
// if we are typing parameter name, restart completion each time we type an upper case letter because new suggestions will appear (previous words can be used as user prefix)
|
||||||
val prefixPattern = StandardPatterns.string().with(object : PatternCondition<String>("Prefix ends with uppercase letter") {
|
val prefixPattern = StandardPatterns.string().with(object : PatternCondition<String>("Prefix ends with uppercase letter") {
|
||||||
@@ -470,7 +491,7 @@ class BasicCompletionSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
superClasses
|
superClasses
|
||||||
.map { lookupElementFactory.createLookupElement(it, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false) }
|
.map { basicLookupElementFactory.createLookupElement(it, qualifyNestedClasses = true, includeClassTypeArguments = false) }
|
||||||
.forEach { collector.addElement(it) }
|
.forEach { collector.addElement(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
|
|||||||
|
|
||||||
class BasicLookupElementFactory(
|
class BasicLookupElementFactory(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val insertHandlerProvider: InsertHandlerProvider
|
val insertHandlerProvider: InsertHandlerProvider
|
||||||
) {
|
) {
|
||||||
public fun createLookupElement(
|
public fun createLookupElement(
|
||||||
descriptor: DeclarationDescriptor,
|
descriptor: DeclarationDescriptor,
|
||||||
|
|||||||
+37
-25
@@ -132,14 +132,16 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter)
|
protected val referenceVariantsHelper = ReferenceVariantsHelper(bindingContext, resolutionFacade, isVisibleFilter)
|
||||||
|
|
||||||
protected val callTypeAndReceiver: CallTypeAndReceiver<*, *>
|
protected val callTypeAndReceiver: CallTypeAndReceiver<*, *>
|
||||||
protected val lookupElementFactory: LookupElementFactory
|
protected val receiverTypes: Collection<KotlinType>?
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes()
|
val (callTypeAndReceiver, receiverTypes) = detectCallTypeAndReceiverTypes()
|
||||||
this.callTypeAndReceiver = callTypeAndReceiver
|
this.callTypeAndReceiver = callTypeAndReceiver
|
||||||
this.lookupElementFactory = createLookupElementFactory(callTypeAndReceiver.callType, receiverTypes)
|
this.receiverTypes = receiverTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected val basicLookupElementFactory = BasicLookupElementFactory(project, InsertHandlerProvider(callTypeAndReceiver.callType) { expectedInfos })
|
||||||
|
|
||||||
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
|
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
|
||||||
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
|
protected val collector: LookupElementsCollector by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter())
|
LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter())
|
||||||
@@ -273,12 +275,13 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
|
data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
|
||||||
|
|
||||||
protected val referenceVariants: ReferenceVariants? by lazy {
|
protected val referenceVariants: ReferenceVariants? by lazy {
|
||||||
descriptorKindFilter?.let { collectReferenceVariants(it) }
|
if (nameExpression != null && descriptorKindFilter != null) collectReferenceVariants(descriptorKindFilter!!, nameExpression) else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter, runtimeReceiver: ExpressionReceiver? = null): ReferenceVariants {
|
|
||||||
|
private fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter, nameExpression: KtSimpleNameExpression, runtimeReceiver: ExpressionReceiver? = null): ReferenceVariants {
|
||||||
var variants = referenceVariantsHelper.getReferenceVariants(
|
var variants = referenceVariantsHelper.getReferenceVariants(
|
||||||
nameExpression!!,
|
nameExpression,
|
||||||
descriptorKindFilter,
|
descriptorKindFilter,
|
||||||
descriptorNameFilter,
|
descriptorNameFilter,
|
||||||
filterOutJavaGettersAndSetters = false,
|
filterOutJavaGettersAndSetters = false,
|
||||||
@@ -315,8 +318,15 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
return ReferenceVariants(variants, notImportedExtensions)
|
return ReferenceVariants(variants, notImportedExtensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun getRuntimeReceiverTypeReferenceVariants(): Pair<ReferenceVariants, LookupElementFactory>? {
|
protected fun referenceVariantsWithSingleFunctionTypeParameter(): ReferenceVariants? {
|
||||||
|
val variants = referenceVariants ?: return null
|
||||||
|
val filter: (DeclarationDescriptor) -> Boolean = { it is FunctionDescriptor && LookupElementFactory.hasSingleFunctionTypeParameter(it) }
|
||||||
|
return ReferenceVariants(variants.imported.filter(filter), variants.notImportedExtensions.filter(filter))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getRuntimeReceiverTypeReferenceVariants(lookupElementFactory: LookupElementFactory): Pair<ReferenceVariants, LookupElementFactory>? {
|
||||||
val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return null
|
val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return null
|
||||||
|
val referenceVariants = referenceVariants ?: return null
|
||||||
|
|
||||||
val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return null
|
val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return null
|
||||||
val type = bindingContext.getType(explicitReceiver) ?: return null
|
val type = bindingContext.getType(explicitReceiver) ?: return null
|
||||||
@@ -325,12 +335,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
val runtimeType = evaluator(explicitReceiver)
|
val runtimeType = evaluator(explicitReceiver)
|
||||||
if (runtimeType == null || runtimeType == type) return null
|
if (runtimeType == null || runtimeType == type) return null
|
||||||
|
|
||||||
val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, ExpressionReceiver(explicitReceiver, runtimeType))
|
val (variants, notImportedExtensions) = collectReferenceVariants(descriptorKindFilter!!, nameExpression!!, ExpressionReceiver(explicitReceiver, runtimeType))
|
||||||
val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants!!.imported)
|
val filteredVariants = filterVariantsForRuntimeReceiverType(variants, referenceVariants.imported)
|
||||||
val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants!!.notImportedExtensions)
|
val filteredNotImportedExtensions = filterVariantsForRuntimeReceiverType(notImportedExtensions, referenceVariants.notImportedExtensions)
|
||||||
|
|
||||||
val referenceVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions)
|
val runtimeVariants = ReferenceVariants(filteredVariants, filteredNotImportedExtensions)
|
||||||
return Pair(referenceVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType)))
|
return Pair(runtimeVariants, lookupElementFactory.copy(receiverTypes = listOf(runtimeType)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <TDescriptor : DeclarationDescriptor> filterVariantsForRuntimeReceiverType(
|
private fun <TDescriptor : DeclarationDescriptor> filterVariantsForRuntimeReceiverType(
|
||||||
@@ -373,24 +383,26 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
|
|||||||
protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) {
|
protected fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean) {
|
||||||
AllClassesCompletion(parameters, indicesHelper, prefixMatcher, resolutionFacade, kindFilter)
|
AllClassesCompletion(parameters, indicesHelper, prefixMatcher, resolutionFacade, kindFilter)
|
||||||
.collect(
|
.collect(
|
||||||
{ descriptor -> collector.addDescriptorElements(descriptor, lookupElementFactory, notImported = true) },
|
{ descriptor -> collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) },
|
||||||
{ javaClass -> collector.addElement(lookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) }
|
{ javaClass -> collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLookupElementFactory(callType: CallType<*>?, receiverTypes: Collection<KotlinType>?): LookupElementFactory {
|
protected fun withCollectRequiredContextVariableTypes(action: (LookupElementFactory) -> Unit): Collection<FuzzyType> {
|
||||||
val contextVariablesProvider = {
|
val provider = CollectRequiredTypesContextVariablesProvider()
|
||||||
nameExpression?.let {
|
val lookupElementFactory = createLookupElementFactory(provider)
|
||||||
val descriptorFilter = DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons
|
action(lookupElementFactory)
|
||||||
referenceVariantsHelper.getReferenceVariants(it, CallTypeAndReceiver.DEFAULT, descriptorFilter, nameFilter = { true })
|
return provider.requiredTypes
|
||||||
.map { it as VariableDescriptor }
|
}
|
||||||
} ?: emptyList()
|
|
||||||
}
|
|
||||||
|
|
||||||
val insertHandlerProvider = InsertHandlerProvider(callType) { expectedInfos }
|
protected fun withContextVariablesProvider(contextVariablesProvider: ContextVariablesProvider, action: (LookupElementFactory) -> Unit) {
|
||||||
return LookupElementFactory(resolutionFacade, receiverTypes,
|
val lookupElementFactory = createLookupElementFactory(contextVariablesProvider)
|
||||||
callType, expression?.parent is KtSimpleNameStringTemplateEntry,
|
action(lookupElementFactory)
|
||||||
insertHandlerProvider, contextVariablesProvider)
|
}
|
||||||
|
|
||||||
|
protected fun createLookupElementFactory(contextVariablesProvider: ContextVariablesProvider): LookupElementFactory {
|
||||||
|
return LookupElementFactory(basicLookupElementFactory, resolutionFacade, receiverTypes,
|
||||||
|
callTypeAndReceiver.callType, contextVariablesProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> {
|
private fun detectCallTypeAndReceiverTypes(): Pair<CallTypeAndReceiver<*, *>, Collection<KotlinType>?> {
|
||||||
|
|||||||
@@ -29,12 +29,8 @@ import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
|
|||||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||||
import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler
|
import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler
|
||||||
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.*
|
||||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
|
||||||
import org.jetbrains.kotlin.idea.util.findLabelAndCall
|
|
||||||
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
@@ -94,22 +90,6 @@ fun LookupElement.withReceiverCast(): LookupElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LookupElement.withBracesSurrounding(): LookupElement {
|
|
||||||
return object: LookupElementDecorator<LookupElement>(this) {
|
|
||||||
override fun handleInsert(context: InsertionContext) {
|
|
||||||
val startOffset = context.getStartOffset()
|
|
||||||
context.getDocument().insertString(startOffset, "{")
|
|
||||||
context.getOffsetMap().addOffset(CompletionInitializationContext.START_OFFSET, startOffset + 1)
|
|
||||||
|
|
||||||
val tailOffset = context.getTailOffset()
|
|
||||||
context.getDocument().insertString(tailOffset, "}")
|
|
||||||
context.setTailOffset(tailOffset)
|
|
||||||
|
|
||||||
super.handleInsert(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY = Key<Unit>("KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY")
|
val KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY = Key<Unit>("KEEP_OLD_ARGUMENT_LIST_ON_TAB_KEY")
|
||||||
|
|
||||||
fun LookupElement.keepOldArgumentListOnTab(): LookupElement {
|
fun LookupElement.keepOldArgumentListOnTab(): LookupElement {
|
||||||
@@ -310,7 +290,7 @@ fun breakOrContinueExpressionItems(position: KtElement, breakOrContinue: String)
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? {
|
fun BasicLookupElementFactory.createLookupElementForType(type: KotlinType): LookupElement? {
|
||||||
if (type.isError()) return null
|
if (type.isError()) return null
|
||||||
|
|
||||||
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type)) {
|
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type)) {
|
||||||
@@ -320,7 +300,7 @@ fun LookupElementFactory.createLookupElementForType(type: KotlinType): LookupEle
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null
|
val classifier = type.getConstructor().getDeclarationDescriptor() ?: return null
|
||||||
val baseLookupElement = createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true, includeClassTypeArguments = false)
|
val baseLookupElement = createLookupElement(classifier, qualifyNestedClasses = true, includeClassTypeArguments = false)
|
||||||
|
|
||||||
val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)
|
val itemText = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.renderType(type)
|
||||||
|
|
||||||
|
|||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.psi.PsiElement
|
||||||
|
import com.intellij.util.SmartList
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||||
|
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
|
||||||
|
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||||
|
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||||
|
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
interface ContextVariablesProvider {
|
||||||
|
fun functionTypeVariables(requiredType: FuzzyType): Collection<Pair<VariableDescriptor, TypeSubstitutor>>
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealContextVariablesProvider(
|
||||||
|
private val referenceVariantsHelper: ReferenceVariantsHelper,
|
||||||
|
private val contextElement: PsiElement
|
||||||
|
) : ContextVariablesProvider {
|
||||||
|
private val functionTypeVariables by lazy {
|
||||||
|
collectVariables().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectVariables(): Collection<VariableDescriptor> {
|
||||||
|
val descriptorFilter = DescriptorKindFilter.VARIABLES exclude DescriptorKindExclude.Extensions // we exclude extensions by performance reasons
|
||||||
|
return referenceVariantsHelper.getReferenceVariants(contextElement, CallTypeAndReceiver.DEFAULT, descriptorFilter, nameFilter = { true })
|
||||||
|
.map { it as VariableDescriptor }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun functionTypeVariables(requiredType: FuzzyType): Collection<Pair<VariableDescriptor, TypeSubstitutor>> {
|
||||||
|
val result = SmartList<Pair<VariableDescriptor, TypeSubstitutor>>()
|
||||||
|
for (variable in functionTypeVariables) {
|
||||||
|
val substitutor = variable.fuzzyReturnType()?.checkIsSubtypeOf(requiredType) ?: continue
|
||||||
|
result.add(variable to substitutor)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CollectRequiredTypesContextVariablesProvider : ContextVariablesProvider {
|
||||||
|
private val _requiredTypes = HashSet<FuzzyType>()
|
||||||
|
|
||||||
|
public val requiredTypes: Set<FuzzyType>
|
||||||
|
get() = _requiredTypes
|
||||||
|
|
||||||
|
override fun functionTypeVariables(requiredType: FuzzyType): Collection<Pair<VariableDescriptor, TypeSubstitutor>> {
|
||||||
|
_requiredTypes.add(requiredType)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -87,7 +87,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
|
|||||||
.filter { it.getName().asString() !in documentedParameters }
|
.filter { it.getName().asString() !in documentedParameters }
|
||||||
|
|
||||||
descriptors.forEach {
|
descriptors.forEach {
|
||||||
collector.addElement(lookupElementFactory.createLookupElement(it, useReceiverTypes = false, parametersAndTypeGrayed = true))
|
collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
|
|||||||
}
|
}
|
||||||
|
|
||||||
scope.collectDescriptorsFiltered(nameFilter = descriptorNameFilter).filter(::isApplicable).forEach {
|
scope.collectDescriptorsFiltered(nameFilter = descriptorNameFilter).filter(::isApplicable).forEach {
|
||||||
val element = lookupElementFactory.createLookupElement(it, useReceiverTypes = false, parametersAndTypeGrayed = true)
|
val element = basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)
|
||||||
collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
|
collector.addElement(object: LookupElementDecorator<LookupElement>(element) {
|
||||||
override fun handleInsert(context: InsertionContext?) {
|
override fun handleInsert(context: InsertionContext?) {
|
||||||
// insert only plain name here, no qualifier/parentheses/etc.
|
// insert only plain name here, no qualifier/parentheses/etc.
|
||||||
|
|||||||
+19
-42
@@ -21,7 +21,6 @@ import com.intellij.codeInsight.lookup.LookupElement
|
|||||||
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
import com.intellij.codeInsight.lookup.LookupElementDecorator
|
||||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||||
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer
|
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer
|
||||||
import com.intellij.psi.PsiClass
|
|
||||||
import com.intellij.util.SmartList
|
import com.intellij.util.SmartList
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
@@ -31,9 +30,8 @@ import org.jetbrains.kotlin.idea.completion.handlers.lambdaPresentation
|
|||||||
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.idea.util.fuzzyReturnType
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
|
import org.jetbrains.kotlin.renderer.render
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
|
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
|
||||||
@@ -44,28 +42,27 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
|||||||
|
|
||||||
data /* we need copy() */
|
data /* we need copy() */
|
||||||
class LookupElementFactory(
|
class LookupElementFactory(
|
||||||
|
val basicFactory: BasicLookupElementFactory,
|
||||||
private val resolutionFacade: ResolutionFacade,
|
private val resolutionFacade: ResolutionFacade,
|
||||||
private val receiverTypes: Collection<KotlinType>?,
|
private val receiverTypes: Collection<KotlinType>?,
|
||||||
private val callType: CallType<*>?,
|
private val callType: CallType<*>?,
|
||||||
private val isInStringTemplateAfterDollar: Boolean,
|
private val contextVariablesProvider: ContextVariablesProvider
|
||||||
public val insertHandlerProvider: InsertHandlerProvider,
|
|
||||||
private val contextVariablesProvider: () -> Collection<VariableDescriptor>
|
|
||||||
) {
|
) {
|
||||||
private val basicFactory = BasicLookupElementFactory(resolutionFacade.project, insertHandlerProvider)
|
companion object {
|
||||||
|
fun hasSingleFunctionTypeParameter(descriptor: FunctionDescriptor): Boolean {
|
||||||
private val functionTypeContextVariables by lazy(LazyThreadSafetyMode.NONE) {
|
val parameter = descriptor.original.valueParameters.singleOrNull() ?: return false
|
||||||
contextVariablesProvider().filter { KotlinBuiltIns.isFunctionOrExtensionFunctionType(it.type) }
|
return KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameter.type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val insertHandlerProvider = basicFactory.insertHandlerProvider
|
||||||
|
|
||||||
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
|
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
|
||||||
val result = SmartList<LookupElement>()
|
val result = SmartList<LookupElement>()
|
||||||
|
|
||||||
val isNormalCall = callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE
|
val isNormalCall = callType == CallType.DEFAULT || callType == CallType.DOT || callType == CallType.SAFE
|
||||||
|
|
||||||
var lookupElement = createLookupElement(descriptor, useReceiverTypes, parametersAndTypeGrayed = !isNormalCall && callType != CallType.INFIX)
|
var lookupElement = createLookupElement(descriptor, useReceiverTypes, parametersAndTypeGrayed = !isNormalCall && callType != CallType.INFIX)
|
||||||
if (isInStringTemplateAfterDollar && (descriptor is FunctionDescriptor || descriptor is ClassifierDescriptor)) {
|
|
||||||
lookupElement = lookupElement.withBracesSurrounding()
|
|
||||||
}
|
|
||||||
result.add(lookupElement)
|
result.add(lookupElement)
|
||||||
|
|
||||||
// add special item for function with one argument of function type with more than one parameter
|
// add special item for function with one argument of function type with more than one parameter
|
||||||
@@ -80,11 +77,12 @@ class LookupElementFactory(
|
|||||||
// check that all parameters except for the last one are optional
|
// check that all parameters except for the last one are optional
|
||||||
val lastParameter = descriptor.valueParameters.lastOrNull() ?: return
|
val lastParameter = descriptor.valueParameters.lastOrNull() ?: return
|
||||||
if (!descriptor.valueParameters.all { it == lastParameter || it.hasDefaultValue() }) return
|
if (!descriptor.valueParameters.all { it == lastParameter || it.hasDefaultValue() }) return
|
||||||
val parameterType = lastParameter.type
|
|
||||||
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
|
|
||||||
val isSingleParameter = descriptor.valueParameters.size() == 1
|
|
||||||
|
|
||||||
val functionParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
|
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(lastParameter.original.type)) {
|
||||||
|
val isSingleParameter = descriptor.valueParameters.size == 1
|
||||||
|
|
||||||
|
val parameterType = lastParameter.type
|
||||||
|
val functionParameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size
|
||||||
// we don't need special item inserting lambda for single functional parameter that does not need multiple arguments because the default item will be special in this case
|
// we don't need special item inserting lambda for single functional parameter that does not need multiple arguments because the default item will be special in this case
|
||||||
if (!isSingleParameter || functionParameterCount > 1) {
|
if (!isSingleParameter || functionParameterCount > 1) {
|
||||||
add(createFunctionCallElementWithLambda(descriptor, parameterType, functionParameterCount > 1, useReceiverTypes))
|
add(createFunctionCallElementWithLambda(descriptor, parameterType, functionParameterCount > 1, useReceiverTypes))
|
||||||
@@ -94,12 +92,9 @@ class LookupElementFactory(
|
|||||||
//TODO: also ::function? at least for local functions
|
//TODO: also ::function? at least for local functions
|
||||||
//TODO: order for them
|
//TODO: order for them
|
||||||
val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters)
|
val fuzzyParameterType = FuzzyType(parameterType, descriptor.typeParameters)
|
||||||
for (variable in functionTypeContextVariables) {
|
for ((variable, substitutor) in contextVariablesProvider.functionTypeVariables(fuzzyParameterType)) {
|
||||||
val substitutor = variable.fuzzyReturnType()?.checkIsSubtypeOf(fuzzyParameterType)
|
val substitutedDescriptor = descriptor.substitute(substitutor)
|
||||||
if (substitutor != null) {
|
add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.render(), useReceiverTypes))
|
||||||
val substitutedDescriptor = descriptor.substitute(substitutor) ?: continue
|
|
||||||
add(createFunctionCallElementWithArgument(substitutedDescriptor, variable.name.asString(), useReceiverTypes))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,10 +134,6 @@ class LookupElementFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInStringTemplateAfterDollar) {
|
|
||||||
lookupElement = lookupElement.withBracesSurrounding()
|
|
||||||
}
|
|
||||||
|
|
||||||
return lookupElement
|
return lookupElement
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,13 +141,7 @@ class LookupElementFactory(
|
|||||||
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
|
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
|
||||||
|
|
||||||
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
|
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler.Normal).inputTypeArguments
|
||||||
lookupElement = FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments)
|
return FunctionCallWithArgumentLookupElement(lookupElement, descriptor, argumentText, needTypeArguments)
|
||||||
|
|
||||||
if (isInStringTemplateAfterDollar) {
|
|
||||||
lookupElement = lookupElement.withBracesSurrounding()
|
|
||||||
}
|
|
||||||
|
|
||||||
return lookupElement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class FunctionCallWithArgumentLookupElement(
|
private inner class FunctionCallWithArgumentLookupElement(
|
||||||
@@ -283,12 +268,4 @@ class LookupElementFactory(
|
|||||||
val typeParameter = receiverParameter.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false
|
val typeParameter = receiverParameter.type.constructor.declarationDescriptor as? TypeParameterDescriptor ?: return false
|
||||||
return typeParameter.containingDeclaration == original
|
return typeParameter.containingDeclaration == original
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement {
|
|
||||||
return basicFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses, includeClassTypeArguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun createLookupElementForPackage(name: FqName): LookupElement {
|
|
||||||
return basicFactory.createLookupElementForPackage(name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -49,7 +49,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
|||||||
|
|
||||||
class OverridesCompletion(
|
class OverridesCompletion(
|
||||||
private val collector: LookupElementsCollector,
|
private val collector: LookupElementsCollector,
|
||||||
private val lookupElementFactory: LookupElementFactory
|
private val lookupElementFactory: BasicLookupElementFactory
|
||||||
) {
|
) {
|
||||||
private val PRESENTATION_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions {
|
private val PRESENTATION_RENDERER = IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.withOptions {
|
||||||
modifiers = emptySet()
|
modifiers = emptySet()
|
||||||
@@ -66,7 +66,7 @@ class OverridesCompletion(
|
|||||||
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
|
if (isConstructorParameter && memberObject.descriptor !is PropertyDescriptor) continue
|
||||||
|
|
||||||
val descriptor = memberObject.descriptor
|
val descriptor = memberObject.descriptor
|
||||||
var lookupElement = lookupElementFactory.createLookupElement(descriptor, useReceiverTypes = false)
|
var lookupElement = lookupElementFactory.createLookupElement(descriptor)
|
||||||
|
|
||||||
var text = "override " + PRESENTATION_RENDERER.render(descriptor)
|
var text = "override " + PRESENTATION_RENDERER.render(descriptor)
|
||||||
if (descriptor is FunctionDescriptor) {
|
if (descriptor is FunctionDescriptor) {
|
||||||
|
|||||||
+9
-9
@@ -48,7 +48,7 @@ import java.util.*
|
|||||||
|
|
||||||
class ParameterNameAndTypeCompletion(
|
class ParameterNameAndTypeCompletion(
|
||||||
private val collector: LookupElementsCollector,
|
private val collector: LookupElementsCollector,
|
||||||
private val lookupElementFactory: LookupElementFactory,
|
private val lookupElementFactory: BasicLookupElementFactory,
|
||||||
private val prefixMatcher: PrefixMatcher,
|
private val prefixMatcher: PrefixMatcher,
|
||||||
private val resolutionFacade: ResolutionFacade
|
private val resolutionFacade: ResolutionFacade
|
||||||
) {
|
) {
|
||||||
@@ -112,9 +112,9 @@ class ParameterNameAndTypeCompletion(
|
|||||||
if (descriptor != null) {
|
if (descriptor != null) {
|
||||||
val parameterType = descriptor.getType()
|
val parameterType = descriptor.getType()
|
||||||
if (parameterType.isVisible(visibilityFilter)) {
|
if (parameterType.isVisible(visibilityFilter)) {
|
||||||
val lookupElement = MyLookupElement.create(name, ArbitraryType(parameterType), lookupElementFactory)
|
val lookupElement = MyLookupElement.create(name, ArbitraryType(parameterType), lookupElementFactory)!!
|
||||||
val count = lookupElementToCount[lookupElement] ?: 0
|
val count = lookupElementToCount[lookupElement] ?: 0
|
||||||
lookupElementToCount[lookupElement!!] = count + 1
|
lookupElementToCount[lookupElement] = count + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,24 +159,24 @@ class ParameterNameAndTypeCompletion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract class Type(private val idString: String) {
|
private abstract class Type(private val idString: String) {
|
||||||
abstract fun createTypeLookupElement(lookupElementFactory: LookupElementFactory): LookupElement?
|
abstract fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory): LookupElement?
|
||||||
|
|
||||||
override fun equals(other: Any?) = other is Type && other.idString == idString
|
override fun equals(other: Any?) = other is Type && other.idString == idString
|
||||||
override fun hashCode() = idString.hashCode()
|
override fun hashCode() = idString.hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) {
|
private class DescriptorType(private val classifier: ClassifierDescriptor) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(classifier)) {
|
||||||
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory)
|
override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory)
|
||||||
= lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false, qualifyNestedClasses = true)
|
= lookupElementFactory.createLookupElement(classifier, qualifyNestedClasses = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) {
|
private class JavaClassType(private val psiClass: PsiClass) : Type(psiClass.getQualifiedName()!!) {
|
||||||
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory)
|
override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory)
|
||||||
= lookupElementFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses = true)
|
= lookupElementFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ArbitraryType(private val type: KotlinType) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)) {
|
private class ArbitraryType(private val type: KotlinType) : Type(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)) {
|
||||||
override fun createTypeLookupElement(lookupElementFactory: LookupElementFactory)
|
override fun createTypeLookupElement(lookupElementFactory: BasicLookupElementFactory)
|
||||||
= lookupElementFactory.createLookupElementForType(type)
|
= lookupElementFactory.createLookupElementForType(type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ class ParameterNameAndTypeCompletion(
|
|||||||
) : LookupElementDecorator<LookupElement>(typeLookupElement) {
|
) : LookupElementDecorator<LookupElement>(typeLookupElement) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun create(parameterName: String, type: Type, factory: LookupElementFactory): LookupElement? {
|
fun create(parameterName: String, type: Type, factory: BasicLookupElementFactory): LookupElement? {
|
||||||
val typeLookupElement = type.createTypeLookupElement(factory) ?: return null
|
val typeLookupElement = type.createTypeLookupElement(factory) ?: return null
|
||||||
val lookupElement = MyLookupElement(parameterName, type, typeLookupElement)
|
val lookupElement = MyLookupElement(parameterName, type, typeLookupElement)
|
||||||
return lookupElement.suppressAutoInsertion()
|
return lookupElement.suppressAutoInsertion()
|
||||||
|
|||||||
+2
@@ -37,6 +37,8 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|||||||
|
|
||||||
object KotlinClassifierInsertHandler : BaseDeclarationInsertHandler() {
|
object KotlinClassifierInsertHandler : BaseDeclarationInsertHandler() {
|
||||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||||
|
surroundWithBracesIfInStringTemplate(context)
|
||||||
|
|
||||||
super.handleInsert(context, item)
|
super.handleInsert(context, item)
|
||||||
|
|
||||||
val file = context.getFile()
|
val file = context.getFile()
|
||||||
|
|||||||
+8
-4
@@ -58,16 +58,20 @@ sealed class KotlinFunctionInsertHandler : KotlinCallableInsertHandler() {
|
|||||||
) = Normal(inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly)
|
) = Normal(inputTypeArguments, inputValueArguments, argumentText, lambdaInfo, argumentsOnly)
|
||||||
|
|
||||||
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
override fun handleInsert(context: InsertionContext, item: LookupElement) {
|
||||||
|
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
||||||
|
val document = context.document
|
||||||
|
|
||||||
if (!argumentsOnly) {
|
if (!argumentsOnly) {
|
||||||
|
surroundWithBracesIfInStringTemplate(context)
|
||||||
|
|
||||||
super.handleInsert(context, item)
|
super.handleInsert(context, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
|
||||||
psiDocumentManager.commitAllDocuments()
|
psiDocumentManager.commitAllDocuments()
|
||||||
psiDocumentManager.doPostponedOperationsAndUnblockDocument(context.document)
|
psiDocumentManager.doPostponedOperationsAndUnblockDocument(document)
|
||||||
|
|
||||||
val startOffset = context.getStartOffset()
|
val startOffset = context.startOffset
|
||||||
val element = context.getFile().findElementAt(startOffset) ?: return
|
val element = context.file.findElementAt(startOffset) ?: return
|
||||||
|
|
||||||
addArguments(context, element)
|
addArguments(context, element)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
@@ -16,8 +16,31 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.completion.handlers
|
package org.jetbrains.kotlin.idea.completion.handlers
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.completion.CompletionInitializationContext
|
||||||
|
import com.intellij.codeInsight.completion.InsertionContext
|
||||||
import com.intellij.openapi.editor.Document
|
import com.intellij.openapi.editor.Document
|
||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import com.intellij.psi.PsiDocumentManager
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
|
||||||
|
fun surroundWithBracesIfInStringTemplate(context: InsertionContext) {
|
||||||
|
val startOffset = context.startOffset
|
||||||
|
val document = context.document
|
||||||
|
if (startOffset > 0 && document.charsSequence[startOffset - 1] == '$') {
|
||||||
|
val psiDocumentManager = PsiDocumentManager.getInstance(context.project)
|
||||||
|
psiDocumentManager.commitAllDocuments()
|
||||||
|
psiDocumentManager.doPostponedOperationsAndUnblockDocument(document)
|
||||||
|
|
||||||
|
if (context.file.findElementAt(startOffset - 1)?.node?.elementType == KtTokens.SHORT_TEMPLATE_ENTRY_START) {
|
||||||
|
document.insertString(startOffset, "{")
|
||||||
|
context.offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, startOffset + 1)
|
||||||
|
|
||||||
|
val tailOffset = context.tailOffset
|
||||||
|
document.insertString(tailOffset, "}")
|
||||||
|
context.tailOffset = tailOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? {
|
fun CharSequence.indexOfSkippingSpace(c: Char, startIndex: Int): Int? {
|
||||||
for (i in startIndex..this.length() - 1) {
|
for (i in startIndex..this.length() - 1) {
|
||||||
|
|||||||
+2
-2
@@ -22,8 +22,8 @@ import com.intellij.codeInsight.lookup.LookupElementDecorator
|
|||||||
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
import com.intellij.codeInsight.lookup.LookupElementPresentation
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory
|
||||||
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
import org.jetbrains.kotlin.idea.completion.ExpectedInfo
|
||||||
import org.jetbrains.kotlin.idea.completion.LookupElementFactory
|
|
||||||
import org.jetbrains.kotlin.idea.completion.createLookupElementForType
|
import org.jetbrains.kotlin.idea.completion.createLookupElementForType
|
||||||
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
import org.jetbrains.kotlin.idea.completion.fuzzyType
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
@@ -36,7 +36,7 @@ object ClassLiteralItems {
|
|||||||
public fun addToCollection(
|
public fun addToCollection(
|
||||||
collection: MutableCollection<LookupElement>,
|
collection: MutableCollection<LookupElement>,
|
||||||
expectedInfos: Collection<ExpectedInfo>,
|
expectedInfos: Collection<ExpectedInfo>,
|
||||||
lookupElementFactory: LookupElementFactory,
|
lookupElementFactory: BasicLookupElementFactory,
|
||||||
isJvmModule: Boolean
|
isJvmModule: Boolean
|
||||||
) {
|
) {
|
||||||
val typeAndSuffixToExpectedInfos = LinkedHashMap<Pair<KotlinType, String>, MutableList<ExpectedInfo>>()
|
val typeAndSuffixToExpectedInfos = LinkedHashMap<Pair<KotlinType, String>, MutableList<ExpectedInfo>>()
|
||||||
|
|||||||
+3
-3
@@ -173,7 +173,7 @@ class SmartCompletion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun additionalItemsNoPostProcess(lookupElementFactory: LookupElementFactory): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> {
|
private fun additionalItemsNoPostProcess(lookupElementFactory: LookupElementFactory): Pair<Collection<LookupElement>, InheritanceItemsSearcher?> {
|
||||||
val asTypePositionItems = buildForAsTypePosition(lookupElementFactory)
|
val asTypePositionItems = buildForAsTypePosition(lookupElementFactory.basicFactory)
|
||||||
if (asTypePositionItems != null) {
|
if (asTypePositionItems != null) {
|
||||||
assert(expectedInfos.isEmpty())
|
assert(expectedInfos.isEmpty())
|
||||||
return Pair(asTypePositionItems, null)
|
return Pair(asTypePositionItems, null)
|
||||||
@@ -209,7 +209,7 @@ class SmartCompletion(
|
|||||||
.addToCollection(items, expectedInfos, expression, descriptorsToSkip)
|
.addToCollection(items, expectedInfos, expression, descriptorsToSkip)
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory, isJvmModule)
|
ClassLiteralItems.addToCollection(items, expectedInfos, lookupElementFactory.basicFactory, isJvmModule)
|
||||||
|
|
||||||
if (!forBasicCompletion) {
|
if (!forBasicCompletion) {
|
||||||
LambdaItems.addToCollection(items, expectedInfos)
|
LambdaItems.addToCollection(items, expectedInfos)
|
||||||
@@ -368,7 +368,7 @@ class SmartCompletion(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildForAsTypePosition(lookupElementFactory: LookupElementFactory): Collection<LookupElement>? {
|
private fun buildForAsTypePosition(lookupElementFactory: BasicLookupElementFactory): Collection<LookupElement>? {
|
||||||
val binaryExpression = ((expression.getParent() as? KtUserType)
|
val binaryExpression = ((expression.getParent() as? KtUserType)
|
||||||
?.getParent() as? KtTypeReference)
|
?.getParent() as? KtTypeReference)
|
||||||
?.getParent() as? KtBinaryExpressionWithTypeRHS
|
?.getParent() as? KtBinaryExpressionWithTypeRHS
|
||||||
|
|||||||
+52
-29
@@ -64,45 +64,68 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression != null) {
|
if (expression == null) return
|
||||||
addFunctionLiteralArgumentCompletions()
|
|
||||||
|
|
||||||
val (additionalItems, inheritanceSearcher) = smartCompletion!!.additionalItems(lookupElementFactory)
|
addFunctionLiteralArgumentCompletions()
|
||||||
collector.addElements(additionalItems)
|
|
||||||
|
|
||||||
if (nameExpression != null) {
|
var inheritanceSearcher: InheritanceItemsSearcher? = null
|
||||||
val filter = smartCompletion!!.descriptorFilter
|
val contextVariableTypesForAdditionalItems = withCollectRequiredContextVariableTypes { lookupElementFactory ->
|
||||||
if (filter != null) {
|
val pair = smartCompletion!!.additionalItems(lookupElementFactory)
|
||||||
val (imported, notImported) = referenceVariants!!
|
collector.addElements(pair.first)
|
||||||
imported.forEach { collector.addElements(filter(it, lookupElementFactory)) }
|
inheritanceSearcher = pair.second
|
||||||
notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
|
}
|
||||||
|
|
||||||
|
val filter = smartCompletion!!.descriptorFilter
|
||||||
|
var contextVariableTypesForReferenceVariants = filter?.let {
|
||||||
|
withCollectRequiredContextVariableTypes { lookupElementFactory ->
|
||||||
|
val (imported, notImported) = referenceVariants ?: return@withCollectRequiredContextVariableTypes
|
||||||
|
imported.forEach { collector.addElements(filter(it, lookupElementFactory)) }
|
||||||
|
notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flushToResultSet()
|
||||||
|
|
||||||
|
val contextVariablesProvider = RealContextVariablesProvider(referenceVariantsHelper, position)
|
||||||
|
withContextVariablesProvider(contextVariablesProvider) { lookupElementFactory ->
|
||||||
|
if (contextVariableTypesForAdditionalItems.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
|
||||||
|
val additionalItems = smartCompletion!!.additionalItems(lookupElementFactory).first
|
||||||
|
collector.addElements(additionalItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filter != null && contextVariableTypesForReferenceVariants!!.any { contextVariablesProvider.functionTypeVariables(it).isNotEmpty() }) {
|
||||||
|
val (imported, notImported) = referenceVariantsWithSingleFunctionTypeParameter()!!
|
||||||
|
imported.forEach { collector.addElements(filter(it, lookupElementFactory)) }
|
||||||
|
notImported.forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
flushToResultSet()
|
||||||
|
|
||||||
|
if (filter != null) {
|
||||||
|
if (shouldCompleteTopLevelCallablesFromIndex()) {
|
||||||
|
getTopLevelCallables().forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
|
||||||
flushToResultSet()
|
flushToResultSet()
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldCompleteTopLevelCallablesFromIndex()) {
|
if (position.getContainingFile() is KtCodeFragment) {
|
||||||
getTopLevelCallables().forEach { collector.addElements(filter(it, lookupElementFactory), notImported = true) }
|
val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants(lookupElementFactory)
|
||||||
|
if (variantsAndFactory != null) {
|
||||||
|
val variants = variantsAndFactory.first
|
||||||
|
@Suppress("NAME_SHADOWING") val lookupElementFactory = variantsAndFactory.second
|
||||||
|
variants.imported.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }) }
|
||||||
|
variants.notImportedExtensions.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }, notImported = true) }
|
||||||
flushToResultSet()
|
flushToResultSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position.getContainingFile() is KtCodeFragment) {
|
|
||||||
val variantsAndFactory = getRuntimeReceiverTypeReferenceVariants()
|
|
||||||
if (variantsAndFactory != null) {
|
|
||||||
val variants = variantsAndFactory.first
|
|
||||||
val lookupElementFactory = variantsAndFactory.second
|
|
||||||
variants.imported.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }) }
|
|
||||||
variants.notImportedExtensions.forEach { collector.addElements(filter(it, lookupElementFactory).map { it.withReceiverCast() }, notImported = true) }
|
|
||||||
flushToResultSet()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// it makes no sense to search inheritors if there is no reference because it means that we have prefix like "this@"
|
|
||||||
inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) {
|
|
||||||
collector.addElement(it)
|
|
||||||
flushToResultSet()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// it makes no sense to search inheritors if there is no reference because it means that we have prefix like "this@"
|
||||||
|
inheritanceSearcher?.search({ prefixMatcher.prefixMatches(it) }) {
|
||||||
|
collector.addElement(it)
|
||||||
|
flushToResultSet()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// special completion for outside parenthesis lambda argument
|
// special completion for outside parenthesis lambda argument
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
class C {
|
||||||
|
companion object {
|
||||||
|
fun create(p: (Int) -> Unit) = C()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val handler: (Int) -> Unit = {}
|
||||||
|
|
||||||
|
fun v: C = cr<caret>
|
||||||
|
|
||||||
|
// EXIST: { allLookupStrings: "C, create", itemText: "C.create", tailText: " {...} (p: (Int) -> Unit) (<root>)", typeText:"C" }
|
||||||
|
// EXIST: { allLookupStrings: "C, create", itemText: "C.create", tailText: "(handler) (<root>)", typeText:"C" }
|
||||||
+16
-4
@@ -1,6 +1,18 @@
|
|||||||
fun foo(p: (String, Char) -> Unit): String {}
|
class C {
|
||||||
|
companion object {
|
||||||
|
fun foo(p: (Int) -> Unit) = C()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun v: String = fo<caret>
|
fun foo(p: (String, Char) -> Unit): C {}
|
||||||
|
|
||||||
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) (<root>)", typeText:"String" }
|
val handler1: (String, Char) -> Unit = {}
|
||||||
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) (<root>)", typeText:"String" }
|
val handler2: (Int) -> Unit = {}
|
||||||
|
|
||||||
|
fun v: C = fo<caret>
|
||||||
|
|
||||||
|
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(p: (String, Char) -> Unit) (<root>)", typeText:"C" }
|
||||||
|
// EXIST: { lookupString:"foo", itemText: "foo", tailText: " { String, Char -> ... } (p: (String, Char) -> Unit) (<root>)", typeText:"C" }
|
||||||
|
// EXIST: { lookupString:"foo", itemText: "foo", tailText: "(handler1) (<root>)", typeText:"C" }
|
||||||
|
// EXIST: { allLookupStrings: "C, foo", itemText: "C.foo", tailText: " {...} (p: (Int) -> Unit) (<root>)", typeText:"C" }
|
||||||
|
// EXIST: { allLookupStrings: "C, foo", itemText: "C.foo", tailText: "(handler2) (<root>)", typeText:"C" }
|
||||||
|
|||||||
+6
@@ -1389,6 +1389,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ContextVariables3.kt")
|
||||||
|
public void testContextVariables3() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ContextVariablesFilter.kt")
|
@TestMetadata("ContextVariablesFilter.kt")
|
||||||
public void testContextVariablesFilter() throws Exception {
|
public void testContextVariablesFilter() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");
|
||||||
|
|||||||
+6
@@ -1389,6 +1389,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("ContextVariables3.kt")
|
||||||
|
public void testContextVariables3() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariables3.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("ContextVariablesFilter.kt")
|
@TestMetadata("ContextVariablesFilter.kt")
|
||||||
public void testContextVariablesFilter() throws Exception {
|
public void testContextVariablesFilter() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/highOrderFunctions/ContextVariablesFilter.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user