Split LookupElementFactory into 3 classes
This commit is contained in:
+261
@@ -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
|
||||
|
||||
+1
-16
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
+109
@@ -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
-307
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user