From e7c2f9831b50788d3da6d952742d80e38c158a89 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 29 Jul 2024 12:20:09 +0200 Subject: [PATCH] ensure TextHighlightingPass is instantiated in the BGT under DaemonIndicator with corresponding HighlightingSession, to make (implicit)contracts more explicit, visible and simple GitOrigin-RevId: f732af4b2bf614e6fca8c6af655beae34dfdcfc6 --- .../TextEditorHighlightingPass.java | 10 ++++++ .../ex/GlobalInspectionContextBase.java | 3 ++ .../ex/GlobalInspectionContextEx.java | 3 ++ .../InspectionApplicationBase.java | 32 ++++++++++++------- .../DeclarativeHintsProviderSettingsModel.kt | 6 ++-- .../ex/GlobalInspectionContextImpl.java | 6 ++-- .../codeInsight/hints/InlayPassTest.kt | 4 ++- .../impl/DeclarativeInlayHintsPassTest.kt | 23 ++++++++----- .../openapi/editor/EditorPaintingTest.java | 3 +- .../DeclarativeInlayHintsProviderTestCase.kt | 10 ++++-- 10 files changed, 71 insertions(+), 29 deletions(-) diff --git a/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java b/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java index 522f525ec85d..5411f75c3c5e 100644 --- a/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java +++ b/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java @@ -25,6 +25,15 @@ import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; +/** + * The highlighting pass which is associated with {@link Document} and its markup model. + * The instantiation of this class must happen in the background thread, under {@link com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator} + * which has corresponding {@link com.intellij.codeInsight.daemon.impl.HighlightingSession}. + * It's discouraged to do all that manually, please register your {@link TextEditorHighlightingPassFactory} in plugin.xml instead, e.g. like this: + *
+ *   {@code }
+ * 
+ */ public abstract class TextEditorHighlightingPass implements HighlightingPass { public static final TextEditorHighlightingPass[] EMPTY_ARRAY = new TextEditorHighlightingPass[0]; protected final @NotNull Document myDocument; @@ -44,6 +53,7 @@ public abstract class TextEditorHighlightingPass implements HighlightingPass { myRunIntentionPassAfter = runIntentionPassAfter; myInitialDocStamp = document.getModificationStamp(); myInitialPsiStamp = PsiModificationTracker.getInstance(project).getModificationCount(); + ThreadingAssertions.assertBackgroundThread(); } protected TextEditorHighlightingPass(@NotNull Project project, @NotNull Document document) { this(project, document, true); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java index 074b3c511c0c..517fe14269e3 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextBase.java @@ -29,6 +29,7 @@ import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.PsiManager; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.scope.packageSet.NamedScope; +import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; import com.intellij.util.containers.CollectionFactory; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashingStrategy; @@ -272,6 +273,7 @@ public class GlobalInspectionContextBase extends UserDataHolderBase implements G protected void notifyInspectionsFinished(@NotNull AnalysisScope scope) { } + @RequiresBackgroundThread public void performInspectionsWithProgress(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) { myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); if (!(myProgressIndicator instanceof ProgressIndicatorEx) || !(myProgressIndicator instanceof ProgressIndicatorWithDelayedPresentation)) { @@ -311,6 +313,7 @@ public class GlobalInspectionContextBase extends UserDataHolderBase implements G } + @RequiresBackgroundThread protected void runTools(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) { } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextEx.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextEx.java index 2643d3f9aa9a..f22af1d4af81 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextEx.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextEx.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.SystemProperties; +import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; @@ -59,6 +60,7 @@ public class GlobalInspectionContextEx extends GlobalInspectionContextBase { public GlobalInspectionContextEx(@NotNull Project project) { super(project); } + @RequiresBackgroundThread public void launchInspectionsOffline(@NotNull AnalysisScope scope, @NotNull Path outputPath, boolean runGlobalToolsOnly, @@ -66,6 +68,7 @@ public class GlobalInspectionContextEx extends GlobalInspectionContextBase { performInspectionsWithProgressAndExportResults(scope, runGlobalToolsOnly, true, outputPath, inspectionsResults); } + @RequiresBackgroundThread public void performInspectionsWithProgressAndExportResults(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections, diff --git a/platform/inspect/src/com/intellij/codeInspection/InspectionApplicationBase.java b/platform/inspect/src/com/intellij/codeInspection/InspectionApplicationBase.java index 3f96aefd1df9..b7a5ee0616f3 100644 --- a/platform/inspect/src/com/intellij/codeInspection/InspectionApplicationBase.java +++ b/platform/inspect/src/com/intellij/codeInspection/InspectionApplicationBase.java @@ -22,6 +22,8 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.progress.impl.ProgressRunner; import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.progress.util.ProgressIndicatorWithDelayedPresentation; import com.intellij.openapi.project.DumbService; @@ -665,19 +667,27 @@ public class InspectionApplicationBase implements CommandLineInspectionProgressR @NotNull AnalysisScope scope, @NotNull Path resultsDataPath, @NotNull List inspectionsResults) { - ProgressManager.getInstance().runProcess(() -> { - configureProject(projectPath, project, scope); + Task.Backgroundable task = new Task.Backgroundable(project, "") { + @Override + public void run(@NotNull ProgressIndicator indicator) { + configureProject(projectPath, project, scope); - if (!GlobalInspectionContextUtil.canRunInspections(project, false, () -> { - })) { - onFailure(InspectionsBundle.message("inspection.application.cannot.configure.project.to.run.inspections")); + if (!GlobalInspectionContextUtil.canRunInspections(project, false, () -> { + })) { + onFailure(InspectionsBundle.message("inspection.application.cannot.configure.project.to.run.inspections")); + } + context.launchInspectionsOffline(scope, resultsDataPath, myRunGlobalToolsOnly, inspectionsResults); + reportMessage(1, "\n" + InspectionsBundle.message("inspection.capitalized.done") + "\n"); + if (!myErrorCodeRequired) { + closeProject(project); + } } - context.launchInspectionsOffline(scope, resultsDataPath, myRunGlobalToolsOnly, inspectionsResults); - reportMessage(1, "\n" + InspectionsBundle.message("inspection.capitalized.done") + "\n"); - if (!myErrorCodeRequired) { - closeProject(project); - } - }, createProgressIndicator()); + }; + new ProgressRunner<>(task) + .onThread(ProgressRunner.ThreadToUse.POOLED) + .withProgress(createProgressIndicator()) + .sync() + .submitAndGet(); } private @NotNull ProgressIndicatorBase createProgressIndicator() { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/declarative/impl/DeclarativeHintsProviderSettingsModel.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/declarative/impl/DeclarativeHintsProviderSettingsModel.kt index 3fb9d4ca11ad..7465f94a4063 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/declarative/impl/DeclarativeHintsProviderSettingsModel.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/declarative/impl/DeclarativeHintsProviderSettingsModel.kt @@ -7,6 +7,7 @@ import com.intellij.codeInsight.hints.InlayGroup import com.intellij.codeInsight.hints.declarative.* import com.intellij.codeInsight.hints.settings.InlayProviderSettingsModel import com.intellij.lang.Language +import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.editor.Document import com.intellij.openapi.editor.Editor import com.intellij.openapi.fileTypes.FileType @@ -111,7 +112,8 @@ class DeclarativeHintsProviderSettingsModel( isEnabled } - val pass = DeclarativeInlayHintsPass(file, editor, listOf(InlayProviderPassInfo(object : InlayHintsProvider { + val pass = ActionUtil.underModalProgress(project, "") { + DeclarativeInlayHintsPass(file, editor, listOf(InlayProviderPassInfo(object : InlayHintsProvider { override fun createCollector(file: PsiFile, editor: Editor): InlayHintsCollector { return object: OwnBypassCollector { override fun collectHintsForFile(file: PsiFile, sink: InlayTreeSink) { @@ -125,7 +127,7 @@ class DeclarativeHintsProviderSettingsModel( } } }, providerId, enabledOptions)), false, !enabled) - + } pass.doCollectInformation(EmptyProgressIndicator()) return Runnable { diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index 9a2a16cc3144..149c3f92f9aa 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -78,6 +78,7 @@ import com.intellij.ui.content.ContentManager; import com.intellij.util.*; import com.intellij.util.concurrency.AppExecutorUtil; import com.intellij.util.concurrency.ThreadingAssertions; +import com.intellij.util.concurrency.annotations.RequiresBackgroundThread; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import io.opentelemetry.api.trace.Span; @@ -279,6 +280,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextEx { } @Override + @RequiresBackgroundThread protected void runTools(@NotNull AnalysisScope scope, boolean runGlobalToolsOnly, boolean isOfflineInspections) { IJTracer tracer = TelemetryManager.getInstance().getTracer(GlobalInspectionScopeKt.GlobalInspectionScope); runToolsSpan = tracer.spanBuilder("globalInspections").setNoParent().startSpan(); @@ -287,9 +289,7 @@ public class GlobalInspectionContextImpl extends GlobalInspectionContextEx { if (!(progressIndicator instanceof ProgressIndicatorWithDelayedPresentation)) { throw new IncorrectOperationException("Must be run under ProgressWindow"); } - if (!isOfflineInspections) { - ApplicationManager.getApplication().assertIsNonDispatchThread(); - } + ApplicationManager.getApplication().assertIsNonDispatchThread(); if (ApplicationManager.getApplication().isWriteAccessAllowed()) { throw new IncorrectOperationException("Must not start inspections from within write action"); } diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/InlayPassTest.kt b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/InlayPassTest.kt index d14aea8c7bce..08880f145dda 100644 --- a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/InlayPassTest.kt +++ b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/InlayPassTest.kt @@ -5,6 +5,7 @@ import com.intellij.codeInsight.hints.presentation.RecursivelyUpdatingRootPresen import com.intellij.codeInsight.hints.presentation.RootInlayPresentation import com.intellij.codeInsight.hints.presentation.SpacePresentation import com.intellij.lang.Language +import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Inlay import com.intellij.openapi.progress.DumbProgressIndicator @@ -178,7 +179,8 @@ class InlayPassTest : BasePlatformTestCase() { } private fun createPass(collectors: List>): InlayHintsPass { - return InlayHintsPass(myFixture.file, collectors, myFixture.editor, myFixture.editor.calculateVisibleRange(), sharedSink) + val visibleRange = myFixture.editor.calculateVisibleRange() + return ActionUtil.underModalProgress(project, "") { InlayHintsPass(myFixture.file, collectors, myFixture.editor, visibleRange, sharedSink) } } private fun InlayHintsPass.collectAndApply() { diff --git a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/declarative/impl/DeclarativeInlayHintsPassTest.kt b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/declarative/impl/DeclarativeInlayHintsPassTest.kt index df957a4a9f36..762b1036f9e4 100644 --- a/platform/lang-impl/testSources/com/intellij/codeInsight/hints/declarative/impl/DeclarativeInlayHintsPassTest.kt +++ b/platform/lang-impl/testSources/com/intellij/codeInsight/hints/declarative/impl/DeclarativeInlayHintsPassTest.kt @@ -3,6 +3,7 @@ package com.intellij.codeInsight.hints.declarative.impl import com.intellij.codeInsight.daemon.impl.DaemonProgressIndicator import com.intellij.codeInsight.hints.declarative.* +import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.Inlay import com.intellij.psi.PsiElement @@ -30,7 +31,7 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() } } }, "test.inlay.provider", emptyMap()) - val pass = DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(passInfo), false) + val pass = createPass(passInfo, false) collectAndApplyPass(pass) @@ -53,12 +54,12 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() } } - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false)) + collectAndApplyPass(createPass(providerInfo, false)) assertEquals(listOf(TextInlayPresentationEntry("inlay text", clickArea = null)), getInlays().single().getEntries()) assertNull(provider.hintAdder) // make sure no inlay is added on the next pass - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false)) + collectAndApplyPass(createPass(providerInfo, false)) assertEquals(emptyList(), getInlays()) } @@ -73,7 +74,7 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() } } - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false)) + collectAndApplyPass(createPass(providerInfo, false)) val inlay = getInlays().single() assertEquals(listOf(TextInlayPresentationEntry("inlay text", clickArea = null)), inlay.getEntries()) @@ -83,7 +84,7 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() } } - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false)) + collectAndApplyPass(createPass(providerInfo, false)) val newInlay = getInlays().single() TestCase.assertSame(inlay, newInlay) assertEquals(listOf(TextInlayPresentationEntry("new text", clickArea = null)), newInlay.getEntries()) @@ -109,7 +110,7 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() } } - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false)) + collectAndApplyPass(createPass(providerInfo, false)) val entries = getInlays().map { it.getEntries().single() } assertEquals(listOf( @@ -120,6 +121,12 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() ), entries) } + private fun createPass(providerInfo: InlayProviderPassInfo, isProviderDisabled: Boolean=false): DeclarativeInlayHintsPass { + return ActionUtil.underModalProgress(project, "") { + DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), isProviderDisabled, isProviderDisabled) + } + } + @Test fun testUpdateEnabled() { myFixture.configureByText("test.txt", "my content of file") @@ -131,14 +138,14 @@ class DeclarativeInlayHintsPassTest : LightPlatformCodeInsightFixture4TestCase() } } - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false, false)) + collectAndApplyPass(createPass(providerInfo, false)) assertFalse(getInlays().single().renderer.presentationList.isDisabled) provider.hintAdder = { addPresentation(InlineInlayPosition(2, true, priority = 1), hasBackground = true) { text("1") } } - collectAndApplyPass(DeclarativeInlayHintsPass(myFixture.file, myFixture.editor, listOf(providerInfo), false, true)) + collectAndApplyPass(createPass(providerInfo, true)) assertTrue(getInlays().single().renderer.presentationList.isDisabled) } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java index b0e83db97254..6531bc3260e2 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/editor/EditorPaintingTest.java @@ -3,6 +3,7 @@ package com.intellij.openapi.editor; import com.intellij.codeInsight.daemon.impl.IndentsPass; import com.intellij.openapi.actionSystem.IdeActions; +import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.event.DocumentEvent; @@ -300,7 +301,7 @@ public class EditorPaintingTest extends EditorPaintingTestCase { } private void runIndentsPass() { - IndentsPass indentsPass = new IndentsPass(getProject(), getEditor(), getFile()); + IndentsPass indentsPass = ActionUtil.underModalProgress(getProject(), "", ()->new IndentsPass(getProject(), getEditor(), getFile())); indentsPass.doCollectInformation(new EmptyProgressIndicator()); indentsPass.doApplyInformationToEditor(); } diff --git a/platform/testFramework/src/com/intellij/testFramework/utils/inlays/declarative/DeclarativeInlayHintsProviderTestCase.kt b/platform/testFramework/src/com/intellij/testFramework/utils/inlays/declarative/DeclarativeInlayHintsProviderTestCase.kt index 16f5560cd3f9..e2285aa33206 100644 --- a/platform/testFramework/src/com/intellij/testFramework/utils/inlays/declarative/DeclarativeInlayHintsProviderTestCase.kt +++ b/platform/testFramework/src/com/intellij/testFramework/utils/inlays/declarative/DeclarativeInlayHintsProviderTestCase.kt @@ -31,7 +31,10 @@ abstract class DeclarativeInlayHintsProviderTestCase : BasePlatformTestCase() { val file = myFixture.file!! val editor = myFixture.editor val providerInfo = InlayProviderPassInfo(provider, "provider.id", enabledOptions) - val pass = DeclarativeInlayHintsPass(file, editor, listOf(providerInfo), isPreview = false) + val pass = ActionUtil.underModalProgress(project, "") { + DeclarativeInlayHintsPass(file, editor, listOf(providerInfo), isPreview = false) + } + applyPassAndCheckResult(pass, expectedFile, sourceText, expectedText) } @@ -40,8 +43,9 @@ abstract class DeclarativeInlayHintsProviderTestCase : BasePlatformTestCase() { val fileName = "preview." + (language.associatedFileType?.defaultExtension ?: error("language must have extension")) myFixture.configureByText(fileName, InlayDumpUtil.removeHints(previewText)) - val pass = DeclarativeInlayHintsPassFactory.createPassForPreview(myFixture.file, myFixture.editor, provider, providerId, - emptyMap(), false) + val pass = ActionUtil.underModalProgress(project, "") { + DeclarativeInlayHintsPassFactory.createPassForPreview(myFixture.file, myFixture.editor, provider, providerId, emptyMap(), false) + } applyPassAndCheckResult(pass, null, previewText, expectedText) }