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 // LookupElementsCollector instantiation is deferred because virtual call to createSorter uses data from derived classes
@@ -17,20 +17,17 @@
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.completion
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Iconable
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiDocCommentOwner import com.intellij.psi.PsiDocCommentOwner
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement import com.intellij.psi.PsiNamedElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor 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.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import javax.swing.Icon
/** /**
* Stores information about resolved descriptor and position of that descriptor. * Stores information about resolved descriptor and position of that descriptor.
@@ -38,8 +35,7 @@ import javax.swing.Icon
*/ */
public abstract class DeclarationLookupObjectImpl( public abstract class DeclarationLookupObjectImpl(
public final override val descriptor: DeclarationDescriptor?, public final override val descriptor: DeclarationDescriptor?,
public final override val psiElement: PsiElement?, public final override val psiElement: PsiElement?
private val resolutionFacade: ResolutionFacade
): DeclarationLookupObject { ): DeclarationLookupObject {
init { init {
assert(descriptor != null || psiElement != null) assert(descriptor != null || psiElement != null)
@@ -65,20 +61,9 @@ public abstract class DeclarationLookupObjectImpl(
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this identityEquals other) return true if (this identityEquals other) return true
if (other == null || javaClass != other.javaClass) return false if (other == null || javaClass != other.javaClass) return false
val lookupObject = other as DeclarationLookupObjectImpl 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 return descriptorsEqualWithSubstitution(descriptor, lookupObject.descriptor) && psiElement == lookupObject.psiElement
} }
override val isDeprecated = if (descriptor != null) KotlinBuiltIns.isDeprecated(descriptor) else (psiElement as? PsiDocCommentOwner)?.isDeprecated() ?: false 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 package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.InsertHandler
import com.intellij.codeInsight.completion.InsertionContext 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.codeInsight.lookup.impl.LookupCellRenderer
import com.intellij.psi.PsiClass import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.util.PlatformIcons import com.intellij.util.PlatformIcons
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.asJava.KotlinLightClass
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider import org.jetbrains.kotlin.idea.completion.handlers.GenerateLambdaInfo
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.* import org.jetbrains.kotlin.idea.completion.handlers.buildLambdaPresentation
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.JetProperty 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.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils 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.JetType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import java.util.*
public class LookupElementFactory( class LookupElementFactory(
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
private val receiverTypes: Collection<JetType>, private val receiverTypes: Collection<JetType>,
private val context: LookupElementFactory.Context, private val context: LookupElementFactory.Context,
private val inDescriptor: DeclarationDescriptor?, private val inDescriptor: DeclarationDescriptor?,
expectedInfosCalculator: () -> Collection<ExpectedInfo> public val insertHandlerProvider: InsertHandlerProvider
) { ) {
private val basicFactory = BasicLookupElementFactory(resolutionFacade.project, insertHandlerProvider)
public enum class Context { public enum class Context {
NORMAL, NORMAL,
STRING_TEMPLATE_AFTER_DOLLAR, STRING_TEMPLATE_AFTER_DOLLAR,
INFIX_CALL INFIX_CALL
} }
private val expectedInfos by lazy { expectedInfosCalculator() }
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>()
@@ -106,7 +97,7 @@ public class LookupElementFactory(
private fun createFunctionCallElementWithExplicitLambdaParameters(descriptor: FunctionDescriptor, parameterType: JetType, useReceiverTypes: Boolean): LookupElement { private fun createFunctionCallElementWithExplicitLambdaParameters(descriptor: FunctionDescriptor, parameterType: JetType, useReceiverTypes: Boolean): LookupElement {
var lookupElement = createLookupElement(descriptor, useReceiverTypes) 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) val lambdaInfo = GenerateLambdaInfo(parameterType, true)
lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) { lookupElement = object : LookupElementDecorator<LookupElement>(lookupElement) {
@@ -166,12 +157,7 @@ public class LookupElementFactory(
qualifyNestedClasses: Boolean = false, qualifyNestedClasses: Boolean = false,
includeClassTypeArguments: Boolean = true includeClassTypeArguments: Boolean = true
): LookupElement { ): LookupElement {
val _descriptor = if (descriptor is CallableMemberDescriptor) var element = basicFactory.createLookupElement(descriptor, qualifyNestedClasses, includeClassTypeArguments)
DescriptorUtils.unwrapFakeOverride(descriptor)
else
descriptor
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(resolutionFacade.project, _descriptor)
var element = createLookupElement(_descriptor, declaration, qualifyNestedClasses, includeClassTypeArguments)
val weight = callableWeight(descriptor) val weight = callableWeight(descriptor)
if (weight != null) { if (weight != null) {
@@ -220,212 +206,6 @@ public class LookupElementFactory(
GRAYED 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? { private fun callableWeight(descriptor: DeclarationDescriptor): CallableWeight? {
if (descriptor !is CallableDescriptor) return null if (descriptor !is CallableDescriptor) return null
@@ -471,82 +251,11 @@ public class LookupElementFactory(
return typeParameter.containingDeclaration == original return typeParameter.containingDeclaration == original
} }
public fun getDefaultInsertHandler(descriptor: DeclarationDescriptor): InsertHandler<LookupElement> { public fun createLookupElementForJavaClass(psiClass: PsiClass, qualifyNestedClasses: Boolean = false, includeClassTypeArguments: Boolean = true): LookupElement {
return when (descriptor) { return basicFactory.createLookupElementForJavaClass(psiClass, qualifyNestedClasses, includeClassTypeArguments)
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 { public fun createLookupElementForPackage(name: FqName): LookupElement {
if (function.typeParameters.isEmpty()) return false return basicFactory.createLookupElementForPackage(name)
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
} }
} }
@@ -53,9 +53,9 @@ object PackageDirectiveCompletion {
val bindingContext = resolutionFacade.analyze(expression) val bindingContext = resolutionFacade.analyze(expression)
val variants = ReferenceVariantsHelper(bindingContext, resolutionFacade, { true }).getPackageReferenceVariants(expression, prefixMatcher.asNameFilter()) 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) { for (variant in variants) {
val lookupElement = lookupElementFactory.createLookupElement(variant, useReceiverTypes = false) val lookupElement = lookupElementFactory.createLookupElement(variant)
if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) { if (!lookupElement.getLookupString().contains(DUMMY_IDENTIFIER)) {
result.addElement(lookupElement) result.addElement(lookupElement)
} }
@@ -185,7 +185,7 @@ class TypeInstantiationItems(
val baseInsertHandler = when (visibleConstructors.size()) { val baseInsertHandler = when (visibleConstructors.size()) {
0 -> KotlinFunctionInsertHandler(needTypeArguments = false, needValueArguments = false) 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) else -> KotlinFunctionInsertHandler(needTypeArguments = false, needValueArguments = true)
} }