From 6dde70e358d581b83e2eb506c394e2d97fbc4939 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 26 Aug 2015 18:31:41 +0300 Subject: [PATCH] Split LookupElementFactory into 3 classes --- .../completion/BasicLookupElementFactory.kt | 261 ++++++++++++++ .../idea/completion/CompletionSession.kt | 2 +- .../completion/DeclarationLookupObjectImpl.kt | 17 +- .../idea/completion/InsertHandlerProvider.kt | 109 ++++++ .../idea/completion/LookupElementFactory.kt | 323 +----------------- .../completion/PackageDirectiveCompletion.kt | 4 +- .../smart/TypeInstantiationItems.kt | 2 +- 7 files changed, 391 insertions(+), 327 deletions(-) create mode 100644 idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt create mode 100644 idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt new file mode 100644 index 00000000000..6742290b0bc --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicLookupElementFactory.kt @@ -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(this) { + override fun renderElement(presentation: LookupElementPresentation) { + super.renderElement(presentation) + presentation.setIcon(DefaultLookupItemRenderer.getRawIcon(this@withIconFromLookupObject, presentation.isReal())) + } + } + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index c7290942c63..623edc671e2 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -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 diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DeclarationLookupObjectImpl.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DeclarationLookupObjectImpl.kt index fe40c78ce9a..8c16044e6a3 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DeclarationLookupObjectImpl.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/DeclarationLookupObjectImpl.kt @@ -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().getName()) - } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt new file mode 100644 index 00000000000..78509456e6b --- /dev/null +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/InsertHandlerProvider.kt @@ -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) { + private val expectedInfos by lazy { expectedInfosCalculator() } + + public fun insertHandler(descriptor: DeclarationDescriptor): InsertHandler { + 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() + + 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 + } +} \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt index 4e5a97ef95a..25970f00cfe 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/LookupElementFactory.kt @@ -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, private val context: LookupElementFactory.Context, private val inDescriptor: DeclarationDescriptor?, - expectedInfosCalculator: () -> Collection + 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 { val result = SmartList() @@ -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) { @@ -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(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 { - 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() - - 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) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt index a453b230c39..8c4e4154c81 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/PackageDirectiveCompletion.kt @@ -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) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt index 68726849675..43d5ca65b95 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt @@ -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) }