Split LookupElementFactory into 3 classes

This commit is contained in:
Valentin Kipyatkov
2015-08-26 18:31:41 +03:00
parent cff817d03b
commit 6dde70e358
7 changed files with 391 additions and 327 deletions
@@ -0,0 +1,261 @@
/*
* 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.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.completion.handlers.BaseDeclarationInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.KotlinClassifierInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.synthetic.SamAdapterExtensionFunctionDescriptor
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
class BasicLookupElementFactory(
private val project: Project,
private val insertHandlerProvider: InsertHandlerProvider
) {
public fun createLookupElement(
descriptor: DeclarationDescriptor,
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true
): LookupElement {
val _descriptor = if (descriptor is CallableMemberDescriptor)
DescriptorUtils.unwrapFakeOverride(descriptor)
else
descriptor
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, _descriptor)
return createLookupElement(_descriptor, declaration, qualifyNestedClasses, includeClassTypeArguments)
}
public fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement {
val lookupObject = object : DeclarationLookupObjectImpl(null, psiClass) {
override fun getIcon(flags: Int) = psiClass.getIcon(flags)
}
var element = LookupElementBuilder.create(lookupObject, psiClass.getName()!!)
.withInsertHandler(KotlinClassifierInsertHandler)
val typeParams = psiClass.getTypeParameters()
if (includeClassTypeArguments && typeParams.isNotEmpty()) {
element = element.appendTailText(typeParams.map { it.getName() }.joinToString(", ", "<", ">"), true)
}
val qualifiedName = psiClass.getQualifiedName()!!
var containerName = qualifiedName.substringBeforeLast('.', FqName.ROOT.toString())
if (qualifyNestedClasses) {
val nestLevel = psiClass.parents.takeWhile { it is PsiClass }.count()
if (nestLevel > 0) {
var itemText = psiClass.getName()
for (i in 1..nestLevel) {
val outerClassName = containerName.substringAfterLast('.')
element = element.withLookupString(outerClassName)
itemText = outerClassName + "." + itemText
containerName = containerName.substringBeforeLast('.', FqName.ROOT.toString())
}
element = element.withPresentableText(itemText!!)
}
}
element = element.appendTailText(" ($containerName)", true)
if (lookupObject.isDeprecated) {
element = element.withStrikeoutness(true)
}
return element.withIconFromLookupObject()
}
public fun createLookupElementForPackage(name: FqName): LookupElement {
var element = LookupElementBuilder.create(PackageLookupObject(name), name.shortName().asString())
element = element.withInsertHandler(BaseDeclarationInsertHandler())
if (!name.parent().isRoot()) {
element = element.appendTailText(" (${name.asString()})", true)
}
return element.withIconFromLookupObject()
}
private fun createLookupElement(
descriptor: DeclarationDescriptor,
declaration: PsiElement?,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean
): LookupElement {
if (descriptor is ClassifierDescriptor &&
declaration is PsiClass &&
declaration !is KotlinLightClass) {
// for java classes we create special lookup elements
// because they must be equal to ones created in TypesCompletion
// otherwise we may have duplicates
return createLookupElementForJavaClass(declaration, qualifyNestedClasses, includeClassTypeArguments)
}
if (descriptor is PackageViewDescriptor) {
return createLookupElementForPackage(descriptor.fqName)
}
if (descriptor is PackageFragmentDescriptor) {
return createLookupElementForPackage(descriptor.fqName)
}
// for constructor use name and icon of containing class
val nameAndIconDescriptor: DeclarationDescriptor
val iconDeclaration: PsiElement?
if (descriptor is ConstructorDescriptor) {
nameAndIconDescriptor = descriptor.getContainingDeclaration()
iconDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, nameAndIconDescriptor)
}
else {
nameAndIconDescriptor = descriptor
iconDeclaration = declaration
}
val name = nameAndIconDescriptor.getName().asString()
val lookupObject = object : DeclarationLookupObjectImpl(descriptor, declaration) {
override fun getIcon(flags: Int) = JetDescriptorIconProvider.getIcon(nameAndIconDescriptor, iconDeclaration, flags)
}
var element = LookupElementBuilder.create(lookupObject, name)
val insertHandler = insertHandlerProvider.insertHandler(descriptor)
element = element.withInsertHandler(insertHandler)
when (descriptor) {
is FunctionDescriptor -> {
val returnType = descriptor.getReturnType()
element = element.withTypeText(if (returnType != null) DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) else "")
val insertsLambda = (insertHandler as KotlinFunctionInsertHandler).lambdaInfo != null
if (insertsLambda) {
element = element.appendTailText(" {...} ", false)
}
element = element.appendTailText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderFunctionParameters(descriptor), insertsLambda)
}
is VariableDescriptor -> {
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType()))
}
is ClassDescriptor -> {
val typeParams = descriptor.getTypeConstructor().getParameters()
if (includeClassTypeArguments && typeParams.isNotEmpty()) {
element = element.appendTailText(typeParams.map { it.getName().asString() }.joinToString(", ", "<", ">"), true)
}
var container = descriptor.getContainingDeclaration()
if (qualifyNestedClasses) {
element = element.withPresentableText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderClassifierName(descriptor))
while (container is ClassDescriptor) {
element = element.withLookupString(container.name.asString())
container = container.getContainingDeclaration()
}
}
if (container is PackageFragmentDescriptor || container is ClassDescriptor) {
element = element.appendTailText(" (" + DescriptorUtils.getFqName(container) + ")", true)
}
}
else -> {
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor))
}
}
if (descriptor is CallableDescriptor) {
val extensionReceiver = descriptor.original.extensionReceiverParameter
when {
descriptor is SyntheticJavaPropertyDescriptor -> {
var from = descriptor.getMethod.getName().asString() + "()"
descriptor.setMethod?.let { from += "/" + it.getName().asString() + "()" }
element = element.appendTailText(" (from $from)", true)
}
// no need to show them as extensions
descriptor is SamAdapterExtensionFunctionDescriptor -> {}
extensionReceiver != null -> {
val receiverPresentation = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(extensionReceiver.type)
element = element.appendTailText(" for $receiverPresentation", true)
val container = descriptor.getContainingDeclaration()
val containerPresentation = if (container is ClassDescriptor)
DescriptorUtils.getFqNameFromTopLevelClass(container).toString()
else if (container is PackageFragmentDescriptor)
container.fqName.toString()
else
null
if (containerPresentation != null) {
element = element.appendTailText(" in $containerPresentation", true)
}
}
else -> {
val container = descriptor.getContainingDeclaration()
if (container is PackageFragmentDescriptor) { // we show container only for global functions and properties
//TODO: it would be probably better to show it also for static declarations which are not from the current class (imported)
element = element.appendTailText(" (${container.fqName})", true)
}
}
}
}
if (descriptor is PropertyDescriptor) {
val getterName = JvmAbi.getterName(name)
if (getterName != name) {
element = element.withLookupString(getterName)
}
if (descriptor.isVar) {
element = element.withLookupString(JvmAbi.setterName(name))
}
}
if (lookupObject.isDeprecated) {
element = element.withStrikeoutness(true)
}
if (insertHandler is KotlinFunctionInsertHandler && insertHandler.lambdaInfo != null) {
element.putUserData(KotlinCompletionCharFilter.ACCEPT_OPENING_BRACE, Unit)
}
return element.withIconFromLookupObject()
}
private fun LookupElement.withIconFromLookupObject(): LookupElement {
// add icon in renderElement only to pass presentation.isReal()
return object : LookupElementDecorator<LookupElement>(this) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this@withIconFromLookupObject, presentation.isReal()))
}
}
}
}
@@ -168,7 +168,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
}
}
LookupElementFactory(resolutionFacade, receiverTypes, factoryContext, inDescriptor, { expectedInfos })
LookupElementFactory(resolutionFacade, receiverTypes, factoryContext, inDescriptor, InsertHandlerProvider { expectedInfos })
}
// LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
@@ -17,20 +17,17 @@
package org.jetbrains.kotlin.idea.completion
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Iconable
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiDocCommentOwner
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import javax.swing.Icon
/**
* Stores information about resolved descriptor and position of that descriptor.
@@ -38,8 +35,7 @@ import javax.swing.Icon
*/
public abstract class DeclarationLookupObjectImpl(
public final override val descriptor: DeclarationDescriptor?,
public final override val psiElement: PsiElement?,
private val resolutionFacade: ResolutionFacade
public final override val psiElement: PsiElement?
): DeclarationLookupObject {
init {
assert(descriptor != null || psiElement != null)
@@ -65,20 +61,9 @@ public abstract class DeclarationLookupObjectImpl(
override fun equals(other: Any?): Boolean {
if (this identityEquals other) return true
if (other == null || javaClass != other.javaClass) return false
val lookupObject = other as DeclarationLookupObjectImpl
if (resolutionFacade != lookupObject.resolutionFacade) {
LOG.warn("Descriptors from different resolve sessions")
return false
}
return descriptorsEqualWithSubstitution(descriptor, lookupObject.descriptor) && psiElement == lookupObject.psiElement
}
override val isDeprecated = if (descriptor != null) KotlinBuiltIns.isDeprecated(descriptor) else (psiElement as? PsiDocCommentOwner)?.isDeprecated() ?: false
companion object {
private val LOG = Logger.getInstance("#" + javaClass<DeclarationLookupObject>().getName())
}
}
@@ -0,0 +1,109 @@
/*
* 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.completion.InsertHandler
import com.intellij.codeInsight.lookup.LookupElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.completion.handlers.*
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.types.JetType
import java.util.*
class InsertHandlerProvider(expectedInfosCalculator: () -> Collection<ExpectedInfo>) {
private val expectedInfos by lazy { expectedInfosCalculator() }
public fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> {
return when (descriptor) {
is FunctionDescriptor -> {
val needTypeArguments = needTypeArguments(descriptor)
val parameters = descriptor.valueParameters
when (parameters.size()) {
0 -> KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = false)
1 -> {
val parameterType = parameters.single().getType()
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
val parameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
if (parameterCount <= 1) {
// otherwise additional item with lambda template is to be added
return KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true, lambdaInfo = GenerateLambdaInfo(parameterType, false))
}
}
KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true)
}
else -> KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true)
}
}
is PropertyDescriptor -> KotlinPropertyInsertHandler
is ClassifierDescriptor -> KotlinClassifierInsertHandler
else -> BaseDeclarationInsertHandler()
}
}
private fun needTypeArguments(function: FunctionDescriptor): Boolean {
if (function.typeParameters.isEmpty()) return false
val originalFunction = function.original
val typeParameters = originalFunction.typeParameters
val potentiallyInferred = HashSet<TypeParameterDescriptor>()
fun addPotentiallyInferred(type: JetType) {
val descriptor = type.constructor.declarationDescriptor as? TypeParameterDescriptor
if (descriptor != null && descriptor in typeParameters) {
potentiallyInferred.add(descriptor)
}
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type) && KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(type).size() <= 1) {
// do not rely on inference from input of function type with one or no arguments - use only return type of functional type
addPotentiallyInferred(KotlinBuiltIns.getReturnTypeFromFunctionType(type))
return
}
for (argument in type.arguments) {
if (!argument.isStarProjection) { // otherwise we can fall into infinite recursion
addPotentiallyInferred(argument.type)
}
}
}
originalFunction.extensionReceiverParameter?.type?.let { addPotentiallyInferred(it) }
originalFunction.valueParameters.forEach { addPotentiallyInferred(it.type) }
fun allTypeParametersPotentiallyInferred() = originalFunction.typeParameters.all { it in potentiallyInferred }
if (allTypeParametersPotentiallyInferred()) return false
val returnType = originalFunction.returnType
// check that there is an expected type and return value from the function can potentially match it
if (returnType != null) {
addPotentiallyInferred(returnType)
if (allTypeParametersPotentiallyInferred() && expectedInfos.any { it.fuzzyType?.checkIsSuperTypeOf(originalFunction.fuzzyReturnType()!!) != null }) {
return false
}
}
return true
}
}
@@ -16,30 +16,22 @@
package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext
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.codeInsight.lookup.impl.LookupCellRenderer
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.util.PlatformIcons
import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.completion.handlers.*
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
import org.jetbrains.kotlin.idea.completion.handlers.GenerateLambdaInfo
import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.buildLambdaPresentation
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.JetProperty
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -49,23 +41,22 @@ import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import java.util.*
public class LookupElementFactory(
class LookupElementFactory(
private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<JetType>,
private val context: LookupElementFactory.Context,
private val inDescriptor: DeclarationDescriptor?,
expectedInfosCalculator: () -> Collection<ExpectedInfo>
public val insertHandlerProvider: InsertHandlerProvider
) {
private val basicFactory = BasicLookupElementFactory(resolutionFacade.project, insertHandlerProvider)
public enum class Context {
NORMAL,
STRING_TEMPLATE_AFTER_DOLLAR,
INFIX_CALL
}
private val expectedInfos by lazy { expectedInfosCalculator() }
public fun createStandardLookupElementsForDescriptor(descriptor: DeclarationDescriptor, useReceiverTypes: Boolean): Collection<LookupElement> {
val result = SmartList<LookupElement>()
@@ -106,7 +97,7 @@ public class LookupElementFactory(
private fun createFunctionCallElementWithExplicitLambdaParameters(descriptor: FunctionDescriptor, parameterType: JetType, useReceiverTypes: Boolean): LookupElement {
var lookupElement = createLookupElement(descriptor, useReceiverTypes)
val needTypeArguments = (getDefaultInsertHandler(descriptor) as KotlinFunctionInsertHandler).needTypeArguments
val needTypeArguments = (insertHandlerProvider.insertHandler(descriptor) as KotlinFunctionInsertHandler).needTypeArguments
val lambdaInfo = GenerateLambdaInfo(parameterType, true)
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
@@ -166,12 +157,7 @@ public class LookupElementFactory(
qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true
): LookupElement {
val _descriptor = if (descriptor is CallableMemberDescriptor)
DescriptorUtils.unwrapFakeOverride(descriptor)
else
descriptor
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(resolutionFacade.project, _descriptor)
var element = createLookupElement(_descriptor, declaration, qualifyNestedClasses, includeClassTypeArguments)
var element = basicFactory.createLookupElement(descriptor, qualifyNestedClasses, includeClassTypeArguments)
val weight = callableWeight(descriptor)
if (weight != null) {
@@ -220,212 +206,6 @@ public class LookupElementFactory(
GRAYED
}
public fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement {
val lookupObject = object : DeclarationLookupObjectImpl(null, psiClass, resolutionFacade) {
override fun getIcon(flags: Int) = psiClass.getIcon(flags)
}
var element = LookupElementBuilder.create(lookupObject, psiClass.getName()!!)
.withInsertHandler(KotlinClassifierInsertHandler)
val typeParams = psiClass.getTypeParameters()
if (includeClassTypeArguments && typeParams.isNotEmpty()) {
element = element.appendTailText(typeParams.map { it.getName() }.joinToString(", ", "<", ">"), true)
}
val qualifiedName = psiClass.getQualifiedName()!!
var containerName = qualifiedName.substringBeforeLast('.', FqName.ROOT.toString())
if (qualifyNestedClasses) {
val nestLevel = psiClass.parents.takeWhile { it is PsiClass }.count()
if (nestLevel > 0) {
var itemText = psiClass.getName()
for (i in 1..nestLevel) {
val outerClassName = containerName.substringAfterLast('.')
element = element.withLookupString(outerClassName)
itemText = outerClassName + "." + itemText
containerName = containerName.substringBeforeLast('.', FqName.ROOT.toString())
}
element = element.withPresentableText(itemText!!)
}
}
element = element.appendTailText(" ($containerName)", true)
if (lookupObject.isDeprecated) {
element = element.withStrikeoutness(true)
}
return element.withIconFromLookupObject()
}
public fun createLookupElementForPackage(name: FqName): LookupElement {
var element = LookupElementBuilder.create(PackageLookupObject(name), name.shortName().asString())
element = element.withInsertHandler(BaseDeclarationInsertHandler())
if (!name.parent().isRoot()) {
element = element.appendTailText(" (${name.asString()})", true)
}
return element.withIconFromLookupObject()
}
private fun createLookupElement(
descriptor: DeclarationDescriptor,
declaration: PsiElement?,
qualifyNestedClasses: Boolean,
includeClassTypeArguments: Boolean
): LookupElement {
if (descriptor is ClassifierDescriptor &&
declaration is PsiClass &&
declaration !is KotlinLightClass) {
// for java classes we create special lookup elements
// because they must be equal to ones created in TypesCompletion
// otherwise we may have duplicates
return createLookupElementForJavaClass(declaration, qualifyNestedClasses, includeClassTypeArguments)
}
if (descriptor is PackageViewDescriptor) {
return createLookupElementForPackage(descriptor.fqName)
}
if (descriptor is PackageFragmentDescriptor) {
return createLookupElementForPackage(descriptor.fqName)
}
// for constructor use name and icon of containing class
val nameAndIconDescriptor: DeclarationDescriptor
val iconDeclaration: PsiElement?
if (descriptor is ConstructorDescriptor) {
nameAndIconDescriptor = descriptor.getContainingDeclaration()
iconDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(resolutionFacade.project, nameAndIconDescriptor)
}
else {
nameAndIconDescriptor = descriptor
iconDeclaration = declaration
}
val name = nameAndIconDescriptor.getName().asString()
val lookupObject = object : DeclarationLookupObjectImpl(descriptor, declaration, resolutionFacade) {
override fun getIcon(flags: Int) = JetDescriptorIconProvider.getIcon(nameAndIconDescriptor, iconDeclaration, flags)
}
var element = LookupElementBuilder.create(lookupObject, name)
val insertHandler = getDefaultInsertHandler(descriptor)
element = element.withInsertHandler(insertHandler)
when (descriptor) {
is FunctionDescriptor -> {
val returnType = descriptor.getReturnType()
element = element.withTypeText(if (returnType != null) DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(returnType) else "")
val insertsLambda = (insertHandler as KotlinFunctionInsertHandler).lambdaInfo != null
if (insertsLambda) {
element = element.appendTailText(" {...} ", false)
}
element = element.appendTailText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderFunctionParameters(descriptor), insertsLambda)
}
is VariableDescriptor -> {
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor.getType()))
}
is ClassDescriptor -> {
val typeParams = descriptor.getTypeConstructor().getParameters()
if (includeClassTypeArguments && typeParams.isNotEmpty()) {
element = element.appendTailText(typeParams.map { it.getName().asString() }.joinToString(", ", "<", ">"), true)
}
var container = descriptor.getContainingDeclaration()
if (qualifyNestedClasses) {
element = element.withPresentableText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderClassifierName(descriptor))
while (container is ClassDescriptor) {
element = element.withLookupString(container.name.asString())
container = container.getContainingDeclaration()
}
}
if (container is PackageFragmentDescriptor || container is ClassDescriptor) {
element = element.appendTailText(" (" + DescriptorUtils.getFqName(container) + ")", true)
}
}
else -> {
element = element.withTypeText(DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor))
}
}
if (descriptor is CallableDescriptor) {
val extensionReceiver = descriptor.original.extensionReceiverParameter
when {
descriptor is SyntheticJavaPropertyDescriptor -> {
var from = descriptor.getMethod.getName().asString() + "()"
descriptor.setMethod?.let { from += "/" + it.getName().asString() + "()" }
element = element.appendTailText(" (from $from)", true)
}
// no need to show them as extensions
descriptor is SamAdapterExtensionFunctionDescriptor -> {}
extensionReceiver != null -> {
val receiverPresentation = DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(extensionReceiver.type)
element = element.appendTailText(" for $receiverPresentation", true)
val container = descriptor.getContainingDeclaration()
val containerPresentation = if (container is ClassDescriptor)
DescriptorUtils.getFqNameFromTopLevelClass(container).toString()
else if (container is PackageFragmentDescriptor)
container.fqName.toString()
else
null
if (containerPresentation != null) {
element = element.appendTailText(" in $containerPresentation", true)
}
}
else -> {
val container = descriptor.getContainingDeclaration()
if (container is PackageFragmentDescriptor) { // we show container only for global functions and properties
//TODO: it would be probably better to show it also for static declarations which are not from the current class (imported)
element = element.appendTailText(" (${container.fqName})", true)
}
}
}
}
if (descriptor is PropertyDescriptor) {
val getterName = JvmAbi.getterName(name)
if (getterName != name) {
element = element.withLookupString(getterName)
}
if (descriptor.isVar) {
element = element.withLookupString(JvmAbi.setterName(name))
}
}
if (lookupObject.isDeprecated) {
element = element.withStrikeoutness(true)
}
if (insertHandler is KotlinFunctionInsertHandler && insertHandler.lambdaInfo != null) {
element.putUserData(KotlinCompletionCharFilter.ACCEPT_OPENING_BRACE, Unit)
}
return element.withIconFromLookupObject()
}
private fun LookupElement.withIconFromLookupObject(): LookupElement {
// add icon in renderElement only to pass presentation.isReal()
return object : LookupElementDecorator<LookupElement>(this) {
override fun renderElement(presentation: LookupElementPresentation) {
super.renderElement(presentation)
presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this@withIconFromLookupObject, presentation.isReal()))
}
}
}
private fun callableWeight(descriptor: DeclarationDescriptor): CallableWeight? {
if (descriptor !is CallableDescriptor) return null
@@ -471,82 +251,11 @@ public class LookupElementFactory(
return typeParameter.containingDeclaration == original
}
public fun getDefaultInsertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> {
return when (descriptor) {
is FunctionDescriptor -> {
val needTypeArguments = needTypeArguments(descriptor)
val parameters = descriptor.valueParameters
when (parameters.size()) {
0 -> KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = false)
1 -> {
val parameterType = parameters.single().getType()
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(parameterType)) {
val parameterCount = KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(parameterType).size()
if (parameterCount <= 1) {
// otherwise additional item with lambda template is to be added
return KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true, lambdaInfo = GenerateLambdaInfo(parameterType, false))
}
}
KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true)
}
else -> KotlinFunctionInsertHandler(needTypeArguments, needValueArguments = true)
}
}
is PropertyDescriptor -> KotlinPropertyInsertHandler
is ClassifierDescriptor -> KotlinClassifierInsertHandler
else -> BaseDeclarationInsertHandler()
}
public fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement {
return basicFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses, includeClassTypeArguments)
}
private fun needTypeArguments(function: FunctionDescriptor): Boolean {
if (function.typeParameters.isEmpty()) return false
val originalFunction = function.original
val typeParameters = originalFunction.typeParameters
val potentiallyInferred = HashSet<TypeParameterDescriptor>()
fun addPotentiallyInferred(type: JetType) {
val descriptor = type.constructor.declarationDescriptor as? TypeParameterDescriptor
if (descriptor != null && descriptor in typeParameters) {
potentiallyInferred.add(descriptor)
}
if (KotlinBuiltIns.isExactFunctionOrExtensionFunctionType(type) && KotlinBuiltIns.getParameterTypeProjectionsFromFunctionType(type).size() <= 1) {
// do not rely on inference from input of function type with one or no arguments - use only return type of functional type
addPotentiallyInferred(KotlinBuiltIns.getReturnTypeFromFunctionType(type))
return
}
for (argument in type.arguments) {
if (!argument.isStarProjection) { // otherwise we can fall into infinite recursion
addPotentiallyInferred(argument.type)
}
}
}
originalFunction.extensionReceiverParameter?.type?.let { addPotentiallyInferred(it) }
originalFunction.valueParameters.forEach { addPotentiallyInferred(it.type) }
fun allTypeParametersPotentiallyInferred() = originalFunction.typeParameters.all { it in potentiallyInferred }
if (allTypeParametersPotentiallyInferred()) return false
val returnType = originalFunction.returnType
// check that there is an expected type and return value from the function can potentially match it
if (returnType != null) {
addPotentiallyInferred(returnType)
if (allTypeParametersPotentiallyInferred() && expectedInfos.any { it.fuzzyType?.checkIsSuperTypeOf(originalFunction.fuzzyReturnType()!!) != null }) {
return false
}
}
return true
public fun createLookupElementForPackage(name: FqName): LookupElement {
return basicFactory.createLookupElementForPackage(name)
}
}
@@ -53,9 +53,9 @@ object PackageDirectiveCompletion {
val bindingContext = resolutionFacade.analyze(expression)
val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter())
val lookupElementFactory = LookupElementFactory(resolutionFacade, emptyList(), LookupElementFactory.Context.NORMAL, null, { emptyList() })
val lookupElementFactory = BasicLookupElementFactory(resolutionFacade.project, InsertHandlerProvider(expectedInfosCalculator = { emptyList() }))
for (variant in variants) {
val lookupElement = lookupElementFactory.createLookupElement(variant, useReceiverTypes = false)
val lookupElement = lookupElementFactory.createLookupElement(variant)
if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(lookupElement)
}
@@ -185,7 +185,7 @@ class TypeInstantiationItems(
val baseInsertHandler = when (visibleConstructors.size()) {
0 -> KotlinFunctionInsertHandler(needTypeArguments = false, needValueArguments = false)
1 -> lookupElementFactory.getDefaultInsertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler
1 -> lookupElementFactory.insertHandlerProvider.insertHandler(visibleConstructors.single()) as KotlinFunctionInsertHandler
else -> KotlinFunctionInsertHandler(needTypeArguments = false, needValueArguments = true)
}