Complete type instantiation items after "by"

This commit is contained in:
Valentin Kipyatkov
2016-03-25 12:57:42 +03:00
parent ba6accd5f9
commit 0edc5c15e2
18 changed files with 153 additions and 50 deletions
@@ -57,9 +57,9 @@ import java.util.*
class BasicCompletionSession( class BasicCompletionSession(
configuration: CompletionSessionConfiguration, configuration: CompletionSessionConfiguration,
parameters: CompletionParameters, parameters: CompletionParameters,
private val toFromOriginalFileMapper: ToFromOriginalFileMapper, toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet resultSet: CompletionResultSet
) : CompletionSession(configuration, parameters, resultSet) { ) : CompletionSession(configuration, parameters, toFromOriginalFileMapper, resultSet) {
private interface CompletionKind { private interface CompletionKind {
val descriptorKindFilter: DescriptorKindFilter? val descriptorKindFilter: DescriptorKindFilter?
@@ -197,11 +197,11 @@ class BasicCompletionSession(
// getting root packages from scope is very slow so we do this in alternative way // getting root packages from scope is very slow so we do this in alternative way
if (callTypeAndReceiver.receiver == null && callTypeAndReceiver.callType.descriptorKindFilter.kindMask.and(DescriptorKindFilter.PACKAGES_MASK) != 0) { if (callTypeAndReceiver.receiver == null && callTypeAndReceiver.callType.descriptorKindFilter.kindMask.and(DescriptorKindFilter.PACKAGES_MASK) != 0) {
//TODO: move this code somewhere else? //TODO: move this code somewhere else?
val packageNames = PackageIndexUtil.getSubPackageFqNames(FqName.ROOT, originalSearchScope, project, prefixMatcher.asNameFilter()) val packageNames = PackageIndexUtil.getSubPackageFqNames(FqName.ROOT, searchScope, project, prefixMatcher.asNameFilter())
.toMutableSet() .toMutableSet()
if (!ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as KtFile)) { if (!ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as KtFile)) {
JavaPsiFacade.getInstance(project).findPackage("")?.getSubPackages(originalSearchScope)?.forEach { psiPackage -> JavaPsiFacade.getInstance(project).findPackage("")?.getSubPackages(searchScope)?.forEach { psiPackage ->
val name = psiPackage.name val name = psiPackage.name
if (Name.isValidIdentifier(name!!)) { if (Name.isValidIdentifier(name!!)) {
packageNames.add(FqName(name)) packageNames.add(FqName(name))
@@ -22,9 +22,7 @@ import com.intellij.codeInsight.completion.CompletionSorter
import com.intellij.codeInsight.completion.CompletionUtil import com.intellij.codeInsight.completion.CompletionUtil
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher import com.intellij.codeInsight.completion.impl.CamelHumpMatcher
import com.intellij.codeInsight.lookup.LookupElement import com.intellij.codeInsight.lookup.LookupElement
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.patterns.StandardPatterns import com.intellij.patterns.StandardPatterns
import com.intellij.psi.search.DelegatingGlobalSearchScope
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.* import org.jetbrains.kotlin.idea.caches.resolve.*
@@ -70,6 +68,7 @@ fun CompletionSessionConfiguration(parameters: CompletionParameters) = Completio
abstract class CompletionSession( abstract class CompletionSession(
protected val configuration: CompletionSessionConfiguration, protected val configuration: CompletionSessionConfiguration,
protected val parameters: CompletionParameters, protected val parameters: CompletionParameters,
protected val toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet resultSet: CompletionResultSet
) { ) {
protected val position = parameters.position protected val position = parameters.position
@@ -148,16 +147,15 @@ abstract class CompletionSession(
LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter()) LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter())
} }
protected val originalSearchScope: GlobalSearchScope = getResolveScope(parameters.getOriginalFile() as KtFile) protected val searchScope: GlobalSearchScope = getResolveScope(parameters.originalFile as KtFile)
// we need to exclude the original file from scope because our resolve session is built with this file replaced by synthetic one
protected val searchScope: GlobalSearchScope = object : DelegatingGlobalSearchScope(originalSearchScope) {
override fun contains(file: VirtualFile) = super.contains(file) && file != parameters.originalFile.virtualFile
}
protected fun indicesHelper(mayIncludeInaccessible: Boolean): KotlinIndicesHelper { protected fun indicesHelper(mayIncludeInaccessible: Boolean): KotlinIndicesHelper {
val filter = if (mayIncludeInaccessible) isVisibleFilter else isVisibleFilterCheckAlways val filter = if (mayIncludeInaccessible) isVisibleFilter else isVisibleFilterCheckAlways
return KotlinIndicesHelper(resolutionFacade, searchScope, filter, filterOutPrivate = !mayIncludeInaccessible) return KotlinIndicesHelper(resolutionFacade,
searchScope,
filter,
filterOutPrivate = !mayIncludeInaccessible,
declarationTranslator = { toFromOriginalFileMapper.toSyntheticFile(it) })
} }
// excludes top-level extensions except for ones declared in the current file - those that are fetched from indices // excludes top-level extensions except for ones declared in the current file - those that are fetched from indices
@@ -59,12 +59,15 @@ class KDocCompletionContributor(): CompletionContributor() {
object KDocNameCompletionProvider: CompletionProvider<CompletionParameters>() { object KDocNameCompletionProvider: CompletionProvider<CompletionParameters>() {
override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) {
KDocNameCompletionSession(parameters, result).complete() KDocNameCompletionSession(parameters, ToFromOriginalFileMapper.create(parameters), result).complete()
} }
} }
class KDocNameCompletionSession(parameters: CompletionParameters, class KDocNameCompletionSession(
resultSet: CompletionResultSet): CompletionSession(CompletionSessionConfiguration(parameters), parameters, resultSet) { parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet
): CompletionSession(CompletionSessionConfiguration(parameters), parameters, toFromOriginalFileMapper, resultSet) {
override val descriptorKindFilter: DescriptorKindFilter? get() = null override val descriptorKindFilter: DescriptorKindFilter? get() = null
override val expectedInfos: Collection<ExpectedInfo> get() = emptyList() override val expectedInfos: Collection<ExpectedInfo> get() = emptyList()
@@ -85,7 +88,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters,
val section = position.getContainingSection() val section = position.getContainingSection()
val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet() val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet()
val descriptors = getParamDescriptors(declarationDescriptor) val descriptors = getParamDescriptors(declarationDescriptor)
.filter { it.getName().asString() !in documentedParameters } .filter { it.name.asString() !in documentedParameters }
descriptors.forEach { descriptors.forEach {
collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)) collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true))
@@ -240,11 +240,10 @@ class KotlinCompletionContributor : CompletionContributor() {
private fun performCompletion(parameters: CompletionParameters, result: CompletionResultSet) { private fun performCompletion(parameters: CompletionParameters, result: CompletionResultSet) {
val position = parameters.position val position = parameters.position
val positionFile = position.containingFile as? KtFile ?: return if (position.containingFile !is KtFile) return
val originalFile = parameters.originalFile as KtFile if ((parameters.originalFile as KtFile).doNotComplete ?: false) return
if (originalFile.doNotComplete ?: false) return
val toFromOriginalFileMapper = ToFromOriginalFileMapper(originalFile, positionFile, parameters.offset) val toFromOriginalFileMapper = ToFromOriginalFileMapper.create(parameters)
if (position.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_START) { if (position.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_START) {
val expression = (position.parent as KtBlockStringTemplateEntry).expression val expression = (position.parent as KtBlockStringTemplateEntry).expression
@@ -16,16 +16,25 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.CompletionParameters
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
class ToFromOriginalFileMapper( class ToFromOriginalFileMapper private constructor(
val originalFile: KtFile, val originalFile: KtFile,
val syntheticFile: KtFile, val syntheticFile: KtFile,
val completionOffset: Int val completionOffset: Int
) { ) {
companion object {
fun create(parameters: CompletionParameters): ToFromOriginalFileMapper {
val originalFile = parameters.originalFile as KtFile
val syntheticFile = parameters.position.containingFile as KtFile
return ToFromOriginalFileMapper(originalFile, syntheticFile, parameters.offset)
}
}
private val syntheticLength: Int private val syntheticLength: Int
private val originalLength: Int private val originalLength: Int
private val tailLength: Int private val tailLength: Int
@@ -41,7 +41,7 @@ class SmartCompletionSession(
parameters: CompletionParameters, parameters: CompletionParameters,
toFromOriginalFileMapper: ToFromOriginalFileMapper, toFromOriginalFileMapper: ToFromOriginalFileMapper,
resultSet: CompletionResultSet resultSet: CompletionResultSet
) : CompletionSession(configuration, parameters, resultSet) { ) : CompletionSession(configuration, parameters, toFromOriginalFileMapper, resultSet) {
override val descriptorKindFilter: DescriptorKindFilter by lazy { override val descriptorKindFilter: DescriptorKindFilter by lazy {
// we do not include SAM-constructors because they are handled separately and adding them requires iterating of java classes // we do not include SAM-constructors because they are handled separately and adding them requires iterating of java classes
@@ -60,7 +60,7 @@ class SmartCompletionSession(
private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) { private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) {
expression?.let { expression?.let {
SmartCompletion(it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, indicesHelper(false), SmartCompletion(it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, indicesHelper(false),
prefixMatcher, originalSearchScope, toFromOriginalFileMapper, prefixMatcher, searchScope, toFromOriginalFileMapper,
callTypeAndReceiver, isJvmModule) callTypeAndReceiver, isJvmModule)
} }
} }
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.core.ExpectedInfo import org.jetbrains.kotlin.idea.core.ExpectedInfo
import org.jetbrains.kotlin.idea.core.Tail import org.jetbrains.kotlin.idea.core.Tail
import org.jetbrains.kotlin.idea.core.fuzzyType import org.jetbrains.kotlin.idea.core.multipleFuzzyTypes
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.FuzzyType
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
class TypeInstantiationItems( class TypeInstantiationItems(
val resolutionFacade: ResolutionFacade, val resolutionFacade: ResolutionFacade,
@@ -67,9 +68,14 @@ class TypeInstantiationItems(
inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>, inheritanceSearchers: MutableCollection<InheritanceItemsSearcher>,
expectedInfos: Collection<ExpectedInfo> expectedInfos: Collection<ExpectedInfo>
) { ) {
val expectedInfosGrouped: Map<FuzzyType?, List<ExpectedInfo>> = expectedInfos.groupBy { it.fuzzyType?.makeNotNullable() } val expectedInfosGrouped = LinkedHashMap<FuzzyType, MutableList<ExpectedInfo>>()
for (expectedInfo in expectedInfos) {
for (fuzzyType in expectedInfo.multipleFuzzyTypes) {
expectedInfosGrouped.getOrPut(fuzzyType.makeNotNullable()) { ArrayList() }.add(expectedInfo)
}
}
for ((type, infos) in expectedInfosGrouped) { for ((type, infos) in expectedInfosGrouped) {
if (type == null) continue
val tail = mergeTails(infos.map { it.tail }) val tail = mergeTails(infos.map { it.tail })
addTo(items, inheritanceSearchers, type, tail) addTo(items, inheritanceSearchers, type, tail)
} }
@@ -1,15 +1,16 @@
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
class Property<TOwner, TValue> class Property<TOwner, TValue>(owner: TOwner, value: TValue)
operator fun <TValue, TOwner> Property<TOwner, TValue>.getValue(thisRef: TOwner, property: KProperty<*>): TValue { operator fun <TValue, TOwner> Property<TOwner, TValue>.getValue(thisRef: TOwner, property: KProperty<*>): TValue {
throw Exception() throw Exception()
} }
fun<TOwner, TValue> createProperty(): Property<TOwner, TValue> = Property() fun<TOwner, TValue> createProperty(owner: TOwner, value: TValue): Property<TOwner, TValue> = Property(owner, value)
class C { class C {
val v by create<caret> val v by <caret>
} }
// EXIST: { itemText: "createProperty", typeText: "Property<C, TValue>" } // EXIST: { itemText: "createProperty", typeText: "Property<C, TValue>" }
// EXIST: Property
@@ -9,7 +9,8 @@ operator fun <TValue, TOwner> Property<TOwner, TValue>.getValue(thisRef: TOwner,
fun<TOwner, TValue> createProperty(): Property<TOwner, TValue> = Property() fun<TOwner, TValue> createProperty(): Property<TOwner, TValue> = Property()
class C { class C {
val v: Int by create<caret> val v: Int by <caret>
} }
// EXIST: { itemText: "createProperty", typeText: "Property<C, Int>" } // EXIST: { itemText: "createProperty", typeText: "Property<C, Int>" }
// EXIST: Property
@@ -13,7 +13,8 @@ operator fun <TValue3, TOwner3> Property<TOwner3, TValue3>.setValue(thisRef: TOw
fun<TOwner4, TValue4> createProperty(): Property<TOwner4, TValue4> = Property() fun<TOwner4, TValue4> createProperty(): Property<TOwner4, TValue4> = Property()
class C { class C {
var v by create<caret> var v by <caret>
} }
// EXIST: { itemText: "createProperty", typeText: "Property<C, TValue4>" } // EXIST: { itemText: "createProperty", typeText: "Property<C, TValue4>" }
// EXIST: Property
@@ -13,7 +13,8 @@ operator fun <TValue3, TOwner3> Property<TOwner3, TValue3>.setValue(thisRef: TOw
fun<TOwner4, TValue4> createProperty(): Property<TOwner4, TValue4> = Property() fun<TOwner4, TValue4> createProperty(): Property<TOwner4, TValue4> = Property()
class C { class C {
var v: Int by create<caret> var v: Int by <caret>
} }
// EXIST: { itemText: "createProperty", typeText: "Property<C, Int>" } // EXIST: { itemText: "createProperty", typeText: "Property<C, Int>" }
// EXIST: Property
@@ -30,10 +30,17 @@ class C
val C.property by <caret> val C.property by <caret>
// EXIST: lazy // EXIST: lazy
// EXIST: createX1 // EXIST: createX1
// ABSENT: createX2 // ABSENT: createX2
// EXIST: createX3 // EXIST: createX3
// EXIST: createY1 // EXIST: createY1
// ABSENT: createY2 // ABSENT: createY2
// EXIST: createY3 // EXIST: createY3
/*TODO: add constructors*/
// EXIST: X1
// ABSENT: X2
// EXIST: X3
// EXIST: Y1
// ABSENT: Y2
// EXIST: Y3
@@ -35,8 +35,15 @@ class C
var C.property by <caret> var C.property by <caret>
// ABSENT: lazy // ABSENT: lazy
// EXIST: createX1 // EXIST: createX1
// ABSENT: createX2 // ABSENT: createX2
// ABSENT: createX3 // ABSENT: createX3
// EXIST: createX4 // EXIST: createX4
// ABSENT: createX5 // ABSENT: createX5
// EXIST: X1
// ABSENT: X2
// ABSENT: X3
// EXIST: X4
// ABSENT: X5
@@ -12,7 +12,7 @@ class X3 {
class Y1 class Y1
class Y2 class Y2
class Y3 abstract class Y3
operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = "" operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = ""
operator fun Y2.getValue(thisRef: String, property: KProperty<*>): String = "" operator fun Y2.getValue(thisRef: String, property: KProperty<*>): String = ""
@@ -36,4 +36,9 @@ class C {
// EXIST: createY1 // EXIST: createY1
// ABSENT: createY2 // ABSENT: createY2
// EXIST: createY3 // EXIST: createY3
/*TODO: add constructors*/ // EXIST: X1
// ABSENT: X2
// EXIST: X3
// EXIST: Y1
// ABSENT: Y2
// ABSENT: Y3
@@ -24,6 +24,13 @@ class X5 {
operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {} operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {}
} }
class Y1
class Y2
operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = ""
operator fun Y1.setValue(thisRef: C, property: KProperty<*>, value: String) {}
operator fun Y2.getValue(thisRef: C, property: KProperty<*>): String = ""
fun createX1() = X1() fun createX1() = X1()
fun createX2() = X2() fun createX2() = X2()
fun createX3() = X3() fun createX3() = X3()
@@ -40,3 +47,10 @@ class C {
// ABSENT: createX3 // ABSENT: createX3
// EXIST: createX4 // EXIST: createX4
// ABSENT: createX5 // ABSENT: createX5
// EXIST: X1
// ABSENT: X2
// ABSENT: X3
// EXIST: X4
// ABSENT: X5
// EXIST: Y1
// ABSENT: Y2
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.resolve.ideService import org.jetbrains.kotlin.idea.resolve.ideService
import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.FuzzyType
import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType
import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
@@ -63,6 +64,12 @@ data class ItemOptions(val starPrefix: Boolean) {
interface ByTypeFilter { interface ByTypeFilter {
fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor?
val fuzzyType: FuzzyType?
get() = null
val multipleFuzzyTypes: Collection<FuzzyType>
get() = fuzzyType.singletonOrEmptyList()
object All : ByTypeFilter { object All : ByTypeFilter {
override fun matchingSubstitutor(descriptorType: FuzzyType) = TypeSubstitutor.EMPTY override fun matchingSubstitutor(descriptorType: FuzzyType) = TypeSubstitutor.EMPTY
} }
@@ -72,7 +79,7 @@ interface ByTypeFilter {
} }
} }
class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter { class ByExpectedTypeFilter(override val fuzzyType: FuzzyType) : ByTypeFilter {
override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType) override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType)
override fun equals(other: Any?) = other is ByExpectedTypeFilter && fuzzyType == other.fuzzyType override fun equals(other: Any?) = other is ByExpectedTypeFilter && fuzzyType == other.fuzzyType
@@ -118,7 +125,10 @@ class ExpectedInfo(
} }
val ExpectedInfo.fuzzyType: FuzzyType? val ExpectedInfo.fuzzyType: FuzzyType?
get() = (this.filter as? ByExpectedTypeFilter)?.fuzzyType get() = filter.fuzzyType
val ExpectedInfo.multipleFuzzyTypes: Collection<FuzzyType>
get() = filter.multipleFuzzyTypes
sealed class ArgumentPositionData(val function: FunctionDescriptor, val callType: Call.CallType) : ExpectedInfo.AdditionalData { sealed class ArgumentPositionData(val function: FunctionDescriptor, val callType: Call.CallType) : ExpectedInfo.AdditionalData {
class Positional( class Positional(
@@ -632,6 +642,25 @@ class ExpectedInfos(
TypeSubstitutor.createChainedSubstitutor(setOperatorSubstitutor.substitution, TypeSubstitutor.createChainedSubstitutor(setOperatorSubstitutor.substitution,
setParamTypeSubstitutor.substitution).substitution) setParamTypeSubstitutor.substitution).substitution)
} }
override val multipleFuzzyTypes: Collection<FuzzyType> by lazy {
val result = ArrayList<FuzzyType>()
for (classDescriptor in typesWithGetDetector.classesWithMemberOperators) {
val type = classDescriptor.defaultType
val typeParameters = classDescriptor.declaredTypeParameters
val substitutor = matchingSubstitutor(FuzzyType(type, typeParameters)) ?: continue
result.add(FuzzyType(substitutor.substitute(type, Variance.INVARIANT)!!, typeParameters))
}
for (extensionOperator in typesWithGetDetector.extensionOperators) {
val receiverType = extensionOperator.fuzzyExtensionReceiverType()!!
val substitutor = matchingSubstitutor(receiverType) ?: continue
result.add(FuzzyType(substitutor.substitute(receiverType.type, Variance.INVARIANT)!!, receiverType.freeParameters))
}
result
}
} }
return listOf(ExpectedInfo(byTypeFilter, null, null)) return listOf(ExpectedInfo(byTypeFilter, null, null))
//TODO: special items for "Delegates...." //TODO: special items for "Delegates...."
@@ -42,10 +42,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.isHiddenInResolution import org.jetbrains.kotlin.resolve.isHiddenInResolution
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
@@ -59,6 +56,7 @@ class KotlinIndicesHelper(
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val scope: GlobalSearchScope, private val scope: GlobalSearchScope,
visibilityFilter: (DeclarationDescriptor) -> Boolean, visibilityFilter: (DeclarationDescriptor) -> Boolean,
private val declarationTranslator: (KtDeclaration) -> KtDeclaration? = { it },
applyExcludeSettings: Boolean = true, applyExcludeSettings: Boolean = true,
private val filterOutPrivate: Boolean = true private val filterOutPrivate: Boolean = true
) { ) {
@@ -94,7 +92,16 @@ class KotlinIndicesHelper(
.filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } .filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
.flatMap { it.resolveToDescriptorsWithHack() } .flatMap { it.resolveToDescriptorsWithHack() }
.filterIsInstance<FunctionDescriptor>() .filterIsInstance<FunctionDescriptor>()
.filter { descriptorFilter(it) } .filter { descriptorFilter(it) && it.extensionReceiverParameter != null }
.distinct()
}
fun getMemberOperatorsByName(name: String): Collection<FunctionDescriptor> {
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
.filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
.flatMap { it.resolveToDescriptorsWithHack() }
.filterIsInstance<FunctionDescriptor>()
.filter { descriptorFilter(it) && it.extensionReceiverParameter == null }
.distinct() .distinct()
} }
@@ -316,7 +323,8 @@ class KotlinIndicesHelper(
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<CallableDescriptor>() return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<CallableDescriptor>()
} }
else { else {
return (resolutionFacade.resolveToDescriptor(this) as? CallableDescriptor).singletonOrEmptyList() val translatedDeclaration = declarationTranslator(this) ?: return emptyList()
return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? CallableDescriptor).singletonOrEmptyList()
} }
} }
} }
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.idea.core package org.jetbrains.kotlin.idea.core
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.FuzzyType
@@ -41,19 +42,32 @@ abstract class TypesWithOperatorDetector(
private val cache = HashMap<FuzzyType, Pair<FunctionDescriptor, TypeSubstitutor>?>() private val cache = HashMap<FuzzyType, Pair<FunctionDescriptor, TypeSubstitutor>?>()
private val extensionOperators: Collection<FunctionDescriptor> by lazy { val extensionOperators: Collection<FunctionDescriptor> by lazy {
val result = ArrayList<FunctionDescriptor>() val result = ArrayList<FunctionDescriptor>()
collectExtensionOperators(scope.collectFunctions(name, NoLookupLocation.FROM_IDE), result)
indicesHelper?.getTopLevelExtensionOperatorsByName(name.asString())?.let { collectExtensionOperators(it, result) } val extensionsFromScope = scope
.collectFunctions(name, NoLookupLocation.FROM_IDE)
.filter { it.extensionReceiverParameter != null }
result.addSuitableOperators(extensionsFromScope)
indicesHelper?.getTopLevelExtensionOperatorsByName(name.asString())?.let { result.addSuitableOperators(it) }
result.distinctBy { it.original } result.distinctBy { it.original }
} }
private fun collectExtensionOperators(functions: Collection<FunctionDescriptor>, result: MutableCollection<FunctionDescriptor>) { val classesWithMemberOperators: Collection<ClassDescriptor> by lazy {
if (indicesHelper == null) return@lazy emptyList<ClassDescriptor>()
val operators = ArrayList<FunctionDescriptor>().addSuitableOperators(indicesHelper.getMemberOperatorsByName(name.asString()))
operators.map { it.containingDeclaration as ClassDescriptor }.distinct()
}
private fun MutableCollection<FunctionDescriptor>.addSuitableOperators(functions: Collection<FunctionDescriptor>): MutableCollection<FunctionDescriptor> {
for (function in functions) { for (function in functions) {
if (function.extensionReceiverParameter == null || !function.isValidOperator()) continue if (!function.isValidOperator()) continue
val substitutor = checkIsSuitableByType(function, function.typeParameters) ?: continue val substitutor = checkIsSuitableByType(function, function.typeParameters) ?: continue
result.add(function.substitute(substitutor)) add(function.substitute(substitutor))
} }
return this
} }
fun findOperator(type: FuzzyType): Pair<FunctionDescriptor, TypeSubstitutor>? { fun findOperator(type: FuzzyType): Pair<FunctionDescriptor, TypeSubstitutor>? {