IDEA-327970 IntentionAction#isAvailable is called from EDT

EA-832528 - T: FileBasedIndexImpl.ensureUpToDate

GitOrigin-RevId: 0910bfd59787017781ff3ed44d49bed3790e6520
This commit is contained in:
Gregory.Shrago
2023-10-03 00:14:37 +00:00
committed by intellij-monorepo-bot
parent c2b955e360
commit 9feb83f731
7 changed files with 80 additions and 105 deletions
@@ -9,13 +9,15 @@ import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl;
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.List;
final class EditorNotificationActions {
static void collectActions(@NotNull Editor hostEditor, @NotNull ShowIntentionsPass.IntentionsInfo intentions) {
@ApiStatus.Internal
public final class EditorNotificationActions {
public static void collectActions(@NotNull Editor hostEditor, @NotNull ShowIntentionsPass.IntentionsInfo intentions) {
Project project = hostEditor.getProject();
if (project == null) return;
FileEditorManager fileEditorManager = FileEditorManager.getInstance(project);
@@ -13,6 +13,7 @@ import com.intellij.codeInsight.template.impl.TemplateState;
import com.intellij.lang.Language;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
@@ -33,7 +34,6 @@ import com.intellij.util.CommonProcessors;
import com.intellij.util.concurrency.ThreadingAssertions;
import com.intellij.util.containers.ContainerUtil;
import kotlin.sequences.SequencesKt;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -173,6 +173,9 @@ public final class ShowIntentionsPass extends TextEditorHighlightingPass {
private @Nullable @NlsContexts.PopupTitle String myTitle;
public void filterActions(@Nullable PsiFile psiFile) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
ThreadingAssertions.assertBackgroundThread();
}
IntentionActionFilter[] filters = IntentionActionFilter.EXTENSION_POINT_NAME.getExtensions();
filter(intentionsToShow, psiFile, filters);
filter(errorFixesToShow, psiFile, filters);
@@ -243,11 +246,12 @@ public final class ShowIntentionsPass extends TextEditorHighlightingPass {
if (state != null && !state.isFinished()) {
return;
}
IntentionsInfo myIntentionsInfo = new IntentionsInfo();
getActionsToShow(myEditor, myFile, myIntentionsInfo, myPassIdToShowIntentionsFor, myQueryIntentionActions);
IntentionsInfo intentionsInfo = new IntentionsInfo();
getActionsToShow(myEditor, myFile, intentionsInfo, myPassIdToShowIntentionsFor, myQueryIntentionActions);
EditorNotificationActions.collectActions(myEditor, intentionsInfo); // TODO EDT-only call! (IDEA-333895)
intentionsInfo.filterActions(myFile);
myCachedIntentions = IntentionsUI.getInstance(myProject).getCachedIntentions(myEditor, myFile);
myActionsChanged = myCachedIntentions.wrapAndUpdateActions(myIntentionsInfo, false);
myActionsChanged = myCachedIntentions.wrapAndUpdateActions(intentionsInfo, false);
UnresolvedReferenceQuickFixUpdater.getInstance(myProject).startComputingNextQuickFixes(myFile, myEditor, myVisibleRange);
}
@@ -259,10 +263,6 @@ public final class ShowIntentionsPass extends TextEditorHighlightingPass {
boolean actionsChanged = myActionsChanged;
TemplateState state = TemplateManagerImpl.getTemplateState(myEditor);
if ((state == null || state.isFinished()) && cachedIntentions != null) {
IntentionsInfo syncInfo = new IntentionsInfo();
getActionsToShowSync(myEditor, myFile, syncInfo);
actionsChanged |= cachedIntentions.addActions(syncInfo);
IntentionsUI.getInstance(myProject).update(cachedIntentions, actionsChanged);
}
}
@@ -276,28 +276,17 @@ public final class ShowIntentionsPass extends TextEditorHighlightingPass {
public static @NotNull IntentionsInfo getActionsToShow(@NotNull Editor hostEditor, @NotNull PsiFile hostFile, boolean includeSyncActions) {
IntentionsInfo result = new IntentionsInfo();
getActionsToShow(hostEditor, hostFile, result, -1);
if (includeSyncActions) {
getActionsToShowSync(hostEditor, hostFile, result);
}
return result;
}
/**
* Collects intention actions from providers intended to be invoked in EDT.
*/
@ApiStatus.Internal
public static void getActionsToShowSync(@NotNull Editor hostEditor, @NotNull PsiFile hostFile, @NotNull IntentionsInfo intentions) {
ThreadingAssertions.assertEventDispatchThread();
EditorNotificationActions.collectActions(hostEditor, intentions);
intentions.filterActions(hostFile);
}
/**
* Collects intention actions from providers intended to be invoked in a background thread.
*/
public static void getActionsToShow(@NotNull Editor hostEditor, @NotNull PsiFile hostFile, @NotNull IntentionsInfo intentions, int passIdToShowIntentionsFor) {
getActionsToShow(hostEditor, hostFile, intentions, passIdToShowIntentionsFor, true);
intentions.filterActions(hostFile);
}
private static void getActionsToShow(@NotNull Editor hostEditor,
@NotNull PsiFile hostFile,
@NotNull IntentionsInfo intentions,
@@ -403,8 +392,6 @@ public final class ShowIntentionsPass extends TextEditorHighlightingPass {
}
}
}
intentions.filterActions(hostFile);
}
private static @NotNull Collection<String> getLanguagesForIntentions(@NotNull PsiFile hostFile,
@@ -5,10 +5,7 @@ import com.intellij.codeInsight.CodeInsightActionHandler;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.codeInsight.daemon.impl.IntentionsUI;
import com.intellij.codeInsight.daemon.impl.IntentionsUIImpl;
import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.HintManagerImpl;
import com.intellij.codeInsight.intention.IntentionAction;
@@ -108,7 +105,7 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
}
editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
showIntentionHint(project, editor, file, calcIntentions(project, editor, file), showFeedbackOnEmptyMenu);
showIntentionHint(project, editor, file, showFeedbackOnEmptyMenu);
long elapsed = System.currentTimeMillis() - start;
IntentionFUSCollector.reportPopupDelay(project, elapsed, file.getFileType());
}
@@ -116,21 +113,15 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
protected void showIntentionHint(@NotNull Project project,
@NotNull Editor editor,
@NotNull PsiFile file,
@NotNull ShowIntentionsPass.IntentionsInfo intentions,
boolean showFeedbackOnEmptyMenu) {
if (intentions.isEmpty()) {
CachedIntentions cachedIntentions = calcCachedIntentions(project, editor, file);
cachedIntentions.wrapAndUpdateGutters();
if (cachedIntentions.getAllActions().isEmpty()) {
showEmptyMenuFeedback(editor, showFeedbackOnEmptyMenu);
}
else {
editor.getScrollingModel().runActionOnScrollingFinished(() -> {
CachedIntentions cachedIntentions = CachedIntentions.createAndUpdateActions(project, file, editor, intentions);
cachedIntentions.wrapAndUpdateGutters();
if (cachedIntentions.getAllActions().isEmpty()) {
showEmptyMenuFeedback(editor, showFeedbackOnEmptyMenu);
}
else {
IntentionHintComponent.showIntentionHint(project, file, editor, true, cachedIntentions);
}
IntentionHintComponent.showIntentionHint(project, file, editor, true, cachedIntentions);
});
}
}
@@ -143,9 +134,9 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
}
@ApiStatus.Internal
public static @NotNull ShowIntentionsPass.IntentionsInfo calcIntentions(@NotNull Project project,
@NotNull Editor editor,
@NotNull PsiFile file) {
public static @NotNull CachedIntentions calcCachedIntentions(@NotNull Project project,
@NotNull Editor editor,
@NotNull PsiFile file) {
ThreadingAssertions.assertEventDispatchThread();
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
throw new IllegalStateException("must not wait for intentions inside write action");
@@ -153,20 +144,22 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
String progressTitle = CodeInsightBundle.message("progress.title.searching.for.context.actions");
DumbService dumbService = DumbService.getInstance(project);
boolean useAlternativeResolve = dumbService.isAlternativeResolveEnabled();
ThrowableComputable<ShowIntentionsPass.IntentionsInfo, RuntimeException> prioritizedRunnable =
ShowIntentionsPass.IntentionsInfo intentionsInfo = new ShowIntentionsPass.IntentionsInfo();
EditorNotificationActions.collectActions(editor, intentionsInfo);
ThrowableComputable<CachedIntentions, RuntimeException> prioritizedRunnable =
() -> ProgressManager.getInstance().computePrioritized(() -> {
intentionsInfo.filterActions(file);
DaemonCodeAnalyzerImpl.waitForUnresolvedReferencesQuickFixesUnderCaret(file, editor);
return ReadAction.compute(() -> ShowIntentionsPass.getActionsToShow(editor, file, false));
return ReadAction.compute(() -> {
ShowIntentionsPass.getActionsToShow(editor, file, intentionsInfo, -1);
return CachedIntentions.createAndUpdateActions(project, file, editor, intentionsInfo);
});
});
ThrowableComputable<ShowIntentionsPass.IntentionsInfo, RuntimeException> process =
ThrowableComputable<CachedIntentions, RuntimeException> process =
useAlternativeResolve
? () -> dumbService.computeWithAlternativeResolveEnabled(prioritizedRunnable)
: prioritizedRunnable;
ShowIntentionsPass.IntentionsInfo intentions =
ProgressManager.getInstance().runProcessWithProgressSynchronously(process, progressTitle, true, project);
ShowIntentionsPass.getActionsToShowSync(editor, file, intentions);
return intentions;
return ProgressManager.getInstance().runProcessWithProgressSynchronously(process, progressTitle, true, project);
}
private static void letAutoImportComplete(@NotNull Editor editor, @NotNull PsiFile file, DaemonCodeAnalyzerImpl codeAnalyzer) {
@@ -246,7 +239,7 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
Editor editorToApply = null;
PsiFile fileToApply = null;
Editor injectedEditor = null;
Editor injectedEditor;
if (injectedFile != null && !(hostEditor instanceof IntentionPreviewEditor)) {
injectedEditor = InjectedLanguageUtil.getInjectedEditorForInjectedFile(hostEditor, injectedFile);
if (hostEditor != injectedEditor && predicate.process(injectedFile, injectedEditor)) {
@@ -285,7 +278,7 @@ public class ShowIntentionActionsHandler implements CodeInsightActionHandler {
Project project = hostFile.getProject();
((FeatureUsageTrackerImpl)FeatureUsageTracker.getInstance()).getFixesStats().registerInvocation();
try (AccessToken __ = SlowOperations.startSection(SlowOperations.ACTION_PERFORM)) {
try (AccessToken ignore = SlowOperations.startSection(SlowOperations.ACTION_PERFORM)) {
PsiDocumentManager.getInstance(project).commitAllDocuments();
ModCommandAction commandAction = action.asModCommandAction();
if (commandAction != null) {
@@ -137,6 +137,7 @@ import com.intellij.usages.*;
import com.intellij.usages.impl.UsageViewImpl;
import com.intellij.util.*;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.concurrency.annotations.RequiresEdt;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileBasedIndexExtension;
@@ -345,41 +346,40 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig
current.waitForHighlighting(file.getProject(), editor);
}
waitForUnresolvedReferencesQuickFixesUnderCaret(file, editor);
return ReadAction.compute(() -> doGetAvailableIntentions(editor, file));
}
@NotNull
private static List<IntentionAction> doGetAvailableIntentions(@NotNull Editor editor, @NotNull PsiFile file) {
IntentionListStep intentionListStep = getIntentionListStep(editor, file);
List<IntentionAction> result = new ArrayList<>();
for (Map.Entry<IntentionAction, List<IntentionAction>> entry : intentionListStep.getActionsWithSubActions().entrySet()) {
result.add(entry.getKey());
result.addAll(entry.getValue());
}
List<HighlightInfo> infos = ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzerEx.getInstanceEx(file.getProject())).getFileLevelHighlights(file.getProject(), file);
for (HighlightInfo info : infos) {
info.findRegisteredQuickFix((descriptor, range) -> {
if (descriptor.getAction().isAvailable(file.getProject(), editor, file)) {
result.add(descriptor.getAction());
for (IntentionAction subAction : descriptor.getOptions(file, editor)) {
if (subAction.isAvailable(file.getProject(), editor, file)) {
result.add(subAction);
ApplicationManager.getApplication().invokeAndWait(() -> {
IntentionListStep intentionListStep = getIntentionListStep(editor, file);
for (Map.Entry<IntentionAction, List<IntentionAction>> entry : intentionListStep.getActionsWithSubActions().entrySet()) {
result.add(entry.getKey());
result.addAll(entry.getValue());
}
});
ReadAction.compute(() -> {
List<HighlightInfo> infos = ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzerEx.getInstanceEx(file.getProject()))
.getFileLevelHighlights(file.getProject(), file);
for (HighlightInfo info : infos) {
info.findRegisteredQuickFix((descriptor, range) -> {
if (descriptor.getAction().isAvailable(file.getProject(), editor, file)) {
result.add(descriptor.getAction());
for (IntentionAction subAction : descriptor.getOptions(file, editor)) {
if (subAction.isAvailable(file.getProject(), editor, file)) {
result.add(subAction);
}
}
}
}
return null;
});
}
return null;
});
}
return result;
});
return result;
}
@RequiresEdt
@NotNull
private static IntentionListStep getIntentionListStep(@NotNull Editor editor, @NotNull PsiFile file) {
ShowIntentionsPass.IntentionsInfo intentions = ShowIntentionsPass.getActionsToShow(editor, file, false);
return new IntentionListStep(null, editor, file, file.getProject(),
CachedIntentions.create(file.getProject(), file, editor, intentions));
CachedIntentions cachedIntentions = ShowIntentionActionsHandler.calcCachedIntentions(file.getProject(), editor, file);
return new IntentionListStep(null, editor, file, file.getProject(), cachedIntentions);
}
public static void waitForUnresolvedReferencesQuickFixesUnderCaret(@NotNull PsiFile file, @NotNull Editor editor) {
@@ -6,7 +6,6 @@ import com.intellij.codeInsight.CodeInsightSettings
import com.intellij.codeInsight.daemon.impl.HighlightInfo
import com.intellij.codeInsight.daemon.quickFix.ActionHint
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.impl.CachedIntentions
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
import com.intellij.codeInspection.InspectionEP
import com.intellij.codeInspection.LocalInspectionEP
@@ -30,14 +29,14 @@ import junit.framework.TestCase
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.quickfix.utils.findInspectionFile
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils
import org.jetbrains.kotlin.idea.test.Directives
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinTestUtils
import org.jetbrains.kotlin.idea.test.TestFiles
import org.jetbrains.kotlin.idea.test.runAll
import org.jetbrains.kotlin.idea.test.withCustomCompilerOptions
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.idea.test.Directives
import org.jetbrains.kotlin.idea.test.KotlinTestUtils
import org.jetbrains.kotlin.idea.test.TestFiles
import org.jetbrains.kotlin.test.utils.IgnoreTests
import java.io.File
import java.nio.file.Paths
@@ -296,8 +295,7 @@ abstract class AbstractQuickFixMultiFileTest : KotlinLightCodeInsightFixtureTest
private val availableActions: List<IntentionAction>
get() {
myFixture.doHighlighting()
val intentions = ShowIntentionActionsHandler.calcIntentions(project, editor, file)
val cachedIntentions = CachedIntentions.create(project, file, editor, intentions)
val cachedIntentions = ShowIntentionActionsHandler.calcCachedIntentions(project, editor, file)
cachedIntentions.wrapAndUpdateGutters()
return cachedIntentions.allActions.map { it.action }
}
@@ -6,7 +6,6 @@ import com.intellij.codeInsight.daemon.quickFix.ActionHint
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.IntentionActionDelegate
import com.intellij.codeInsight.intention.PriorityAction
import com.intellij.codeInsight.intention.impl.CachedIntentions
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
import com.intellij.codeInspection.SuppressableProblemGroup
import com.intellij.codeInspection.ex.QuickFixWrapper
@@ -356,8 +355,7 @@ abstract class AbstractQuickFixTest : KotlinLightCodeInsightFixtureTestCase(), Q
}
myFixture.doHighlighting()
val intentions = ShowIntentionActionsHandler.calcIntentions(project, editor, file)
val cachedIntentions = CachedIntentions.create(project, file, editor, intentions)
val cachedIntentions = ShowIntentionActionsHandler.calcCachedIntentions(project, editor, file)
cachedIntentions.wrapAndUpdateGutters()
val actions = cachedIntentions.allActions.map { it.action }.toMutableList()
@@ -1,11 +1,9 @@
package com.jetbrains.performancePlugin.commands;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass;
import com.intellij.codeInsight.intention.impl.CachedIntentions;
import com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching;
import com.intellij.codeInsight.intention.impl.IntentionHintComponent;
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
import com.intellij.platform.diagnostic.telemetry.helpers.TraceUtil;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
@@ -14,6 +12,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.playback.PlaybackContext;
import com.intellij.openapi.ui.playback.commands.AbstractCommand;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.platform.diagnostic.telemetry.helpers.TraceUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.jetbrains.performancePlugin.PerformanceTestSpan;
@@ -58,27 +57,25 @@ public final class ShowAltEnter extends AbstractCommand implements Disposable {
PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if (psiFile != null) {
TraceUtil.runWithSpanThrows(PerformanceTestSpan.TRACER, SPAN_NAME, span -> {
ShowIntentionsPass.IntentionsInfo intentions = ShowIntentionActionsHandler.calcIntentions(project, editor, psiFile);
CachedIntentions intentions = ShowIntentionActionsHandler.calcCachedIntentions(project, editor, psiFile);
if (!actionName.isEmpty()) {
List<HighlightInfo.IntentionActionDescriptor> combined = new ArrayList<>();
combined.addAll(intentions.intentionsToShow);
combined.addAll(intentions.inspectionFixesToShow);
combined.addAll(intentions.errorFixesToShow);
List<IntentionActionWithTextCaching> combined = new ArrayList<>();
combined.addAll(intentions.getIntentions());
combined.addAll(intentions.getInspectionFixes());
combined.addAll(intentions.getErrorFixes());
//combined.addAll(intentions.guttersToShow);
combined.addAll(intentions.notificationActionsToShow);
combined.addAll(intentions.getNotifications());
span.setAttribute("number", combined.size());
Optional<HighlightInfo.IntentionActionDescriptor>
Optional<IntentionActionWithTextCaching>
singleIntention = combined.stream().filter(s -> s.getAction().getText().startsWith(actionName)).findFirst();
if (singleIntention.isEmpty()) actionCallback.reject(actionName + " is not found among " + combined);
if (invoke) {
singleIntention
.ifPresent(
c -> ShowIntentionActionsHandler.chooseActionAndInvoke(psiFile, editor, c.getAction(), c.getAction().getText()));
singleIntention.ifPresent(
c -> ShowIntentionActionsHandler.chooseActionAndInvoke(psiFile, editor, c.getAction(), c.getAction().getText()));
}
}
if (!invoke || actionName.isEmpty()) {
CachedIntentions cachedIntentions = CachedIntentions.create(project, psiFile, editor, intentions);
IntentionHintComponent.showIntentionHint(project, psiFile, editor, true, cachedIntentions);
IntentionHintComponent.showIntentionHint(project, psiFile, editor, true, intentions);
}
});
actionCallback.setDone();