KT-14732 Slow typing, copy-pasting, highlighting in Kotlin files due to auto-import suggestion calculation in EDT

#KT-14732 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-11-10 19:44:30 +03:00
parent 3d3a202601
commit 0ef5f662ad
3 changed files with 45 additions and 36 deletions
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.KotlinDescriptorIconProvider
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.ImportableFqNameClassifier
import org.jetbrains.kotlin.idea.imports.importableFqName
@@ -52,15 +53,17 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
internal fun createSingleImportAction(project: Project,
editor: Editor,
element: KtElement,
descriptors: Collection<DeclarationDescriptor>): KotlinAddImportAction {
internal fun createSingleImportAction(
project: Project,
editor: Editor,
element: KtElement,
fqNames: Collection<FqName>
): KotlinAddImportAction {
val file = element.getContainingKtFile()
val prioritizer = Prioritizer(element.getContainingKtFile())
val variants = descriptors
.groupBy { it.importableFqName!! }
.map {
val (fqName, sameFqNameDescriptors) = it
val variants = fqNames
.map { fqName ->
val sameFqNameDescriptors = file.resolveImportReference(fqName)
val priority = sameFqNameDescriptors.map { prioritizer.priority(it) }.min()!!
Prioritizer.VariantWithPriority(SingleImportVariant(fqName, sameFqNameDescriptors), priority)
}
@@ -70,25 +73,29 @@ internal fun createSingleImportAction(project: Project,
return KotlinAddImportAction(project, editor, element, variants)
}
internal fun createGroupedImportsAction(project: Project,
editor: Editor,
element: KtElement,
autoImportDescription: String,
descriptors: Collection<DeclarationDescriptor>): KotlinAddImportAction {
internal fun createGroupedImportsAction(
project: Project,
editor: Editor,
element: KtElement,
autoImportDescription: String,
fqNames: Collection<FqName>
): KotlinAddImportAction {
val prioritizer = DescriptorGroupPrioritizer(element.getContainingKtFile())
val variants = descriptors
.groupBy { it.importableFqName!!.parentOrNull() ?: FqName.ROOT }
val file = element.getContainingKtFile()
val variants = fqNames
.groupBy { it.parentOrNull() ?: FqName.ROOT }
.map {
val samePackageDescriptors = it.value
val variant = if (samePackageDescriptors.size > 1) {
GroupedImportVariant(autoImportDescription, samePackageDescriptors)
val samePackageFqNames = it.value
val descriptors = samePackageFqNames.flatMap { file.resolveImportReference(it) }
val variant = if (samePackageFqNames.size > 1) {
GroupedImportVariant(autoImportDescription, descriptors)
}
else {
SingleImportVariant(samePackageDescriptors.first().importableFqName!!, samePackageDescriptors)
SingleImportVariant(samePackageFqNames.first(), descriptors)
}
val priority = prioritizer.priority(samePackageDescriptors)
val priority = prioritizer.priority(descriptors)
DescriptorGroupPrioritizer.VariantWithPriority(variant, priority)
}
.sortedBy {
@@ -56,6 +56,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.KtPsiUtil.isSelectorInQualified
@@ -64,7 +65,6 @@ import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
@@ -91,8 +91,8 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(project).modificationCount
private val suggestionCount: Int by CachedValueProperty(
calculator = { computeSuggestions().size },
protected val suggestions: Collection<FqName> by CachedValueProperty(
calculator = { computeSuggestions() },
timestampCalculator = { PsiModificationTracker.SERVICE.getInstance(project).modificationCount }
)
@@ -108,7 +108,7 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
if (ApplicationManager.getApplication().isUnitTestMode && HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) return false
if (suggestionCount == 0) return false
if (suggestions.isEmpty()) return false
return createAction(project, editor, element).showHint()
}
@@ -118,7 +118,7 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
override fun getFamilyName() = KotlinBundle.message("import.fix")
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile)
= element != null && super.isAvailable(project, editor, file) && suggestionCount > 0
= element != null && super.isAvailable(project, editor, file) && suggestions.isNotEmpty()
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val element = element ?: return
@@ -132,10 +132,10 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
private fun isOutdated() = modificationCountOnCreate != PsiModificationTracker.SERVICE.getInstance(project).modificationCount
protected open fun createAction(project: Project, editor: Editor, element: KtExpression): KotlinAddImportAction {
return createSingleImportAction(project, editor, element, computeSuggestions())
return createSingleImportAction(project, editor, element, suggestions)
}
fun computeSuggestions(): Collection<DeclarationDescriptor> {
fun computeSuggestions(): Collection<FqName> {
val element = element ?: return emptyList()
if (!element.isValid) return emptyList()
if (element.containingFile !is KtFile) return emptyList()
@@ -146,9 +146,11 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
if (importNames.isEmpty()) return emptyList()
return importNames.flatMapTo(LinkedHashSet()) {
computeSuggestionsForName(it, callTypeAndReceiver)
}
return importNames
.flatMap { computeSuggestionsForName(it, callTypeAndReceiver) }
.distinct()
.map { it.fqNameSafe }
.distinct()
}
private fun computeSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>): Collection<DeclarationDescriptor> {
@@ -323,7 +325,7 @@ internal class DelegateAccessorsImportFix(
override fun createAction(project: Project, editor: Editor, element: KtExpression): KotlinAddImportAction {
if (solveSeveralProblems) {
return createGroupedImportsAction(project, editor, element, "Delegate accessors", computeSuggestions())
return createGroupedImportsAction(project, editor, element, "Delegate accessors", suggestions)
}
return super.createAction(project, editor, element)
@@ -364,7 +366,7 @@ internal class ComponentsImportFix(
override fun createAction(project: Project, editor: Editor, element: KtExpression): KotlinAddImportAction {
if (solveSeveralProblems) {
return createGroupedImportsAction(project, editor, element, "Component functions", computeSuggestions())
return createGroupedImportsAction(project, editor, element, "Component functions", suggestions)
}
return super.createAction(project, editor, element)
@@ -26,8 +26,8 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.actions.createSingleImportAction
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference
import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
@@ -88,11 +88,11 @@ class KotlinReferenceImporter : ReferenceImporter {
if (mainReference.resolveToDescriptors(bindingContext).isNotEmpty()) return false
val suggestions = ImportFix(this).computeSuggestions()
if (suggestions.distinctBy { it.importableFqName!! }.size != 1) return false
if (suggestions.size != 1) return false
val descriptors = file.resolveImportReference(suggestions.single())
// we do not auto-import nested classes because this will probably add qualification into the text and this will confuse the user
if (suggestions.any { it is ClassDescriptor && it.containingDeclaration is ClassDescriptor }) return false
if (descriptors.any { it is ClassDescriptor && it.containingDeclaration is ClassDescriptor }) return false
var result = false
CommandProcessor.getInstance().runUndoTransparentAction {