redhat-developer / redhat-developer/lsp4ij

Support LSP quick fixes and intention actions on non-project files by implementing `DumbAware` on `LSPLazyCodeActionIntentionAction`

Open
#1,668 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
344
Forks
113
Avg merge
5h 22m
Merged PRs (30d)
15

Description

Summary

LSP4IJ already supports diagnostics, folding, code lenses, documentation, and document links on non-project files (files outside content roots) by marking their respective passes/providers as DumbAware (LSPDiagnosticsPassFactory, LSPFoldingRangeBuilder, LSPCodeLensProvider, LSPDocumentationTargetProvider, LSPDocumentLinkPass).

However, LSP quick fixes (LSPQuickFixIntentionAction) and intention actions (LSPIntentionAction0..19) do not work on non-project files because their base class LSPLazyCodeActionIntentionAction does not implement com.intellij.openapi.project.DumbAware.

Root Cause

In IntelliJ Platform, both the intention popup (Alt+Enter via ShowIntentionsPass#addAvailableFixesForGroups and ShowIntentionsPass#getRegisteredIntentionActions) and the hover tooltip quick-fix button (DaemonTooltipActionProviderKt#extractMostPriorityFixFromHighlightInfo / DaemonTooltipAction#execute -> findIntention) filter actions using DumbService#isUsableInCurrentContext(Object thing, VirtualFile file):

fun isUsableInCurrentContext(thing: Any, file: VirtualFile?) : Boolean {
  if (file == null || !file.isInLocalFileSystem) return isUsableInCurrentContext(thing)
  return (!isDumb && FileIndexFacade.getInstance(project).isIndexable(file)) || isDumbAware(thing, project is LightEditCompatible)
}

For a non-project file (a file outside the project's content roots/libraries), FileIndexFacade.getInstance(project).isIndexable(file) returns false even when the IDE is in Smart Mode (!isDumb == true).

Because (!isDumb && isIndexable(file)) evaluates to false, IntelliJ requires isDumbAware(thing) to be true:

  • Prior to IntelliJ 2026.2 (IJPL-254242), extractMostPriorityFixFromHighlightInfo missed the isUsableInCurrentContext check, so LSP quick fixes were shown in hover tooltips on non-project files, but clicking them silently failed inside DaemonTooltipAction#execute -> findIntention (which called ShowIntentionsPass#getAvailableFixes -> isUsableInCurrentContext -> false).
  • In newer IntelliJ builds where extractMostPriorityFixFromHighlightInfo also checks isUsableInCurrentContext, non-DumbAware quick fixes and intentions are completely hidden on non-project files in both tooltips and Alt+Enter.

Proposed Solution

  1. Implement DumbAware on LSPLazyCodeActionIntentionAction:
    Since external language servers operate out-of-process and do not rely on IntelliJ's PSI indexes, LSPLazyCodeActionIntentionAction should implement DumbAware.

    • Note: For LSPIntentionAction0..19 registered via <intentionAction> in plugin.xml, IntelliJ wraps them in IntentionActionWrapper which implements IntentionActionDelegate (PossiblyDumbAware) and delegates isDumbAware() to DumbService.isDumbAware(getDelegate()). Thus, marking LSPLazyCodeActionIntentionAction as DumbAware automatically makes both LSPQuickFixIntentionAction and LSPIntentionAction0..19 dumb-aware.
  2. Guard against actual project indexing in LSPLazyCodeActionIntentionAction#isAvailable:
    LSPIntentionAction#isAvailable already checks ProjectIndexingManager.canExecuteLSPFeature(file) != ExecuteLSPFeatureStatus.NOW to avoid triggering LSP requests while the project is actually indexing (scanning || dumbIndexing || DumbService.isDumb(project)). Moving this check up to LSPLazyCodeActionIntentionAction#isAvailable ensures that LSPQuickFixIntentionAction is also guarded during actual indexing while still returning true (ExecuteLSPFeatureStatus.NOW) for non-project files in Smart Mode:

// In com.redhat.devtools.lsp4ij.features.codeAction.LSPLazyCodeActionIntentionAction
public class LSPLazyCodeActionIntentionAction implements IntentionAction, DumbAware {
    ...
    @Override
    public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
        if (ProjectIndexingManager.canExecuteLSPFeature(file) != ExecuteLSPFeatureStatus.NOW) {
            return false;
        }
        loadCodeActionIfNeeded();
        return isValidCodeAction(this.action);
    }
  1. (Optional) Ensure write access for multi-file WorkspaceEdits on non-project files:
    For the currently active PsiFile, ShowIntentionActionsHandler#invokeIntention automatically calls FileModificationService.getInstance().preparePsiElementsForWrite(...) (triggering IntelliJ's standard "Non-Project Files Protection" unlock dialog before invoke() runs). However, if a WorkspaceEdit modifies secondary non-project files, LSPIJUtils#applyWorkspaceEdit should call FileModificationService.getInstance().prepareVirtualFilesForWrite(project, affectedFiles) (or ReadonlyStatusHandler.ensureFilesWritable(...)) before modifying documents.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in com.redhat.devtools.lsp4ij.features.codeAction.LSPLazyCodeActionIntentionAction and compare LSPIntentionAction#isAvailable with ProjectIndexingManager.canExecuteLSPFeature. Check how IntentionActionWrapper delegates DumbAware, then verify quick fixes and intention actions are available for non-project files in Smart Mode but not during indexing. Review LSPIJUtils#applyWorkspaceEdit and the mentioned FileModificationService behavior if addressing secondary-file writes.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
devtools
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.