From fa0e3feb12b1737fbd72918caafd72abcff20823 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 1 Sep 2025 19:45:28 +0200 Subject: [PATCH] IJPL-203550 platform: fix cancellation from "Find in Path" popup 'FindAndReplaceExecutorImpl' is using 'Job.cancel' to stop an ongoing 'FindInProjectUtil.findUsages' task. 'FindInProjectUtil' sets an additional EmptyProjectIndicator into the context, that never gets canceled. Without the 'Job' propagation in the 'FilesScanExecutor', only the 'main' thread gets canceled, leaving the pooled workers running. (cherry picked from commit 65f7029836f92770cebfa38632ca95418c5ecd24) IJ-CR-176933 GitOrigin-RevId: 4bb055f1ef74c4b6589991e52bc1778a6695f4c5 --- .../find/backend/src/FindRemoteApiImpl.kt | 47 ++++++++++--------- .../openapi/roots/impl/FilesScanExecutor.kt | 2 +- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/platform/find/backend/src/FindRemoteApiImpl.kt b/platform/find/backend/src/FindRemoteApiImpl.kt index 93256f5a0d27..3666ae1d0b3c 100644 --- a/platform/find/backend/src/FindRemoteApiImpl.kt +++ b/platform/find/backend/src/FindRemoteApiImpl.kt @@ -18,6 +18,7 @@ import com.intellij.ide.vfs.virtualFile import com.intellij.openapi.application.readAction import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.progress.coroutineToIndicator import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.newvfs.VfsPresentationUtil import com.intellij.platform.find.FindInFilesResult @@ -56,25 +57,26 @@ internal class FindRemoteApiImpl : FindRemoteApi { setCustomScopeById(project, findModel) //read action is necessary in case of the loading from a directory val scope = readAction { FindInProjectUtil.getGlobalSearchScope(project, findModel) } - FindInProjectUtil.findUsages(findModel, project, presentation, filesToScanInitially) { usageInfo -> - val virtualFile = usageInfo.virtualFile - if (virtualFile == null) - return@findUsages true + coroutineToIndicator { + FindInProjectUtil.findUsages(findModel, project, presentation, filesToScanInitially) { usageInfo -> + val virtualFile = usageInfo.virtualFile + if (virtualFile == null) + return@findUsages true - if (sentItems.get() >= maxUsagesCount) { - return@findUsages false - } + if (sentItems.get() >= maxUsagesCount) { + return@findUsages false + } - val adapter = UsageInfo2UsageAdapter(usageInfo) - val previousItem: UsageInfo2UsageAdapter? = previousResult.get() - if (!isReplaceState && previousItem != null) adapter.merge(previousItem) - previousResult.set(adapter) - adapter.updateCachedPresentation() - val textChunks = adapter.text.map { - it.toSerializableTextChunk() - } - val bgColor = VfsPresentationUtil.getFileBackgroundColor(project, virtualFile)?.rpcId() - val presentablePath = getPresentableFilePath(project, scope, virtualFile) + val adapter = UsageInfo2UsageAdapter(usageInfo) + val previousItem: UsageInfo2UsageAdapter? = previousResult.get() + if (!isReplaceState && previousItem != null) adapter.merge(previousItem) + previousResult.set(adapter) + adapter.updateCachedPresentation() + val textChunks = adapter.text.map { + it.toSerializableTextChunk() + } + val bgColor = VfsPresentationUtil.getFileBackgroundColor(project, virtualFile)?.rpcId() + val presentablePath = getPresentableFilePath(project, scope, virtualFile) val result = FindInFilesResult( presentation = textChunks, @@ -94,12 +96,13 @@ internal class FindRemoteApiImpl : FindRemoteApi { usageInfos = adapter.mergedInfos.toList() ) - val sent = trySend(result) - if (sent.isSuccess) { - sentItems.incrementAndGet() - } + val sent = trySend(result) + if (sent.isSuccess) { + sentItems.incrementAndGet() + } - sentItems.get() <= maxUsagesCount + sentItems.get() <= maxUsagesCount + } } }.buffer(capacity = maxUsagesCount) } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/FilesScanExecutor.kt b/platform/lang-impl/src/com/intellij/openapi/roots/impl/FilesScanExecutor.kt index 7280dae314bc..0c7e14251fad 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/FilesScanExecutor.kt +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/FilesScanExecutor.kt @@ -31,7 +31,6 @@ import com.intellij.util.indexing.roots.kind.LibraryOrigin import com.intellij.util.indexing.roots.kind.SdkOrigin import com.intellij.util.indexing.roots.kind.SyntheticLibraryOrigin import com.intellij.util.indexing.roots.origin.ExternalEntityOrigin -import kotlinx.coroutines.CancellationException import org.jetbrains.annotations.ApiStatus.Internal import java.util.concurrent.ConcurrentLinkedDeque import java.util.concurrent.Future @@ -54,6 +53,7 @@ object FilesScanExecutor { } val results = ArrayList>() for (i in 0 until THREAD_COUNT) { + // The current Job cancellation will NOT stop the pooled threads. Use 'coroutineToIndicator'. results.add(ourExecutor.submit { ProgressManager.getInstance().runProcess(runnable, ProgressWrapper.wrap(progress)) }) }