Optimization in completion: don't spend too much time calculating root packages

This commit is contained in:
Valentin Kipyatkov
2015-07-15 21:48:19 +03:00
parent 10b5942537
commit 136af1cd60
8 changed files with 83 additions and 16 deletions
@@ -19,12 +19,17 @@ package org.jetbrains.kotlin.idea.completion
import com.intellij.codeInsight.completion.* import com.intellij.codeInsight.completion.*
import com.intellij.patterns.PatternCondition import com.intellij.patterns.PatternCondition
import com.intellij.patterns.StandardPatterns import com.intellij.patterns.StandardPatterns
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.ProcessingContext import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.stubindex.PackageIndexUtil
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -70,7 +75,10 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
private val completionKind = calcCompletionKind() private val completionKind = calcCompletionKind()
override val descriptorKindFilter = completionKind.descriptorKindFilter override val descriptorKindFilter = if (isNoQualifierContext())
completionKind.descriptorKindFilter?.withoutKinds(DescriptorKindFilter.PACKAGES_MASK)
else
completionKind.descriptorKindFilter
private val parameterNameAndTypeCompletion = if (shouldCompleteParameterNameAndType()) private val parameterNameAndTypeCompletion = if (shouldCompleteParameterNameAndType())
ParameterNameAndTypeCompletion(collector, lookupElementFactory, prefixMatcher, resolutionFacade) ParameterNameAndTypeCompletion(collector, lookupElementFactory, prefixMatcher, resolutionFacade)
@@ -193,6 +201,26 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
} }
} }
// getting root packages from scope is very slow so we do this in alternative way
if (isNoQualifierContext() && (completionKind.descriptorKindFilter?.kindMask ?: 0).and(DescriptorKindFilter.PACKAGES_MASK) != 0) {
//TODO: move this code somewhere else
//TODO: filter by prefix for better performance?
val packageNames = PackageIndexUtil.getSubPackageFqNames(FqName.ROOT, originalSearchScope, project)
.map { it.shortName() }
.toMutableSet()
if (!ProjectStructureUtil.isJsKotlinModule(parameters.getOriginalFile() as JetFile)) {
JavaPsiFacade.getInstance(project).findPackage("")?.getSubPackages(originalSearchScope)?.forEach { psiPackage ->
val name = psiPackage.getName()
if (Name.isValidIdentifier(name!!)) {
packageNames.add(Name.identifier(name))
}
}
}
packageNames.forEach { collector.addElement(lookupElementFactory.createLookupElementForPackage(it)) }
}
if (completionKind != CompletionKind.KEYWORDS_ONLY) { if (completionKind != CompletionKind.KEYWORDS_ONLY) {
flushToResultSet() flushToResultSet()
@@ -238,7 +238,8 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
} }
protected fun getRuntimeReceiverTypeReferenceVariants(): Collection<DeclarationDescriptor> { protected fun getRuntimeReceiverTypeReferenceVariants(): Collection<DeclarationDescriptor> {
val descriptors = referenceVariantsHelper.getReferenceVariants(nameExpression!!, descriptorKindFilter!!, prefixMatcher.asNameFilter(), useRuntimeReceiverType = true) val restrictedKindFilter = descriptorKindFilter!!.restrictedToKinds(DescriptorKindFilter.FUNCTIONS_MASK or DescriptorKindFilter.VARIABLES_MASK) // optimization
val descriptors = referenceVariantsHelper.getReferenceVariants(nameExpression!!, restrictedKindFilter, prefixMatcher.asNameFilter(), useRuntimeReceiverType = true)
return descriptors.filter { descriptor -> return descriptors.filter { descriptor ->
referenceVariants.none { comparePossiblyOverridingDescriptors(project, it, descriptor) } referenceVariants.none { comparePossiblyOverridingDescriptors(project, it, descriptor) }
} }
@@ -143,11 +143,10 @@ private class DeclarationRemotenessWeigher(private val file: JetFile) : LookupEl
return Weight.thisFile return Weight.thisFile
} }
val qualifiedName = o.qualifiedName() val fqName = o.importableFqName
// Invalid name can be met for companion object descriptor: Test.MyTest.A.<no name provided>.testOther // Invalid name can be met for companion object descriptor: Test.MyTest.A.<no name provided>.testOther
if (qualifiedName != null && isValidJavaFqName(qualifiedName)) { if (fqName != null) {
val importPath = ImportPath(qualifiedName) val importPath = ImportPath(fqName, false)
val fqName = importPath.fqnPart()
return when { return when {
JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() -> Weight.notToBeUsedInKotlin JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() -> Weight.notToBeUsedInKotlin
ImportInsertHelper.getInstance(file.getProject()).isImportedWithDefault(importPath, file) -> Weight.kotlinDefaultImport ImportInsertHelper.getInstance(file.getProject()).isImportedWithDefault(importPath, file) -> Weight.kotlinDefaultImport
@@ -18,12 +18,17 @@ 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.openapi.util.Iconable
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 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.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.caches.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.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution import org.jetbrains.kotlin.util.descriptorsEqualWithSubstitution
import javax.swing.Icon import javax.swing.Icon
@@ -40,6 +45,17 @@ public abstract class DeclarationLookupObjectImpl(
assert(descriptor != null || psiElement != null) assert(descriptor != null || psiElement != null)
} }
override val name: Name?
get() = descriptor?.getName() ?: (psiElement as? PsiNamedElement)?.getName()?.let { Name.identifier(it) }
override val importableFqName: FqName?
get() {
return if (descriptor != null)
descriptor.importableFqName
else
(psiElement as? PsiClass)?.getQualifiedName()?.let { FqName(it) }
}
override fun toString() = super<DeclarationLookupObject>.toString() + " " + (descriptor ?: psiElement) override fun toString() = super<DeclarationLookupObject>.toString() + " " + (descriptor ?: psiElement)
override fun hashCode(): Int { override fun hashCode(): Int {
@@ -35,16 +35,10 @@ public class KotlinExcludeFromCompletionLookupActionProvider : LookupActionProvi
val project = lookup.getPsiFile().getProject() val project = lookup.getPsiFile().getProject()
lookupObject.descriptor?.importableFqName?.let { lookupObject.importableFqName?.let {
addExcludes(consumer, project, it.asString()) addExcludes(consumer, project, it.asString())
return return
} }
val psiElement = lookupObject.psiElement
if (psiElement is PsiClass) {
val qualifiedName = psiElement.getQualifiedName() ?: return
addExcludes(consumer, project, qualifiedName)
}
} }
private fun addExcludes(consumer: Consumer<LookupElementAction>, project: Project, fqName: String) { private fun addExcludes(consumer: Consumer<LookupElementAction>, project: Project, fqName: String) {
@@ -21,15 +21,18 @@ import com.intellij.codeInsight.lookup.*
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.psi.PsiElement
import com.intellij.util.PlatformIcons
import org.jetbrains.kotlin.asJava.KotlinLightClass 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.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.completion.handlers.* import org.jetbrains.kotlin.idea.completion.handlers.*
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.TypeNullability
import org.jetbrains.kotlin.types.typeUtil.nullability import org.jetbrains.kotlin.types.typeUtil.nullability
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
@@ -37,6 +40,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.synthetic.SyntheticJavaBeansPropertyDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaBeansPropertyDescriptor
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 javax.swing.Icon
public class LookupElementFactory( public class LookupElementFactory(
private val resolutionFacade: ResolutionFacade, private val resolutionFacade: ResolutionFacade,
@@ -137,6 +141,24 @@ public class LookupElementFactory(
return element.withIconFromLookupObject() return element.withIconFromLookupObject()
} }
public fun createLookupElementForPackage(shortName: Name): LookupElement {
return LookupElementBuilder.create(PackageLookupObject(shortName), shortName.asString())
.withInsertHandler(BaseDeclarationInsertHandler())
.withIconFromLookupObject()
}
private data class PackageLookupObject(override val name: Name) : DeclarationLookupObject {
override val psiElement: PsiElement? get() = null
override val descriptor: DeclarationDescriptor? get() = null
override val importableFqName: FqName? get() = null
override val isDeprecated: Boolean get() = false
override fun getIcon(flags: Int) = PlatformIcons.PACKAGE_ICON
}
private fun createLookupElement( private fun createLookupElement(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
declaration: PsiElement?, declaration: PsiElement?,
@@ -152,6 +174,10 @@ public class LookupElementFactory(
return createLookupElementForJavaClass(declaration, qualifyNestedClasses, includeClassTypeArguments) return createLookupElementForJavaClass(declaration, qualifyNestedClasses, includeClassTypeArguments)
} }
if (descriptor is PackageViewDescriptor || descriptor is PackageFragmentDescriptor) {
return createLookupElementForPackage(descriptor.getName())
}
// for constructor use name and icon of containing class // for constructor use name and icon of containing class
val nameAndIconDescriptor: DeclarationDescriptor val nameAndIconDescriptor: DeclarationDescriptor
val iconDeclaration: PsiElement? val iconDeclaration: PsiElement?
@@ -26,9 +26,8 @@ import org.jetbrains.kotlin.renderer.render
open class BaseDeclarationInsertHandler : InsertHandler<LookupElement> { open class BaseDeclarationInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) { override fun handleInsert(context: InsertionContext, item: LookupElement) {
val descriptor = (item.getObject() as? DeclarationLookupObject)?.descriptor val name = (item.getObject() as? DeclarationLookupObject)?.name
if (descriptor != null) { if (name != null) {
val name = descriptor.getName()
val nameInCode = name.render() val nameInCode = name.render()
val document = context.getDocument() val document = context.getDocument()
val needEscaping = nameInCode != name.asString() val needEscaping = nameInCode != name.asString()
@@ -19,9 +19,13 @@ package org.jetbrains.kotlin.idea.core.completion
import com.intellij.openapi.util.Iconable import com.intellij.openapi.util.Iconable
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
public interface DeclarationLookupObject : Iconable { public interface DeclarationLookupObject : Iconable {
public val psiElement: PsiElement? public val psiElement: PsiElement?
public val descriptor: DeclarationDescriptor? public val descriptor: DeclarationDescriptor?
public val name: Name?
public val importableFqName: FqName?
public val isDeprecated: Boolean public val isDeprecated: Boolean
} }