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:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user