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.patterns.PatternCondition
import com.intellij.patterns.StandardPatterns
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.ProcessingContext
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
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.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
@@ -70,7 +75,10 @@ class BasicCompletionSession(configuration: CompletionSessionConfiguration,
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())
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) {
flushToResultSet()
@@ -238,7 +238,8 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
}
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 ->
referenceVariants.none { comparePossiblyOverridingDescriptors(project, it, descriptor) }
}
@@ -143,11 +143,10 @@ private class DeclarationRemotenessWeigher(private val file: JetFile) : LookupEl
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
if (qualifiedName != null && isValidJavaFqName(qualifiedName)) {
val importPath = ImportPath(qualifiedName)
val fqName = importPath.fqnPart()
if (fqName != null) {
val importPath = ImportPath(fqName, false)
return when {
JavaToKotlinClassMap.INSTANCE.mapPlatformClass(fqName).isNotEmpty() -> Weight.notToBeUsedInKotlin
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.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.caches.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
@@ -40,6 +45,17 @@ public abstract class DeclarationLookupObjectImpl(
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 hashCode(): Int {
@@ -35,16 +35,10 @@ public class KotlinExcludeFromCompletionLookupActionProvider : LookupActionProvi
val project = lookup.getPsiFile().getProject()
lookupObject.descriptor?.importableFqName?.let {
lookupObject.importableFqName?.let {
addExcludes(consumer, project, it.asString())
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) {
@@ -21,15 +21,18 @@ import com.intellij.codeInsight.lookup.*
import com.intellij.codeInsight.lookup.impl.LookupCellRenderer
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.util.PlatformIcons
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.caches.resolve.ResolutionFacade
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.nullability
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.renderer.DescriptorRenderer
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.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import javax.swing.Icon
public class LookupElementFactory(
private val resolutionFacade: ResolutionFacade,
@@ -137,6 +141,24 @@ public class LookupElementFactory(
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(
descriptor: DeclarationDescriptor,
declaration: PsiElement?,
@@ -152,6 +174,10 @@ public class LookupElementFactory(
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
val nameAndIconDescriptor: DeclarationDescriptor
val iconDeclaration: PsiElement?
@@ -26,9 +26,8 @@ import org.jetbrains.kotlin.renderer.render
open class BaseDeclarationInsertHandler : InsertHandler<LookupElement> {
override fun handleInsert(context: InsertionContext, item: LookupElement) {
val descriptor = (item.getObject() as? DeclarationLookupObject)?.descriptor
if (descriptor != null) {
val name = descriptor.getName()
val name = (item.getObject() as? DeclarationLookupObject)?.name
if (name != null) {
val nameInCode = name.render()
val document = context.getDocument()
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.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
public interface DeclarationLookupObject : Iconable {
public val psiElement: PsiElement?
public val descriptor: DeclarationDescriptor?
public val name: Name?
public val importableFqName: FqName?
public val isDeprecated: Boolean
}