Dumb Mode
What is Dumb Mode?
When a project is opened or significant file changes occur, Consulo updates its indexes in the background. During this indexing phase the IDE enters dumb mode, a state where most functionality that depends on indexes is unavailable. Only features that are explicitly marked as safe to use during indexing (by implementing DumbAware) remain active.
Dumb mode starts and ends inside a write action, which means that if you are inside a ReadAction on a background thread, dumb mode will not begin or end in the middle of your operation. However, every time you start a new top-level read action on a background thread, the dumb mode state may have changed.
DumbService
DumbService
is a project-level service annotated with @ServiceAPI(ComponentScope.PROJECT) that manages the dumb mode lifecycle and provides utilities for working with it.
Obtaining the Service
DumbService dumbService = DumbService.getInstance(project);
Checking Dumb Mode Status
Use isDumb() to check whether the project is currently in dumb mode. To avoid race conditions, call this method either on the EDT or inside a read action.
if (dumbService.isDumb()) {
// indexes are currently being updated
}
A static convenience method is also available:
if (DumbService.isDumb(project)) {
// ...
}
Running Code When Smart Mode Returns
runWhenSmart() schedules a Runnable to execute on the EDT as soon as the project is initialized and dumb mode is over. If the project is already in smart mode, the runnable may execute immediately.
DumbService.getInstance(project).runWhenSmart(() -> {
// indexes are available here
refreshReferences();
});
Note: There is no guarantee that dumb mode will not start again during the execution of the runnable. Your code should handle that situation explicitly.
Running a Read Action in Smart Mode
runReadActionInSmartMode() pauses the current thread until dumb mode ends, then executes the given code inside a read action where indexes are guaranteed to be available.
// Runnable variant
DumbService.getInstance(project).runReadActionInSmartMode(() -> {
PsiClass psiClass = findClass(qualifiedName);
// work with psiClass
});
// Supplier variant -- returns a value
PsiClass result = DumbService.getInstance(project).runReadActionInSmartMode(() -> {
return findClass(qualifiedName);
});
If this method is called when read access is already held, the runnable executes immediately (since waiting would cause a deadlock). In that case, an IndexNotReadyException may be thrown if dumb mode is active.
Waiting for Smart Mode
waitForSmartMode() blocks the current thread until dumb mode ends. Prefer runWhenSmart() or runReadActionInSmartMode() over this method, because there is no guarantee that dumb mode will not begin again before your next statement executes.
dumbService.waitForSmartMode();
// no guarantee that we are still in smart mode here
Smart Invocation on EDT
smartInvokeLater() posts a Runnable to the EDT that will only execute when the IDE leaves dumb mode. If the project is disposed during dumb mode, the runnable is discarded.
dumbService.smartInvokeLater(() -> {
// runs on EDT when smart mode is active
updateUI();
});
// Variant with explicit modality state
dumbService.smartInvokeLater(() -> {
updateUI();
}, ModalityState.defaultModalityState());
Filtering Extensions by Dumb-Awareness
During dumb mode, you may need to filter a list of extensions to only those that are safe to use:
List<MyExtension> safeExtensions = dumbService.filterByDumbAwareness(allExtensions);
A static convenience method is also available:
List<MyExtension> safeExtensions = DumbService.getDumbAwareExtensions(project, MY_EXTENSION_POINT);
Checking if an Object is Dumb-Aware
if (DumbService.isDumbAware(someObject)) {
// safe to use during indexing
}
This returns true if the object implements DumbAware, or if it implements PossiblyDumbAware and its isDumbAware() method returns true.
DumbAware Marker Interface
DumbAware
is a marker interface in the consulo.application.dumb package. Classes that implement this interface declare that they can operate safely during dumb mode -- that is, without relying on indexes.
public class MyAction extends AnAction implements DumbAware {
@Override
public void actionPerformed(@Nonnull AnActionEvent e) {
// this action is available even while indexes are being built
}
}
Common types that can implement DumbAware include:
- Actions
- File editor providers
- Post-startup activities
- Completion contributors
- Annotators
- Line marker providers
- Local inspection tools
- Tool window factories
When implementing DumbAware, you must ensure that your code does not call any API that requires indexes (e.g., reference resolution, class lookup by name) unless you handle IndexNotReadyException explicitly.
DumbModeTask
DumbModeTask
is an abstract class for tasks that should execute during dumb mode. These tasks are queued via DumbService.queueTask() and run sequentially in the background while indexes are being built.
Creating a DumbModeTask
Extend DumbModeTask and implement performInDumbMode():
public class MyIndexingTask extends DumbModeTask {
@Override
public void performInDumbMode(@Nonnull ProgressIndicator indicator, Exception trace) {
indicator.setTextValue(LocalizeValue.localizeTODO("Updating custom index..."));
indicator.setIndeterminate(false);
for (int i = 0; i < items.size(); i++) {
indicator.checkCanceled();
indicator.setFraction((double) i / items.size());
processItem(items.get(i));
}
}
@Override
public void dispose() {
// clean up resources if needed
}
}
Queueing and Cancelling
Queue the task through DumbService:
DumbService.getInstance(project).queueTask(new MyIndexingTask());
If needed, you can cancel a previously queued task:
DumbService.getInstance(project).cancelTask(task);
Cancelling a running task cancels its ProgressIndicator, so the next checkCanceled() call inside performInDumbMode() will throw ProcessCanceledException.
Task Equivalence
DumbModeTask supports an equivalence mechanism to prevent duplicate tasks. If several equivalent tasks are queued at once, only one will execute.
By default, equivalence is determined by object identity (this). You can provide a custom equivalence object through the constructor:
public class MyIndexingTask extends DumbModeTask {
public MyIndexingTask() {
super("my-custom-index"); // equivalence key
}
@Override
public void performInDumbMode(@Nonnull ProgressIndicator indicator, Exception trace) {
// ...
}
}
Two tasks are considered equivalent if their equivalence objects are equal according to Object.equals().
Listening for Dumb Mode Transitions
To react to dumb mode changes, subscribe to
DumbModeListener
on the project message bus. This topic is annotated with @TopicAPI(ComponentScope.PROJECT).
project.getMessageBus().connect(disposable).subscribe(DumbModeListener.class, new DumbModeListener() {
@Override
public void enteredDumbMode() {
// indexes are now being updated -- disable index-dependent features
}
@Override
public void exitDumbMode() {
// indexes are ready -- re-enable features
}
});
Both callbacks are delivered on the EDT.
Suspending Indexing
DumbService allows temporarily suspending indexing for heavy activities that should not compete with the indexer for CPU time:
dumbService.suspendIndexingAndRun(LocalizeValue.localizeTODO("Running build"), () -> {
// indexing is paused while this runs
performBuild();
});
The user can still manually resume indexing while the activity is running.
Alternative Resolution in Dumb Mode
In some cases (e.g., explicit Go To Declaration), it is beneficial to enable slower alternative resolve strategies that do not require indexes:
dumbService.withAlternativeResolveEnabled(() -> {
PsiReference ref = element.getReference();
PsiElement target = ref != null ? ref.resolve() : null;
// target may or may not be found -- IndexNotReadyException can still occur
});
Note: Even with alternative resolution enabled, methods like
resolve()andfindClass()may still throwIndexNotReadyException. Alternative resolution improves coverage but is not a complete substitute for index-based resolution.
Best Practices
- Implement
DumbAwareon actions and tool windows that do not need indexes, so they remain available during indexing. - Use
runReadActionInSmartMode()when you need guaranteed index access on a background thread. - Use
runWhenSmart()to schedule UI updates that depend on indexes. - Call
checkCanceled()frequently insideDumbModeTask.performInDumbMode()to allow prompt cancellation. - Do not assume that smart mode will persist across multiple statements -- always access indexes inside a read action or use
runReadActionInSmartMode().