diff --git a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesScannerExecutorImpl.kt b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesScannerExecutorImpl.kt index 6580aa1c38e0..1d48f1d33129 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesScannerExecutorImpl.kt +++ b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesScannerExecutorImpl.kt @@ -13,6 +13,7 @@ import com.intellij.openapi.progress.impl.ProgressSuspender import com.intellij.openapi.progress.util.PingProgress import com.intellij.openapi.project.* import com.intellij.openapi.util.NlsContexts.ProgressText +import com.intellij.openapi.util.registry.Registry import com.intellij.platform.util.coroutines.childScope import com.intellij.util.gist.GistManager import com.intellij.util.gist.GistManagerImpl @@ -22,6 +23,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import org.jetbrains.annotations.ApiStatus +import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.VisibleForTesting import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.locks.LockSupport @@ -29,6 +31,7 @@ import java.util.concurrent.locks.LockSupport @ApiStatus.Internal class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: CoroutineScope) : Disposable, UnindexedFilesScannerExecutor { + private val scanningWaitsForNonDumbModeOverride = MutableStateFlow(null) private val runningDumbTask = AtomicReference() // note that shouldShowProgressIndicator = false in UnindexedFilesScannerExecutor, so there is no suspender for the progress indicator @@ -115,6 +118,11 @@ class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: Corout } } + private fun scanningWaitsForNonDumbMode(override: Boolean?): Boolean = override ?: Registry.`is`("scanning.waits.for.non.dumb.mode", true) + + @VisibleForTesting + fun scanningWaitsForNonDumbMode(): Boolean = scanningWaitsForNonDumbMode(scanningWaitsForNonDumbModeOverride.value) + private suspend fun waitUntilNextTaskExecutionAllowed() { // wait until scanning is enabled var flow: Flow = scanningEnabled.combine(scanningTask) { enabled, scanningTask -> @@ -125,15 +133,25 @@ class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: Corout // For example, PythonLanguageLevelPusher.initExtra is invoked from RequiredForSmartModeActivity and may submit additional dumb tasks. // We want scanning to start after all these "extra" dumb tasks are finished. // Note that a project may become dumb immediately after the check. This is not a problem - we schedule scanning anyway. - if (UnindexedFilesScannerExecutor.scanningWaitsForNonDumbMode()) { - flow = flow.combine(DumbServiceImpl.getInstance(project).isDumbAsFlow) { shouldRun, isDumb -> - shouldRun && !isDumb + if (scanningWaitsForNonDumbMode()) { + flow = flow.combine( + // nested flow is needed because of negation (!shouldWaitForNonDumb) + DumbServiceImpl.getInstance(project).isDumbAsFlow.combine(scanningWaitsForNonDumbModeOverride) { isDumb, scanningWaitsCurrentValue -> + isDumb && scanningWaitsForNonDumbMode(scanningWaitsCurrentValue) + } + ) { shouldRun, shouldWaitForNonDumb -> + shouldRun && !shouldWaitForNonDumb } } flow.first { it } } + @TestOnly + fun overrideScanningWaitsForNonDumbMode(newValue: Boolean?) { + scanningWaitsForNonDumbModeOverride.value = newValue + } + private suspend fun runScanningTask(task: UnindexedFilesScanner) { val shouldShowProgress: StateFlow = if (task.shouldHideProgressInSmartMode()) { project.service().isDumbModeForScanningActive() @@ -277,5 +295,8 @@ class UnindexedFilesScannerExecutorImpl(private val project: Project, cs: Corout companion object { private val LOG = Logger.getInstance(UnindexedFilesScannerExecutor::class.java) + + @JvmStatic + fun getInstance(project: Project): UnindexedFilesScannerExecutorImpl = project.service() as UnindexedFilesScannerExecutorImpl } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/UnindexedFilesScannerExecutor.kt b/platform/platform-impl/src/com/intellij/openapi/project/UnindexedFilesScannerExecutor.kt index 70605727b8da..027cd1d038f7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/UnindexedFilesScannerExecutor.kt +++ b/platform/platform-impl/src/com/intellij/openapi/project/UnindexedFilesScannerExecutor.kt @@ -3,7 +3,6 @@ package com.intellij.openapi.project import com.intellij.openapi.components.service import com.intellij.openapi.util.NlsContexts.ProgressText -import com.intellij.openapi.util.registry.Registry import kotlinx.collections.immutable.PersistentList import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow @@ -30,9 +29,6 @@ interface UnindexedFilesScannerExecutor { @JvmStatic fun shouldScanInSmartMode(): Boolean = true - @JvmStatic - fun scanningWaitsForNonDumbMode(): Boolean = Registry.`is`("scanning.waits.for.non.dumb.mode", true) - fun > unwrapTask(task: MergingTaskQueue.QueuedTask): T { return task.task } diff --git a/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt b/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt index 0c111530bb3f..aeabf8ddd079 100644 --- a/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt +++ b/platform/testFramework/src/com/intellij/testFramework/IndexingTestUtil.kt @@ -13,6 +13,7 @@ import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.project.UnindexedFilesScannerExecutor import com.intellij.openapi.util.Disposer import com.intellij.platform.util.coroutines.childScope +import com.intellij.util.indexing.UnindexedFilesScannerExecutorImpl import kotlinx.coroutines.* import org.junit.Assert import kotlin.time.Duration.Companion.seconds @@ -106,11 +107,11 @@ class IndexingTestUtil(private val project: Project) { dumbService.ensureInitialDumbTaskRequiredForSmartModeSubmitted() // TODO IJPL-578: don't submit - val scannerExecutor = UnindexedFilesScannerExecutor.getInstance(project) + val scannerExecutor = UnindexedFilesScannerExecutorImpl.getInstance(project) // Scheduled tasks will become a running tasks soon. To avoid a race, we check scheduled tasks first if (scannerExecutor.hasQueuedTasks) { - return if (UnindexedFilesScannerExecutor.scanningWaitsForNonDumbMode() && dumbService.isDumb) { + return if (scannerExecutor.scanningWaitsForNonDumbMode() && dumbService.isDumb) { val isEternal = DumbModeTestUtils.isEternalDumbTaskRunning(project) if (isEternal) { thisLogger().debug("Do not wait for queued scanning task, because eternal dumb task is running in the project [$project]") diff --git a/platform/testFramework/src/com/intellij/testFramework/TestIndexingModeSupporter.java b/platform/testFramework/src/com/intellij/testFramework/TestIndexingModeSupporter.java index b1b58805c793..c8cb818d88d4 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestIndexingModeSupporter.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestIndexingModeSupporter.java @@ -9,6 +9,7 @@ import com.intellij.openapi.util.RecursionManager; import com.intellij.testFramework.DumbModeTestUtils.EternalTaskShutdownToken; import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.indexing.UnindexedFilesScanner; +import com.intellij.util.indexing.UnindexedFilesScannerExecutorImpl; import junit.framework.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,6 +45,8 @@ public interface TestIndexingModeSupporter { public @NotNull ShutdownToken setUpTestInternal(@NotNull Project project, @NotNull Disposable testRootDisposable) { EternalTaskShutdownToken dumbTask = indexEverythingAndBecomeDumb(project); RecursionManager.disableMissedCacheAssertions(testRootDisposable); + // we don't want "waiting-for-non-dumb-mode" to pause tasks submitted from ensureIndexingStatus + UnindexedFilesScannerExecutorImpl.getInstance(project).overrideScanningWaitsForNonDumbMode(false); return new ShutdownToken(dumbTask); } @@ -57,6 +60,8 @@ public interface TestIndexingModeSupporter { public @NotNull ShutdownToken setUpTestInternal(@NotNull Project project, @NotNull Disposable testRootDisposable) { EternalTaskShutdownToken dumbTask = becomeDumb(project); RecursionManager.disableMissedCacheAssertions(testRootDisposable); + // we don't want "waiting-for-non-dumb-mode" to pause tasks submitted from ensureIndexingStatus + UnindexedFilesScannerExecutorImpl.getInstance(project).overrideScanningWaitsForNonDumbMode(false); return new ShutdownToken(dumbTask); } }, DUMB_EMPTY_INDEX { @@ -97,6 +102,9 @@ public interface TestIndexingModeSupporter { public void tearDownTest(@Nullable Project project, @NotNull ShutdownToken token) { if (token.dumbTask != null) { DumbModeTestUtils.endEternalDumbModeTaskAndWaitForSmartMode(project, token.dumbTask); + if (project != null) { + UnindexedFilesScannerExecutorImpl.getInstance(project).overrideScanningWaitsForNonDumbMode(null /* reset to default */); + } } }