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
This commit is contained in:
Aleksey Pivovarov
2025-09-01 19:45:28 +02:00
committed by intellij-monorepo-bot
parent a20d670e38
commit fa0e3feb12
2 changed files with 26 additions and 23 deletions

View File

@@ -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)
}

View File

@@ -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<Future<*>>()
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)) })
}