Optimization in completion: don't spend too much time calculating root packages
This commit is contained in:
+29
-1
@@ -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
|
||||
|
||||
+16
@@ -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 {
|
||||
|
||||
+1
-7
@@ -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?
|
||||
|
||||
+2
-3
@@ -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()
|
||||
|
||||
+4
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user