diff --git a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/PlainTextPasteImportResolver.kt b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/PlainTextPasteImportResolver.kt index 34e390eb8f2..12ada08e309 100644 --- a/idea/src/org/jetbrains/kotlin/idea/conversion/copy/PlainTextPasteImportResolver.kt +++ b/idea/src/org/jetbrains/kotlin/idea/conversion/copy/PlainTextPasteImportResolver.kt @@ -38,7 +38,6 @@ import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtImportDirective import org.jetbrains.kotlin.psi.psiUtil.referenceExpression -import java.util.* class PlainTextPasteImportResolver(private val dataForConversion: DataForConversion, val targetFile: KtFile) { private val file = dataForConversion.file @@ -57,7 +56,7 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers private var ambiguityInResolution = false private var couldNotResolve = false - val addedImports = ArrayList() + val addedImports = mutableListOf() private fun canBeImported(descriptor: DeclarationDescriptorWithVisibility?): Boolean { return descriptor != null @@ -65,17 +64,17 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers && descriptor.isVisible(targetFile, null, bindingContext, resolutionFacade) } - private fun addImport(importStatement: PsiImportStatementBase, shouldAddToTarget: Boolean = false) { - importList.add(importStatement) - if (shouldAddToTarget) - addedImports.add(importStatement) + private fun addImport(addImportData: AddImportData) { + importList.add(addImportData.importStatement) + if (addImportData.shouldAddToTarget) + addedImports.add(addImportData.importStatement) } fun addImportsFromTargetFile() { if (importList in dataForConversion.elementsAndTexts.toList()) return val task = { - val addImportList = mutableListOf() + val addImportList = mutableListOf() fun tryConvertKotlinImport(importDirective: KtImportDirective) { val importPath = importDirective.importPath @@ -100,31 +99,26 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers when { isClassReceiver -> addImportList.add( - psiElementFactory.createImportStaticStatement( - receiver as PsiClass, - "*" - ) + AddImportData(psiElementFactory.createImportStaticStatement(receiver as PsiClass, "*")) ) isPackageReceiver -> addImportList.add( - psiElementFactory.createImportStatementOnDemand( - (receiver as PsiPackage).qualifiedName - ) + AddImportData(psiElementFactory.createImportStatementOnDemand((receiver as PsiPackage).qualifiedName)) ) } } else { when { isClassSelector -> addImportList.add( - psiElementFactory.createImportStatement( - selector as PsiClass - ) + AddImportData(psiElementFactory.createImportStatement(selector as PsiClass)) ) isClassReceiver -> addImportList.add( - psiElementFactory.createImportStaticStatement( - receiver as PsiClass, - importPath.importedName!!.asString() + AddImportData( + psiElementFactory.createImportStaticStatement( + receiver as PsiClass, + importPath.importedName!!.asString() + ) ) ) } @@ -150,76 +144,101 @@ class PlainTextPasteImportResolver(private val dataForConversion: DataForConvers ) } + private data class AddImportData(val importStatement: PsiImportStatementBase, val shouldAddToTarget: Boolean = false) + fun tryResolveReferences() { - val elementsWithUnresolvedRef = PsiTreeUtil.collectElements(file) { element -> - element.reference != null - && element.reference is PsiQualifiedReference - && element.reference?.resolve() == null - } + val task = { + val addImportList = mutableListOf() + fun tryResolveReference(reference: PsiQualifiedReference): Boolean { + if (reference.resolve() != null) return true + val referenceName = reference.referenceName ?: return false + if (referenceName in failedToResolveReferenceNames) return false + if (reference.qualifier != null) return false + val classes = shortNameCache.getClassesByName(referenceName, scope) + .mapNotNull { psiClass -> + val containingFile = psiClass.containingFile + if (ProjectRootsUtil.isInProjectOrLibraryContent(containingFile)) { + psiClass to psiClass.getJavaMemberDescriptor() as? ClassDescriptor + } else null + }.filter { canBeImported(it.second) } - fun tryResolveReference(reference: PsiQualifiedReference): Boolean { - if (reference.resolve() != null) return true - val referenceName = reference.referenceName ?: return false - if (referenceName in failedToResolveReferenceNames) return false - if (reference.qualifier != null) return false - val classes = shortNameCache.getClassesByName(referenceName, scope) - .mapNotNull { psiClass -> - val containingFile = psiClass.containingFile - if (ProjectRootsUtil.isInProjectOrLibraryContent(containingFile)) { - psiClass to psiClass.getJavaMemberDescriptor() as? ClassDescriptor - } else null - }.filter { canBeImported(it.second) } + classes.find { (_, descriptor) -> + JavaToKotlinClassMap.mapPlatformClass(descriptor!!).isNotEmpty() + }?.let { (psiClass, _) -> + addImportList.add(AddImportData(psiElementFactory.createImportStatement(psiClass))) + } + if (reference.resolve() != null) return true - classes.find { (_, descriptor) -> - JavaToKotlinClassMap.mapPlatformClass(descriptor!!).isNotEmpty() - }?.let { (psiClass, _) -> - addImport(psiElementFactory.createImportStatement(psiClass)) - } - if (reference.resolve() != null) return true + classes.singleOrNull()?.let { (psiClass, _) -> + addImportList.add(AddImportData(psiElementFactory.createImportStatement(psiClass), true)) + } - classes.singleOrNull()?.let { (psiClass, _) -> - addImport(psiElementFactory.createImportStatement(psiClass), true) + when { + reference.resolve() != null -> return true + classes.isNotEmpty() -> { + ambiguityInResolution = true + return false + } + } + + val members = (shortNameCache.getMethodsByName(referenceName, scope).asList() + + shortNameCache.getFieldsByName(referenceName, scope).asList()) + .asSequence() + .map { it as PsiMember } + .filter { it.getNullableModuleInfo() != null } + .map { it to it.getJavaMemberDescriptor(resolutionFacade) as? DeclarationDescriptorWithVisibility } + .filter { canBeImported(it.second) } + .toList() + + members.singleOrNull()?.let { (psiMember, _) -> + addImportList.add( + AddImportData( + psiElementFactory.createImportStaticStatement(psiMember.containingClass!!, psiMember.name!!), + true + ) + ) + } + + when { + reference.resolve() != null -> return false + members.isNotEmpty() -> ambiguityInResolution = true + else -> couldNotResolve = true + } + return false } - when { - reference.resolve() != null -> return true - classes.isNotEmpty() -> { - ambiguityInResolution = true - return false + + val elementsWithUnresolvedRef = runReadAction { + PsiTreeUtil.collectElements(file) { element -> + element.reference != null + && element.reference is PsiQualifiedReference + && element.reference?.resolve() == null } } - val members = (shortNameCache.getMethodsByName(referenceName, scope).asList() + - shortNameCache.getFieldsByName(referenceName, scope).asList()) - .asSequence() - .map { it as PsiMember } - .filter { it.getNullableModuleInfo() != null } - .map { it to it.getJavaMemberDescriptor(resolutionFacade) as? DeclarationDescriptorWithVisibility } - .filter { canBeImported(it.second) } - .toList() - - members.singleOrNull()?.let { (psiMember, _) -> - addImport(psiElementFactory.createImportStaticStatement(psiMember.containingClass!!, psiMember.name!!), true) - } - - when { - reference.resolve() != null -> return false - members.isNotEmpty() -> ambiguityInResolution = true - else -> couldNotResolve = true - } - return false - } - - runWriteAction { - elementsWithUnresolvedRef.reversed().forEach { - val reference = it.reference as PsiQualifiedReference - if (!tryResolveReference(reference)) { - val referenceName = reference.referenceName - if (referenceName != null) { - failedToResolveReferenceNames += referenceName + val reversed = elementsWithUnresolvedRef.reversedArray() + reversed.forEachIndexed { index, value -> + ProgressManager.getInstance().progressIndicator?.fraction = 1.0 * index / reversed.size + val reference = value.reference as PsiQualifiedReference + addImportList.clear() + val tryResolveReference = runReadAction { tryResolveReference(reference) } + if (!tryResolveReference) { + ApplicationManager.getApplication().invokeAndWait { + runWriteAction { + addImportList.forEach { addImport(it) } + val referenceName = reference.referenceName + if (referenceName != null) { + failedToResolveReferenceNames += referenceName + } + } } } } } + + ProgressManager.getInstance().runProcessWithProgressSynchronously( + task, "Resolving references ...", true, project + ) + } } \ No newline at end of file