Fix ImportFixBase to compute suggestions not on EDT

This commit is contained in:
Simon Ogorodnik
2017-03-28 23:16:45 +03:00
committed by Simon Ogorodnik
parent 94b261d701
commit ddcff3c96d
2 changed files with 45 additions and 39 deletions
@@ -75,7 +75,6 @@ import org.jetbrains.kotlin.resolve.scopes.ExplicitImportsScope
import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScope import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScope
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.utils.CachedValueProperty
import java.util.* import java.util.*
/** /**
@@ -89,10 +88,11 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(project).modificationCount private val modificationCountOnCreate = PsiModificationTracker.SERVICE.getInstance(project).modificationCount
protected val suggestions: Collection<FqName> by CachedValueProperty( protected lateinit var suggestions: Collection<FqName>
calculator = { computeSuggestions() },
timestampCalculator = { PsiModificationTracker.SERVICE.getInstance(project).modificationCount } fun computeSuggestions() {
) suggestions = collectSuggestions()
}
protected open fun getSupportedErrors() = factory.supportedErrors protected open fun getSupportedErrors() = factory.supportedErrors
@@ -133,7 +133,7 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
return createSingleImportAction(project, editor, element, suggestions) return createSingleImportAction(project, editor, element, suggestions)
} }
fun computeSuggestions(): Collection<FqName> { fun collectSuggestions(): Collection<FqName> {
val element = element ?: return emptyList() val element = element ?: return emptyList()
if (!element.isValid) return emptyList() if (!element.isValid) return emptyList()
if (element.containingFile !is KtFile) return emptyList() if (element.containingFile !is KtFile) return emptyList()
@@ -145,13 +145,13 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
if (importNames.isEmpty()) return emptyList() if (importNames.isEmpty()) return emptyList()
return importNames return importNames
.flatMap { computeSuggestionsForName(it, callTypeAndReceiver) } .flatMap { collectSuggestionsForName(it, callTypeAndReceiver) }
.distinct() .distinct()
.map { it.fqNameSafe } .map { it.fqNameSafe }
.distinct() .distinct()
} }
private fun computeSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>): Collection<DeclarationDescriptor> { private fun collectSuggestionsForName(name: Name, callTypeAndReceiver: CallTypeAndReceiver<*, *>): Collection<DeclarationDescriptor> {
val element = element ?: return emptyList() val element = element ?: return emptyList()
val nameStr = name.asString() val nameStr = name.asString()
if (nameStr.isEmpty()) return emptyList() if (nameStr.isEmpty()) return emptyList()
@@ -222,7 +222,20 @@ internal abstract class ImportFixBase<T : KtExpression> protected constructor(
val supportedErrors: Collection<DiagnosticFactory<*>> by lazy { QuickFixes.getInstance().getDiagnostics(this) } val supportedErrors: Collection<DiagnosticFactory<*>> by lazy { QuickFixes.getInstance().getDiagnostics(this) }
override fun isApplicableForCodeFragment() = true override fun isApplicableForCodeFragment() = true
abstract fun createImportAction(diagnostic: Diagnostic): ImportFixBase<*>?
open fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<ImportFixBase<*>> = emptyList()
override final fun createAction(diagnostic: Diagnostic): IntentionAction? {
return createImportAction(diagnostic)?.apply { computeSuggestions() }
}
override final fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<IntentionAction> {
return createImportActionsForAllProblems(sameTypeDiagnostics).onEach { it.computeSuggestions() }
}
} }
} }
internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, factory: Factory) : ImportFixBase<T>(expression, factory) { internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, factory: Factory) : ImportFixBase<T>(expression, factory) {
@@ -319,10 +332,8 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
} }
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtSimpleNameExpression)?.let { (diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportFix)
ImportFix(it).apply { computeSuggestions() }
}
} }
} }
@@ -346,10 +357,8 @@ internal class ImportConstructorReferenceFix(expression: KtSimpleNameExpression)
override val importNames = element?.mainReference?.resolvesByNames ?: emptyList() override val importNames = element?.mainReference?.resolvesByNames ?: emptyList()
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtSimpleNameExpression)?.let { (diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportConstructorReferenceFix)
ImportConstructorReferenceFix(it).apply { computeSuggestions() }
}
} }
} }
@@ -359,10 +368,8 @@ internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase
override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.OPERATOR(it) } override fun getCallTypeAndReceiver() = element?.let { CallTypeAndReceiver.OPERATOR(it) }
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createAction(diagnostic: Diagnostic) = override fun createImportAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtExpression)?.let { (diagnostic.psiElement as? KtExpression)?.let(::InvokeImportFix)
InvokeImportFix(it).apply { computeSuggestions() }
}
} }
} }
@@ -385,13 +392,13 @@ internal open class ArrayAccessorImportFix(
} }
} }
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtArrayAccessExpression>? { override fun createImportAction(diagnostic: Diagnostic): ArrayAccessorImportFix? {
val factory = diagnostic.factory val factory = diagnostic.factory
assert(factory == Errors.NO_GET_METHOD || factory == Errors.NO_SET_METHOD) assert(factory == Errors.NO_GET_METHOD || factory == Errors.NO_SET_METHOD)
val element = diagnostic.psiElement val element = diagnostic.psiElement
if (element is KtArrayAccessExpression && element.arrayExpression != null) { if (element is KtArrayAccessExpression && element.arrayExpression != null) {
return ArrayAccessorImportFix(element, listOf(importName(diagnostic)), true).apply { computeSuggestions() } return ArrayAccessorImportFix(element, listOf(importName(diagnostic)), true)
} }
return null return null
@@ -426,13 +433,13 @@ internal class DelegateAccessorsImportFix(
}.distinct() }.distinct()
} }
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtExpression>? { override fun createImportAction(diagnostic: Diagnostic) =
return (diagnostic.psiElement as? KtExpression)?.let { (diagnostic.psiElement as? KtExpression)?.let {
DelegateAccessorsImportFix(it, importNames(listOf(diagnostic)), false).apply { computeSuggestions() } DelegateAccessorsImportFix(it, importNames(listOf(diagnostic)), false)
} }
}
override fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<IntentionAction> {
override fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<DelegateAccessorsImportFix> {
val element = sameTypeDiagnostics.first().psiElement val element = sameTypeDiagnostics.first().psiElement
val names = importNames(sameTypeDiagnostics) val names = importNames(sameTypeDiagnostics)
return listOfNotNull((element as? KtExpression)?.let { DelegateAccessorsImportFix(it, names, true) }) return listOfNotNull((element as? KtExpression)?.let { DelegateAccessorsImportFix(it, names, true) })
@@ -460,13 +467,12 @@ internal class ComponentsImportFix(
private fun importNames(diagnostics: Collection<Diagnostic>) = private fun importNames(diagnostics: Collection<Diagnostic>) =
diagnostics.map { Name.identifier(Errors.COMPONENT_FUNCTION_MISSING.cast(it).a.identifier) } diagnostics.map { Name.identifier(Errors.COMPONENT_FUNCTION_MISSING.cast(it).a.identifier) }
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtExpression>? { override fun createImportAction(diagnostic: Diagnostic) =
return (diagnostic.psiElement as? KtExpression)?.let { (diagnostic.psiElement as? KtExpression)?.let {
ComponentsImportFix(it, importNames(listOf(diagnostic)), false).apply { computeSuggestions() } ComponentsImportFix(it, importNames(listOf(diagnostic)), false)
} }
}
override fun doCreateActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<IntentionAction> { override fun createImportActionsForAllProblems(sameTypeDiagnostics: Collection<Diagnostic>): List<ComponentsImportFix> {
val element = sameTypeDiagnostics.first().psiElement val element = sameTypeDiagnostics.first().psiElement
val names = importNames(sameTypeDiagnostics) val names = importNames(sameTypeDiagnostics)
val solveSeveralProblems = sameTypeDiagnostics.size > 1 val solveSeveralProblems = sameTypeDiagnostics.size > 1
@@ -552,17 +558,17 @@ internal class ImportForMismatchingArgumentsFix(
} }
companion object MyFactory : Factory() { companion object MyFactory : Factory() {
override fun createAction(diagnostic: Diagnostic): ImportForMismatchingArgumentsFix? { override fun createImportAction(diagnostic: Diagnostic): ImportForMismatchingArgumentsFix? {
//TODO: not only KtCallExpression //TODO: not only KtCallExpression
val callExpression = diagnostic.psiElement.getStrictParentOfType<KtCallExpression>() ?: return null val callExpression = diagnostic.psiElement.getStrictParentOfType<KtCallExpression>() ?: return null
val nameExpression = callExpression.calleeExpression as? KtNameReferenceExpression ?: return null val nameExpression = callExpression.calleeExpression as? KtNameReferenceExpression ?: return null
return ImportForMismatchingArgumentsFix(nameExpression).apply { computeSuggestions() } return ImportForMismatchingArgumentsFix(nameExpression)
} }
} }
} }
object ImportForMissingOperatorFactory : KotlinSingleIntentionActionFactory() { internal object ImportForMissingOperatorFactory : ImportFixBase.Factory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? { override fun createImportAction(diagnostic: Diagnostic): ImportFixBase<*>? {
val element = diagnostic.psiElement as? KtExpression ?: return null val element = diagnostic.psiElement as? KtExpression ?: return null
val operatorDescriptor = Errors.OPERATOR_MODIFIER_REQUIRED.cast(diagnostic).a val operatorDescriptor = Errors.OPERATOR_MODIFIER_REQUIRED.cast(diagnostic).a
val name = operatorDescriptor.name val name = operatorDescriptor.name
@@ -87,7 +87,7 @@ class KotlinReferenceImporter : ReferenceImporter {
val bindingContext = analyze(BodyResolveMode.PARTIAL) val bindingContext = analyze(BodyResolveMode.PARTIAL)
if (mainReference.resolveToDescriptors(bindingContext).isNotEmpty()) return false if (mainReference.resolveToDescriptors(bindingContext).isNotEmpty()) return false
val suggestions = ImportFix(this).computeSuggestions() val suggestions = ImportFix(this).collectSuggestions()
if (suggestions.size != 1) return false if (suggestions.size != 1) return false
val descriptors = file.resolveImportReference(suggestions.single()) val descriptors = file.resolveImportReference(suggestions.single())