diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt index d252c869517..ed61d86ee1b 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/KotlinConstraintSystemCompleter.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem @@ -302,6 +303,7 @@ class KotlinConstraintSystemCompleter( analyze: (PostponedResolvedAtom) -> Unit ): Boolean { for (argument in getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)) { + ProgressIndicatorAndCompilationCanceledStatus.checkCanceled() if (canWeAnalyzeIt(c, argument)) { analyze(argument) return true diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt index c46694dd367..f8c249192c8 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt @@ -43,4 +43,10 @@ fun Project.executeCommand(name: String, groupId: Any? = null, command: () - return result as T } -fun runWithCancellationCheck(block: () -> T): T = block() \ No newline at end of file +fun runWithCancellationCheck(block: () -> T): T = block() + +inline fun invokeLater(crossinline action: () -> Unit) { + ApplicationManager.getApplication().invokeLater { action() } +} + +inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.192 b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.192 index 05b6e75addb..6c2ded8c6b2 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.192 +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.192 @@ -46,4 +46,10 @@ fun Project.executeCommand(name: String, groupId: Any? = null, command: () - return result as T } -fun runWithCancellationCheck(block: () -> T): T = block() \ No newline at end of file +fun runWithCancellationCheck(block: () -> T): T = block() + +inline fun invokeLater(crossinline action: () -> Unit) { + ApplicationManager.getApplication().invokeLater { action() } +} + +inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.201 b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.201 index 39b5a4b7375..12557bf3978 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.201 +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.201 @@ -44,4 +44,10 @@ fun Project.executeCommand(name: String, groupId: Any? = null, command: () - return result as T } -fun runWithCancellationCheck(block: () -> T): T = CancellationCheck.runWithCancellationCheck(block) \ No newline at end of file +fun runWithCancellationCheck(block: () -> T): T = CancellationCheck.runWithCancellationCheck(block) + +inline fun invokeLater(crossinline action: () -> Unit) { + ApplicationManager.getApplication().invokeLater { action() } +} + +inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.as40 b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.as40 index 05b6e75addb..6c2ded8c6b2 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.as40 +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/ApplicationUtils.kt.as40 @@ -46,4 +46,10 @@ fun Project.executeCommand(name: String, groupId: Any? = null, command: () - return result as T } -fun runWithCancellationCheck(block: () -> T): T = block() \ No newline at end of file +fun runWithCancellationCheck(block: () -> T): T = block() + +inline fun invokeLater(crossinline action: () -> Unit) { + ApplicationManager.getApplication().invokeLater { action() } +} + +inline fun isUnitTestMode(): Boolean = ApplicationManager.getApplication().isUnitTestMode \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt index 01f7407f8df..f9315ab6307 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.codeInsight import com.intellij.codeInsight.CodeInsightSettings import com.intellij.codeInsight.editorActions.CopyPastePostProcessor import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ReadAction.nonBlocking import com.intellij.openapi.diagnostic.ControlFlowException import com.intellij.openapi.diagnostic.Logger @@ -54,6 +53,9 @@ import org.jetbrains.kotlin.idea.references.* import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.ProgressIndicatorUtils import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.idea.util.application.invokeLater +import org.jetbrains.kotlin.idea.util.application.isUnitTestMode +import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.getFileResolutionScope import org.jetbrains.kotlin.idea.util.getSourceRoot import org.jetbrains.kotlin.incremental.components.NoLookupLocation @@ -77,7 +79,6 @@ import java.awt.datatransfer.Transferable import java.awt.datatransfer.UnsupportedFlavorException import java.io.IOException import java.util.* -import java.util.function.Consumer class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor() { @@ -132,15 +133,17 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor = collectReferenceData( - file, - textRanges.map { it.startOffset }.toIntArray(), - textRanges.map { it.endOffset }.toIntArray(), - fakePackageName, - sourcePackageName, - targetFile.packageDirective?.fqName?.asString() ?: "" + file = file, + startOffsets = textRanges.map { it.startOffset }.toIntArray(), + endOffsets = textRanges.map { it.endOffset }.toIntArray(), + fakePackageName = fakePackageName, + sourcePackageName = sourcePackageName, + targetPackageName = runReadAction { targetFile.packageDirective?.fqName?.asString() ?: "" }, + indicator = indicator ) fun collectReferenceData( @@ -149,24 +152,30 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { val ranges = toTextRanges(startOffsets, endOffsets) - val elementsByRange = ranges.associateBy({ it }, { textRange -> - file.elementsInRange(textRange).filter { it is KtElement || it is KDocElement } - }) + val elements = ranges.flatMap { textRange -> + runReadAction { + indicator?.checkCanceled() + file.elementsInRange(textRange).filter { it is KtElement || it is KDocElement } + } + } - val allElementsToResolve = elementsByRange.values.flatten().flatMap { it.collectDescendantsOfType() } + val allElementsToResolve = elements.flatMap { it.collectDescendantsOfType() } // TODO: allowResolveInDispatchThread could be dropped as soon as // ConvertJavaCopyPasteProcessor will perform it on non UI thread - val bindingContext = + val bindingContext = runReadAction { allowResolveInDispatchThread { file.getResolutionFacade().analyze(allElementsToResolve, BodyResolveMode.PARTIAL) } + } val result = mutableListOf() - for ((range, elements) in elementsByRange) { - elements.forEachDescendant { ktElement -> + for (ktElement in allElementsToResolve) { + runReadAction { + indicator?.checkCanceled() result.addReferenceDataInsideElement( ktElement, file, ranges, bindingContext, fakePackageName = fakePackageName, sourcePackageName = sourcePackageName, targetPackageName = targetPackageName @@ -199,7 +208,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor) override fun processTransferableData( project: Project, @@ -273,21 +282,25 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor - val textRange = TextRange( - startOffsetDelta, - startOffsetDelta + originalTextRange.endOffset - originalTextRange.startOffset - ) - startOffsetDelta = startOffsetDelta + originalTextRange.endOffset - originalTextRange.startOffset + 1 - file.elementsInRange(textRange) - .filter { it is KtElement || it is KDocElement } - .map { - val range = TextRange( - originalTextRange.startOffset + it.textRange.startOffset - textRange.startOffset, - originalTextRange.startOffset + it.textRange.endOffset - textRange.startOffset - ) - PsiElementByTextRange(range, it) - } + val elementsByRange = runReadAction { + transferableData.textRanges.flatMap { originalTextRange -> + val elementsList = mutableListOf() + val textRange = TextRange( + startOffsetDelta, + startOffsetDelta + originalTextRange.endOffset - originalTextRange.startOffset + ) + startOffsetDelta = startOffsetDelta + originalTextRange.endOffset - originalTextRange.startOffset + 1 + file.elementsInRange(textRange) + .filter { it is KtElement || it is KDocElement } + .forEachDescendant { + val range = TextRange( + originalTextRange.startOffset + it.textRange.startOffset - textRange.startOffset, + originalTextRange.startOffset + it.textRange.endOffset - textRange.startOffset + ) + elementsList.add(PsiElementByTextRange(range, it.createSmartPointer())) + } + elementsList + } } processReferenceData(project, editor, file) { indicator: ProgressIndicator -> @@ -312,24 +325,15 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor List ) { - val task = object : Task.Modal(project, "Resolving pasted references ...", true) { + val task = object : Task.Backgroundable(project, "Resolving pasted references ...", true) { override fun run(indicator: ProgressIndicator) { assert(!ApplicationManager.getApplication().isWriteAccessAllowed) { "Resolving references on dispatch thread leads to live lock" } - ProgressIndicatorUtils.awaitWithCheckCanceled( - nonBlocking> { - return@nonBlocking findReferenceProvider(indicator) - } - .finishOnUiThread( - ModalityState.defaultModalityState(), - Consumer> { referencesPossibleToRestore -> - applyResolvedImports(project, referencesPossibleToRestore, file, editor) - } - ) - .withDocumentsCommitted(project) - .submit(AppExecutorUtil.getAppExecutorService()) - ) + + val referencesPossibleToRestore = findReferenceProvider(indicator) + + applyResolvedImports(project, referencesPossibleToRestore, file, editor) } } ProgressManager.getInstance().run(task) @@ -341,15 +345,17 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor() - restoreReferences(selectedReferencesToRestore, file, imported) + project.executeWriteCommand("resolve pasted references") { + val imported = TreeSet() + restoreReferences(selectedReferencesToRestore, file, imported) - reviewAddedImports(project, editor, file, imported) + reviewAddedImports(project, editor, file, imported) + } } } @@ -361,31 +367,33 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { if (file !is KtFile) return emptyList() - val referencesByRange = elementsByRange.flatMap { elementByTextRange -> - val element = elementByTextRange.element - if (!element.isValid) return@flatMap emptyList>() - val refElementsRanges = mutableListOf>() - val offsetDelta = elementByTextRange.originalTextRange.startOffset - element.textRange.startOffset - element.forEachDescendant { ktElement -> - indicator.checkCanceled() + val referencesByRange: Map = elementsByRange.mapNotNull { elementByTextRange -> + runReadAction { + val element = elementByTextRange.element.element ?: return@runReadAction null - ktElement.mainReference?.let { ref -> + val mainReference = element.mainReference + mainReference?.let { ref -> val textRange = ref.element.textRange - findReference(file, textRange)?.let { + val findReference = findReference(file, textRange) + findReference?.let { // remap reference to the original (as it was on paste phase) text range - val range = TextRange(textRange.startOffset + offsetDelta, textRange.endOffset + offsetDelta) - refElementsRanges.add(range to it) + val itTextRange = it.element.textRange + val refTextRange = ref.element.textRange + val originalTextRange = elementByTextRange.originalTextRange + val offset = originalTextRange.start - refTextRange.start + val range = TextRange(itTextRange.start + offset, itTextRange.end + offset) + range to it } } } - refElementsRanges }.toMap() val sourcePackageName = transferableData.packageName val imports: List = transferableData.imports + val project = runReadAction { file.project } // Step 0. Recreate original source file (i.e. source file as it was on copy action) and resolve references from it - val ctxFile = sourceFile(file.project, transferableData) ?: file + val ctxFile = sourceFile(project, transferableData) ?: file // put original source file to some fake package to avoid ambiguous resolution ( a real file VS a virtual file ) val fakePackageName = "__kotlin.__some.__funky.__package" @@ -398,12 +406,14 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor> { + return@nonBlocking findReferencesToRestore(file, indicator, sourceFileBasedReferences, referencesByRange) + } + .withDocumentsCommitted(project) + .cancelWith(indicator) + .submit(AppExecutorUtil.getAppExecutorService()) + ) } private fun buildDummySourceScope( @@ -448,19 +465,23 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor) = items.joinToString("\n") - val dummyImportsFile = KtPsiFactory(file.project) - .createAnalyzableFile( - "dummy-imports.kt", - "package $fakePkgName\n" + - "${joinLines(fakePkgImports)}\n" + - transferableData.sourceText, - ctxFile - ) + val dummyImportsFile = runReadAction { + KtPsiFactory(file.project) + .createAnalyzableFile( + "dummy-imports.kt", + "package $fakePkgName\n" + + "${joinLines(fakePkgImports)}\n" + + transferableData.sourceText, + ctxFile + ) + } - val dummyFileImports = dummyImportsFile.collectDescendantsOfType().mapNotNull { directive -> - val importedReference = - directive.importedReference?.getQualifiedElementSelector()?.mainReference?.resolve() as? KtNamedDeclaration - importedReference?.let { directive.text } + val dummyFileImports = runReadAction { + dummyImportsFile.collectDescendantsOfType().mapNotNull { directive -> + val importedReference = + directive.importedReference?.getQualifiedElementSelector()?.mainReference?.resolve() as? KtNamedDeclaration + importedReference?.let { directive.text } + } } val dummyFileImportsSet = dummyFileImports.toSet() @@ -476,12 +497,13 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor, referencesByPosition: Map - ): List = + ): List { // use already found reference candidates - so file could be changed - findReferences(file, referenceData.map { + return findReferences(file, referenceData.map { + indicator.checkCanceled() val textRange = TextRange(it.startOffset, it.endOffset) val reference = referencesByPosition[textRange] - it to if (reference?.element?.isValid == true) reference else null + it to reference }) + } private fun findReferences( file: PsiFile, @@ -526,15 +551,25 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { val ktFile = file as? KtFile ?: return listOf() - val bindingContext = try { - ktFile.getResolutionFacade().analyze(references.mapNotNull { it.second?.element }, BodyResolveMode.PARTIAL) - } catch (e: Throwable) { - if (e is ControlFlowException) throw e - LOG.error("Failed to analyze references after copy paste", e) - return emptyList() - } - val fileResolutionScope = ktFile.getResolutionFacade().getFileResolutionScope(ktFile) - return references.mapNotNull { pair -> + val resolutionFacade = runReadAction { ktFile.getResolutionFacade() } + val referencesList = mutableListOf>() + val bindingContext = + try { + runReadAction { + referencesList.addAll(references.mapNotNull { if (it.second?.element?.isValid == true) it.first to it.second else null }) + resolutionFacade.analyze( + referencesList.mapNotNull { it.second?.element }, + BodyResolveMode.PARTIAL + ) + } + } catch (e: Throwable) { + if (e is ControlFlowException) throw e + LOG.error("Failed to analyze references after copy paste", e) + return emptyList() + } + + val fileResolutionScope = runReadAction { resolutionFacade.getFileResolutionScope(ktFile) } + return referencesList.mapNotNull { pair -> val data = pair.first val reference = pair.second reference?.let { createReferenceToRestoreData(it, data, ktFile, fileResolutionScope, bindingContext) } @@ -545,19 +580,19 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { val element = reference.element - if (element is KtNameReferenceExpression && reference is KtSimpleNameReference) { - val classifierDescriptor = - bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] - (classifierDescriptor ?: bindingContext[BindingContext.REFERENCE_TARGET, element])?.let { return listOf(it) } - } + return runReadAction { + if (element.isValid && element is KtNameReferenceExpression && reference is KtSimpleNameReference) { + val classifierDescriptor = + bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, element] + (classifierDescriptor ?: bindingContext[BindingContext.REFERENCE_TARGET, element])?.let { return@runReadAction listOf(it) } + } - return reference.resolveToDescriptors(bindingContext).toList() + reference.resolveToDescriptors(bindingContext).toList() + } } private fun restoreReferences( @@ -644,9 +681,11 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { - return file.resolveImportReference(fqName).filterNot { - /*TODO: temporary hack until we don't have ability to insert qualified reference into root package*/ - DescriptorUtils.getParentOfType(it, PackageFragmentDescriptor::class.java)?.fqName?.isRoot ?: false + return runReadAction { + file.resolveImportReference(fqName).filterNot { + /*TODO: temporary hack until we don't have ability to insert qualified reference into root package*/ + DescriptorUtils.getParentOfType(it, PackageFragmentDescriptor::class.java)?.fqName?.isRoot ?: false + } } } @@ -667,7 +706,7 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { val fqNames = referencesToRestore.map { it.refData.fqName }.toSortedSet() - if (ApplicationManager.getApplication().isUnitTestMode) { + if (isUnitTestMode()) { declarationsToImportSuggested = fqNames } @@ -688,10 +727,10 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor): Boolean { - if (containingFile != fileCopiedFrom) return false - return textRanges.any { this.range in it } - } + private fun PsiElement.isInCopiedArea(fileCopiedFrom: KtFile, textRanges: List): Boolean = + runReadAction { + if (containingFile != fileCopiedFrom) false else textRanges.any { this.range in it } + } companion object { @get:TestOnly diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt index 5f84829b909..5ec3cfa97e0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt @@ -29,14 +29,12 @@ object ProgressIndicatorUtils { ): T = com.intellij.openapi.actionSystem.ex.ActionUtil.underModalProgress(project, progressTitle, computable) @JvmStatic - fun awaitWithCheckCanceled(future: Future) { - val indicator = - ProgressManager.getInstance().progressIndicator + fun awaitWithCheckCanceled(future: Future): T { + val indicator = ProgressManager.getInstance().progressIndicator while (true) { checkCancelledEvenWithPCEDisabled(indicator) try { - future[10, TimeUnit.MILLISECONDS] - return + return future[10, TimeUnit.MILLISECONDS] } catch (ignore: TimeoutException) { } catch (e: Throwable) { val cause = e.cause diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 index ea8de15894e..7ec156f4c29 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 +++ b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 @@ -36,12 +36,11 @@ object ProgressIndicatorUtils { } @JvmStatic - fun awaitWithCheckCanceled(future: Future) { + fun awaitWithCheckCanceled(future: Future): T { while (true) { ProgressManager.checkCanceled() try { - future.get(50, TimeUnit.MILLISECONDS) - return + return future.get(50, TimeUnit.MILLISECONDS) } catch (e: TimeoutException) { // ignore } catch (e: Exception) { diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 index af322b15e23..3fdc0aa8b2c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 +++ b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 @@ -1,10 +1,11 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlin.idea.util +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.DumbService @@ -14,8 +15,12 @@ import com.intellij.openapi.util.ThrowableComputable import org.jetbrains.annotations.Nls import org.jetbrains.kotlin.idea.util.application.runReadAction import java.util.concurrent.Future +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException object ProgressIndicatorUtils { + private val LOG = Logger.getInstance(ProgressIndicatorUtils::class.java) + fun underModalProgress( project: Project, @Nls progressTitle: String, @@ -33,6 +38,17 @@ object ProgressIndicatorUtils { } @JvmStatic - fun awaitWithCheckCanceled(future: Future) = - com.intellij.openapi.progress.util.BackgroundTaskUtil.awaitWithCheckCanceled(future) + fun awaitWithCheckCanceled(future: Future): T { + while (true) { + ProgressManager.checkCanceled() + try { + return future.get(50, TimeUnit.MILLISECONDS) + } catch (e: TimeoutException) { + // ignore + } catch (e: Exception) { + LOG.warn(e) + throw ProcessCanceledException(e) + } + } + } } \ No newline at end of file