Reworked resolve references in background thread in PlainTextPasteImportResolver

Relates to #KT-16774
This commit is contained in:
Vladimir Dolzhenko
2019-11-24 12:21:59 +01:00
parent 37371edb8c
commit 29b25e3901
@@ -44,6 +44,8 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
private val project = targetFile.project private val project = targetFile.project
private val importList = file.importList!! private val importList = file.importList!!
// keep access to deprecated PsiElementFactory.SERVICE for bwc with <= 191
private val psiElementFactory = PsiElementFactory.SERVICE.getInstance(project) private val psiElementFactory = PsiElementFactory.SERVICE.getInstance(project)
private val bindingContext by lazy { targetFile.analyzeWithContent() } private val bindingContext by lazy { targetFile.analyzeWithContent() }
@@ -64,17 +66,17 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
&& descriptor.isVisible(targetFile, null, bindingContext, resolutionFacade) && descriptor.isVisible(targetFile, null, bindingContext, resolutionFacade)
} }
private fun addImport(addImportData: AddImportData) { private fun addImport(importStatement: PsiImportStatementBase, shouldAddToTarget: Boolean = false) {
importList.add(addImportData.importStatement) importList.add(importStatement)
if (addImportData.shouldAddToTarget) if (shouldAddToTarget)
addedImports.add(addImportData.importStatement) addedImports.add(importStatement)
} }
fun addImportsFromTargetFile() { fun addImportsFromTargetFile() {
if (importList in dataForConversion.elementsAndTexts.toList()) return if (importList in dataForConversion.elementsAndTexts.toList()) return
val task = { val task = {
val addImportList = mutableListOf<AddImportData>() val addImportList = mutableListOf<PsiImportStatementBase>()
fun tryConvertKotlinImport(importDirective: KtImportDirective) { fun tryConvertKotlinImport(importDirective: KtImportDirective) {
val importPath = importDirective.importPath val importPath = importDirective.importPath
@@ -98,27 +100,19 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
if (importPath.isAllUnder) { if (importPath.isAllUnder) {
when { when {
isClassReceiver -> isClassReceiver ->
addImportList.add( addImportList.add(psiElementFactory.createImportStaticStatement(receiver as PsiClass, "*"))
AddImportData(psiElementFactory.createImportStaticStatement(receiver as PsiClass, "*"))
)
isPackageReceiver -> isPackageReceiver ->
addImportList.add( addImportList.add(psiElementFactory.createImportStatementOnDemand((receiver as PsiPackage).qualifiedName))
AddImportData(psiElementFactory.createImportStatementOnDemand((receiver as PsiPackage).qualifiedName))
)
} }
} else { } else {
when { when {
isClassSelector -> isClassSelector ->
addImportList.add( addImportList.add(psiElementFactory.createImportStatement(selector as PsiClass))
AddImportData(psiElementFactory.createImportStatement(selector as PsiClass))
)
isClassReceiver -> isClassReceiver ->
addImportList.add( addImportList.add(
AddImportData( psiElementFactory.createImportStaticStatement(
psiElementFactory.createImportStaticStatement( receiver as PsiClass,
receiver as PsiClass, importPath.importedName!!.asString()
importPath.importedName!!.asString()
)
) )
) )
} }
@@ -144,63 +138,69 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
) )
} }
private data class AddImportData(val importStatement: PsiImportStatementBase, val shouldAddToTarget: Boolean = false)
fun tryResolveReferences() { fun tryResolveReferences() {
val task = { val task = {
val addImportList = mutableListOf<AddImportData>() fun performWriteAction(block: () -> Unit) {
ApplicationManager.getApplication().invokeAndWait { runWriteAction { block() } }
}
fun tryResolveReference(reference: PsiQualifiedReference): Boolean { fun tryResolveReference(reference: PsiQualifiedReference): Boolean {
if (reference.resolve() != null) return true if (runReadAction { reference.resolve() } != null) return true
val referenceName = reference.referenceName ?: return false val referenceName = runReadAction { reference.referenceName } ?: return false
if (referenceName in failedToResolveReferenceNames) return false if (referenceName in failedToResolveReferenceNames) return false
if (reference.qualifier != null) return false if (runReadAction { reference.qualifier } != null) return false
val classes = shortNameCache.getClassesByName(referenceName, scope)
.mapNotNull { psiClass -> val classes = runReadAction {
val containingFile = psiClass.containingFile shortNameCache.getClassesByName(referenceName, scope)
if (ProjectRootsUtil.isInProjectOrLibraryContent(containingFile)) { .mapNotNull { psiClass ->
psiClass to psiClass.getJavaMemberDescriptor() as? ClassDescriptor val containingFile = psiClass.containingFile
} else null if (ProjectRootsUtil.isInProjectOrLibraryContent(containingFile)) {
}.filter { canBeImported(it.second) } psiClass to psiClass.getJavaMemberDescriptor() as? ClassDescriptor
} else null
}.filter { canBeImported(it.second) }
}
classes.find { (_, descriptor) -> classes.find { (_, descriptor) ->
JavaToKotlinClassMap.mapPlatformClass(descriptor!!).isNotEmpty() JavaToKotlinClassMap.mapPlatformClass(descriptor!!).isNotEmpty()
}?.let { (psiClass, _) -> }?.let { (psiClass, _) ->
addImportList.add(AddImportData(psiElementFactory.createImportStatement(psiClass))) performWriteAction { addImport(psiElementFactory.createImportStatement(psiClass)) }
} }
if (reference.resolve() != null) return true if (runReadAction { reference.resolve() } != null) return true
classes.singleOrNull()?.let { (psiClass, _) -> classes.singleOrNull()?.let { (psiClass, _) ->
addImportList.add(AddImportData(psiElementFactory.createImportStatement(psiClass), true)) performWriteAction { addImport(psiElementFactory.createImportStatement(psiClass), true) }
} }
when { when {
reference.resolve() != null -> return true runReadAction { reference.resolve() } != null -> return true
classes.isNotEmpty() -> { classes.isNotEmpty() -> {
ambiguityInResolution = true ambiguityInResolution = true
return false return false
} }
} }
val members = (shortNameCache.getMethodsByName(referenceName, scope).asList() + val members = runReadAction {
shortNameCache.getFieldsByName(referenceName, scope).asList()) (shortNameCache.getMethodsByName(referenceName, scope).asList() +
.asSequence() shortNameCache.getFieldsByName(referenceName, scope).asList())
.map { it as PsiMember } .asSequence()
.filter { it.getNullableModuleInfo() != null } .map { it as PsiMember }
.map { it to it.getJavaMemberDescriptor(resolutionFacade) as? DeclarationDescriptorWithVisibility } .filter { it.getNullableModuleInfo() != null }
.filter { canBeImported(it.second) } .map { it to it.getJavaMemberDescriptor(resolutionFacade) as? DeclarationDescriptorWithVisibility }
.toList() .filter { canBeImported(it.second) }
.toList()
}
members.singleOrNull()?.let { (psiMember, _) -> members.singleOrNull()?.let { (psiMember, _) ->
addImportList.add( performWriteAction {
AddImportData( addImport(
psiElementFactory.createImportStaticStatement(psiMember.containingClass!!, psiMember.name!!), psiElementFactory.createImportStaticStatement(psiMember.containingClass!!, psiMember.name!!),
true true
) )
) }
} }
when { when {
reference.resolve() != null -> return false runReadAction { reference.resolve() } != null -> return false
members.isNotEmpty() -> ambiguityInResolution = true members.isNotEmpty() -> ambiguityInResolution = true
else -> couldNotResolve = true else -> couldNotResolve = true
} }
@@ -220,17 +220,9 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers
reversed.forEachIndexed { index, value -> reversed.forEachIndexed { index, value ->
ProgressManager.getInstance().progressIndicator?.fraction = 1.0 * index / reversed.size ProgressManager.getInstance().progressIndicator?.fraction = 1.0 * index / reversed.size
val reference = value.reference as PsiQualifiedReference val reference = value.reference as PsiQualifiedReference
addImportList.clear() if (!tryResolveReference(reference)) {
val tryResolveReference = runReadAction { tryResolveReference(reference) } runReadAction { reference.referenceName }?.let {
if (!tryResolveReference) { failedToResolveReferenceNames += it
ApplicationManager.getApplication().invokeAndWait {
runWriteAction {
addImportList.forEach { addImport(it) }
val referenceName = reference.referenceName
if (referenceName != null) {
failedToResolveReferenceNames += referenceName
}
}
} }
} }
} }