diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java b/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java index 5f42a4ab3f61..fb1fbea42811 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/AlternativeSourceNotificationProvider.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.debugger.ui; import com.intellij.debugger.DebuggerManagerEx; @@ -44,14 +44,14 @@ public final class AlternativeSourceNotificationProvider implements EditorNotifi private static final Key FILE_PROCESSED_KEY = Key.create("AlternativeSourceCheckDone"); @Override - public @NotNull Function collectNotificationData(@NotNull Project project, + public @Nullable Function collectNotificationData(@NotNull Project project, @NotNull VirtualFile file) { if (!DebuggerSettings.getInstance().SHOW_ALTERNATIVE_SOURCE) { - return CONST_NULL; + return null; } if (DumbService.getInstance(project).isDumb()) { - return CONST_NULL; + return null; } DebuggerSession javaSession = DebuggerManagerEx.getInstanceEx(project).getContext().getDebuggerSession(); @@ -59,29 +59,29 @@ public final class AlternativeSourceNotificationProvider implements EditorNotifi if (session == null) { setFileProcessed(file, false); - return CONST_NULL; + return null; } XSourcePosition position = session.getCurrentPosition(); if (position == null || !file.equals(position.getFile())) { setFileProcessed(file, false); - return CONST_NULL; + return null; } final PsiFile psiFile = PsiManager.getInstance(project).findFile(file); if (!(psiFile instanceof PsiJavaFile)) { - return CONST_NULL; + return null; } PsiClass[] classes = ((PsiJavaFile)psiFile).getClasses(); if (classes.length == 0) { - return CONST_NULL; + return null; } PsiClass baseClass = classes[0]; String name = baseClass.getQualifiedName(); if (name == null) { - return CONST_NULL; + return null; } PsiClass[] altClasses = JavaPsiFacade.getInstance(project).findClasses(name, javaSession.getSearchScope()); @@ -94,7 +94,7 @@ public final class AlternativeSourceNotificationProvider implements EditorNotifi setFileProcessed(file, true); if (alts.size() <= 1) { - return CONST_NULL; + return null; } for (PsiClass cls : alts) { diff --git a/java/execution/impl/src/com/intellij/execution/jshell/SnippetEditorDecorator.java b/java/execution/impl/src/com/intellij/execution/jshell/SnippetEditorDecorator.java index b31ae9caa39a..de7faa4313a7 100644 --- a/java/execution/impl/src/com/intellij/execution/jshell/SnippetEditorDecorator.java +++ b/java/execution/impl/src/com/intellij/execution/jshell/SnippetEditorDecorator.java @@ -187,12 +187,12 @@ public final class SnippetEditorDecorator implements EditorNotificationProvider } @Override - public @NotNull Function collectNotificationData(@NotNull Project project, + public @Nullable Function collectNotificationData(@NotNull Project project, @NotNull VirtualFile file) { if (ScratchFileService.findRootType(file) instanceof JShellRootType) { return editor -> new ConfigurationPane(project, file, editor); } - return CONST_NULL; + return null; } } \ No newline at end of file diff --git a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java index e6f6dbb81740..9d16fc9be141 100644 --- a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java +++ b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/AttachSourcesNotificationProvider.java @@ -80,10 +80,10 @@ final class AttachSourcesNotificationProvider implements EditorNotificationProvi } @Override - public @NotNull Function collectNotificationData(@NotNull Project project, + public @Nullable Function collectNotificationData(@NotNull Project project, @NotNull VirtualFile file) { if (!FileTypeRegistry.getInstance().isFileOfType(file, JavaClassFileType.INSTANCE)) { - return CONST_NULL; + return null; } String classFileInfo = getTextWithClassFileInfo(file); diff --git a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/LibrarySourceNotificationProvider.kt b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/LibrarySourceNotificationProvider.kt index 5539cfc6fc85..855c68c7ecd9 100644 --- a/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/LibrarySourceNotificationProvider.kt +++ b/java/idea-ui/src/com/intellij/codeInsight/daemon/impl/LibrarySourceNotificationProvider.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInsight.daemon.impl import com.intellij.diff.DiffContentFactory @@ -38,7 +38,7 @@ class LibrarySourceNotificationProvider : EditorNotificationProvider { override fun collectNotificationData( project: Project, file: VirtualFile, - ): Function { + ): Function? { if (file.fileType is LanguageFileType && ProjectRootManager.getInstance(project).fileIndex.isInLibrarySource(file)) { val psiFile = PsiManager.getInstance(project).findFile(file) if (psiFile is PsiJavaFile) { @@ -69,7 +69,7 @@ class LibrarySourceNotificationProvider : EditorNotificationProvider { } } - return EditorNotificationProvider.CONST_NULL + return null } private fun differs(src: PsiClass): Boolean { diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/SdkSetupNotificationTestBase.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/SdkSetupNotificationTestBase.java index ccf776acb6f1..aa5114b13243 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/SdkSetupNotificationTestBase.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/SdkSetupNotificationTestBase.java @@ -17,11 +17,12 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.PlatformTestUtil; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture; import com.intellij.ui.EditorNotificationPanel; +import com.intellij.ui.EditorNotifications; import com.intellij.ui.EditorNotificationsImpl; -import com.intellij.util.ui.EDT; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,20 +60,22 @@ public abstract class SdkSetupNotificationTestBase extends JavaCodeInsightFixtur @NotNull String fileName, @NotNull String fileText) { FileEditor editor = openTextInEditor(fixture, fileName, fileText); - return (EditorNotificationPanel)EditorNotificationsImpl.getNotificationPanels(editor).get(SdkSetupNotificationProvider.class); + EditorNotificationsImpl editorNotifications = (EditorNotificationsImpl)EditorNotifications.getInstance(fixture.getProject()); + return (EditorNotificationPanel)(editorNotifications.getNotificationPanels(editor).get(SdkSetupNotificationProvider.class)); } static @NotNull FileEditor openTextInEditor(@NotNull JavaCodeInsightTestFixture fixture, @NotNull String fileName, @NotNull String fileText) { - EDT.dispatchAllInvocationEvents(); - EditorNotificationsImpl.completeAsyncTasks(fixture.getProject()); + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); + EditorNotificationsImpl editorNotifications = (EditorNotificationsImpl)EditorNotifications.getInstance(fixture.getProject()); + editorNotifications.completeAsyncTasks(); - final PsiFile psiFile = fixture.configureByText(fileName, fileText); + PsiFile psiFile = fixture.configureByText(fileName, fileText); FileEditorManager fileEditorManager = FileEditorManager.getInstance(fixture.getProject()); VirtualFile virtualFile = psiFile.getVirtualFile(); - final FileEditor[] editors = fileEditorManager.openFile(virtualFile, true); + FileEditor[] editors = fileEditorManager.openFile(virtualFile, true); Disposer.register(fixture.getTestRootDisposable(), new Disposable() { @Override public void dispose() { @@ -81,8 +84,8 @@ public abstract class SdkSetupNotificationTestBase extends JavaCodeInsightFixtur }); assertThat(editors).hasSize(1); - EDT.dispatchAllInvocationEvents(); - EditorNotificationsImpl.completeAsyncTasks(fixture.getProject()); + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue(); + editorNotifications.completeAsyncTasks(); return editors[0]; } diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/UnknownSdkTrackerTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/UnknownSdkTrackerTest.java index 0feb22df5a97..9160b24fa472 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/UnknownSdkTrackerTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/impl/UnknownSdkTrackerTest.java @@ -67,21 +67,23 @@ public class UnknownSdkTrackerTest extends JavaCodeInsightFixtureTestCase { public void testMissingModuleJdk() { setModuleSdk("missingSDK", JavaSdk.getInstance()); - final List fixes = detectMissingSdks(); + List fixes = detectMissingSdks(); assertThat(fixes) .withFailMessage(String.valueOf(fixes)) .hasSize(1) - .first().asString().startsWith("SdkFixInfo:"); + .first().asString().matches(s -> s.startsWith("SdkFixInfo:") || s.startsWith("SdkSetupNotification:")); } public void testMissingModuleUnknownSdk() { setModuleSdk("missingSDK", "foo-bar-baz"); - final List fixes = detectMissingSdks(); + List fixes = detectMissingSdks(); assertThat(fixes) .withFailMessage(String.valueOf(fixes)) .hasSize(1) - .first().asString().startsWith("SdkSetupNotification:"); + .first() + .asString() + .startsWith("SdkSetupNotification:"); } public void testNoProjectSdk() { @@ -106,7 +108,6 @@ public class UnknownSdkTrackerTest extends JavaCodeInsightFixtureTestCase { public void testNoModuleSdk() { ModuleRootModificationUtil.setModuleSdk(getModule(), null); - final List fixes = detectMissingSdks(); assertThat(fixes) .withFailMessage(String.valueOf(fixes)) @@ -156,7 +157,6 @@ public class UnknownSdkTrackerTest extends JavaCodeInsightFixtureTestCase { assertThat(lookupCalls).hasValue(2); } - @TestFor(issues = "IDEA-237884") public void testShouldNotRantOnCustomSDKType() { final Sdk broken = ProjectJdkTable.getInstance().createSdk("broken-sdk-123", SimpleJavaSdkType.getInstance()); @@ -267,7 +267,7 @@ public class UnknownSdkTrackerTest extends JavaCodeInsightFixtureTestCase { private @NotNull List detectMissingSdks() { UnknownSdkTracker.getInstance(getProject()).updateUnknownSdks(); - ArrayList infos = new ArrayList<>(); + List infos = new ArrayList<>(); EditorNotificationPanel sdkNotification = SdkSetupNotificationTestBase.runOnText(myFixture, "Sample.java", "class Sample { java.lang.String foo; }"); if (sdkNotification != null) { diff --git a/platform/execution-impl/src/com/intellij/execution/impl/RunConfigEditorNotificationProvider.kt b/platform/execution-impl/src/com/intellij/execution/impl/RunConfigEditorNotificationProvider.kt index fb0e2b63881c..65860cf23ab5 100644 --- a/platform/execution-impl/src/com/intellij/execution/impl/RunConfigEditorNotificationProvider.kt +++ b/platform/execution-impl/src/com/intellij/execution/impl/RunConfigEditorNotificationProvider.kt @@ -1,41 +1,43 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.execution.impl import com.intellij.execution.ExecutionBundle import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectFileIndex -import com.intellij.openapi.util.Key import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.EditorNotificationPanel -import com.intellij.ui.EditorNotifications +import com.intellij.ui.EditorNotificationProvider +import java.util.function.Function +import javax.swing.JComponent -class RunConfigEditorNotificationProvider : EditorNotifications.Provider() { - private val KEY: Key = Key.create("RunConfigEditorNotificationProvider") - - override fun getKey(): Key = KEY - - override fun createNotificationPanel(file: VirtualFile, fileEditor: FileEditor, project: Project): EditorNotificationPanel? { - if (!file.name.endsWith(".run.xml")) return null - if (!ProjectFileIndex.getInstance(project).isInContent(file)) return null - - val runManager = RunManagerImpl.getInstanceImpl(project) - if (!runManager.isFileContainsRunConfiguration(file)) return null - - val panel = EditorNotificationPanel(fileEditor, EditorNotificationPanel.Status.Warning) - panel.text = ExecutionBundle.message("manual.editing.of.config.file.not.recommended") - @Suppress("DialogTitleCapitalization") val message = ExecutionBundle.message("open.run.debug.dialog") - panel.createActionLabel(message) { - val oldSelectedConfig = runManager.selectedConfiguration - - runManager.selectConfigurationStoredInFile(file) - val ok = EditConfigurationsDialog(project).showAndGet() - - if (!ok) { - runManager.selectedConfiguration = oldSelectedConfig - } +private class RunConfigEditorNotificationProvider : EditorNotificationProvider { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { + if (!file.nameSequence.endsWith(".run.xml") || !ProjectFileIndex.getInstance(project).isInContent(file)) { + return null } - return panel + val runManager = RunManagerImpl.getInstanceImpl(project) + if (!runManager.isFileContainsRunConfiguration(file)) { + return null + } + + return Function { fileEditor -> + val panel = EditorNotificationPanel(fileEditor, EditorNotificationPanel.Status.Warning) + panel.text = ExecutionBundle.message("manual.editing.of.config.file.not.recommended") + @Suppress("DialogTitleCapitalization") val message = ExecutionBundle.message("open.run.debug.dialog") + panel.createActionLabel(message) { + val oldSelectedConfig = runManager.selectedConfiguration + + runManager.selectConfigurationStoredInFile(file) + val ok = EditConfigurationsDialog(project).showAndGet() + + if (!ok) { + runManager.selectedConfiguration = oldSelectedConfig + } + } + + panel + } } } \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SdkSetupNotificationProvider.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SdkSetupNotificationProvider.java index b2c0b0d3d583..e7d8cfaf0024 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SdkSetupNotificationProvider.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/SdkSetupNotificationProvider.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.codeInsight.daemon.impl; import com.intellij.codeInsight.daemon.ProjectSdkSetupValidator; @@ -22,26 +22,22 @@ import java.util.function.Function; /** * @author Danila Ponomarenko */ -public final class SdkSetupNotificationProvider implements EditorNotificationProvider, - DumbAware { - +public final class SdkSetupNotificationProvider implements EditorNotificationProvider, DumbAware { @Override - public @NotNull Function collectNotificationData(@NotNull Project project, - @NotNull VirtualFile file) { + public @Nullable Function collectNotificationData(@NotNull Project project, + @NotNull VirtualFile file) { if (!UnknownSdkEditorNotification.getInstance(project).allowProjectSdkNotifications()) { - return CONST_NULL; + return null; } for (ProjectSdkSetupValidator validator : ProjectSdkSetupValidator.EP_NAME.getExtensionList()) { if (validator.isApplicableFor(project, file)) { String errorMessage = validator.getErrorMessage(project, file); - return errorMessage != null ? - fileEditor -> createPanel(errorMessage, fileEditor, validator.getFixHandler(project, file)) : - CONST_NULL; + return errorMessage == null ? null : fileEditor -> createPanel(errorMessage, fileEditor, validator.getFixHandler(project, file)); } } - return CONST_NULL; + return null; } @RequiresEdt diff --git a/platform/platform-api/src/com/intellij/ui/EditorNotificationProvider.java b/platform/platform-api/src/com/intellij/ui/EditorNotificationProvider.java index 6879eb533bab..4afd4e48f30c 100644 --- a/platform/platform-api/src/com/intellij/ui/EditorNotificationProvider.java +++ b/platform/platform-api/src/com/intellij/ui/EditorNotificationProvider.java @@ -22,13 +22,16 @@ import java.util.function.Function; *

*/ public interface EditorNotificationProvider { - ProjectExtensionPointName EP_NAME = new ProjectExtensionPointName<>("com.intellij.editorNotificationProvider"); + /** + * @deprecated Use {@code null}. + */ + @Deprecated Function CONST_NULL = __ -> null; @RequiresReadLock - @NotNull Function collectNotificationData(@NotNull Project project, - @NotNull VirtualFile file); + @Nullable Function collectNotificationData(@NotNull Project project, + @NotNull VirtualFile file); } diff --git a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.kt b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.kt index 96bd2a32b549..4a81f045685a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.kt +++ b/platform/platform-impl/src/com/intellij/openapi/updateSettings/impl/pluginsAdvertisement/PluginAdvertiserEditorNotificationProvider.kt @@ -34,7 +34,7 @@ class PluginAdvertiserEditorNotificationProvider : EditorNotificationProvider, override fun collectNotificationData( project: Project, file: VirtualFile, - ): Function { + ): Function? { val suggestionData = getSuggestionData(project, ApplicationInfo.getInstance().build.productCode, file.name, file.fileType) if (suggestionData == null) { @@ -58,7 +58,7 @@ class PluginAdvertiserEditorNotificationProvider : EditorNotificationProvider, LOG.debug("Tried to update extensions cache for file '${file.name}'. shouldUpdateNotifications=$shouldUpdateNotifications") } - return EditorNotificationProvider.CONST_NULL + return null } return suggestionData diff --git a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.kt b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.kt index 68d127415341..8a14fcf94c29 100644 --- a/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.kt +++ b/platform/platform-impl/src/com/intellij/ui/EditorNotificationsImpl.kt @@ -6,8 +6,8 @@ package com.intellij.ui import com.intellij.ProjectTopics import com.intellij.diagnostic.PluginException import com.intellij.ide.impl.runUnderModalProgressIfIsEdt +import com.intellij.openapi.Disposable import com.intellij.openapi.application.* -import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.extensions.ExtensionPointListener import com.intellij.openapi.extensions.PluginDescriptor @@ -26,11 +26,11 @@ import com.intellij.psi.PsiFile import com.intellij.refactoring.listeners.RefactoringElementAdapter import com.intellij.refactoring.listeners.RefactoringElementListener import com.intellij.refactoring.listeners.RefactoringElementListenerProvider +import com.intellij.util.SingleAlarm +import com.intellij.util.childScope import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.containers.CollectionFactory import com.intellij.util.ui.UIUtil -import com.intellij.util.ui.update.MergingUpdateQueue -import com.intellij.util.ui.update.Update import kotlinx.coroutines.* import org.jetbrains.annotations.TestOnly import org.jetbrains.annotations.VisibleForTesting @@ -39,10 +39,14 @@ import java.util.concurrent.CancellationException import java.util.function.BiFunction import javax.swing.JComponent -class EditorNotificationsImpl(private val project: Project) : EditorNotifications() { - private val updateMerger = MergingUpdateQueue("EditorNotifications update merger", 100, true, null, project).usePassThroughInUnitTestMode() +class EditorNotificationsImpl(private val project: Project) : EditorNotifications(), Disposable { + private val updateAllAlarm = SingleAlarm(::doUpdateAllNotifications, 100, this) private val fileToUpdateNotificationJob = CollectionFactory.createConcurrentWeakMap() + private val fileEditorToMap = + CollectionFactory.createConcurrentWeakMap, JComponent>>() + + private val coroutineScope: CoroutineScope = project.coroutineScope.childScope() init { val connection = project.messageBus.connect() @@ -52,11 +56,11 @@ class EditorNotificationsImpl(private val project: Project) : EditorNotificatio } override fun selectionChanged(event: FileEditorManagerEvent) { - val file = event.newFile - val editor = event.newEditor - if (file != null && editor != null && java.lang.Boolean.TRUE == editor.getUserData(PENDING_UPDATE)) { + val file = event.newFile ?: return + val editor = event.newEditor ?: return + if (editor.getUserData(PENDING_UPDATE) == java.lang.Boolean.TRUE) { editor.putUserData(PENDING_UPDATE, null) - updateEditor(file, editor) + updateEditors(file, listOf(editor)) } } }) @@ -87,142 +91,139 @@ class EditorNotificationsImpl(private val project: Project) : EditorNotificatio }, false, null) } + override fun dispose() { + coroutineScope.cancel() + // help GC + fileToUpdateNotificationJob.clear() + fileEditorToMap.clear() + } + companion object { - private val EDITOR_NOTIFICATION_PROVIDER = - Key.create, JComponent?>>("editor.notification.provider") - private val PENDING_UPDATE = Key.create("pending.notification.update") + } - @VisibleForTesting - @JvmStatic - fun getNotificationPanels(editor: FileEditor): MutableMap, JComponent?> { - editor.getUserData(EDITOR_NOTIFICATION_PROVIDER)?.let { - return it - } + @VisibleForTesting + fun getNotificationPanels(fileEditor: FileEditor): MutableMap, JComponent> { + return fileEditorToMap.computeIfAbsent(fileEditor) { WeakHashMap() } + } - editor.putUserData(EDITOR_NOTIFICATION_PROVIDER, WeakHashMap()) - editor.getUserData(EDITOR_NOTIFICATION_PROVIDER)?.let { - return it - } - val editorClass = editor.javaClass - val pluginException = PluginException.createByClass( - "User data is not supported; editorClass='${editorClass.name}'; key='$EDITOR_NOTIFICATION_PROVIDER'", - null, - editorClass) - Logger.getInstance(editorClass).error(pluginException) - throw pluginException - } - - @TestOnly - @JvmStatic - fun completeAsyncTasks(project: Project) { - runUnderModalProgressIfIsEdt { + @TestOnly + fun completeAsyncTasks() { + runUnderModalProgressIfIsEdt { + val parentJob = coroutineScope.coroutineContext[Job]!! + while (true) { + // process all events in EDT withContext(Dispatchers.EDT) { yield() } - val editorNotificationManager = getInstance(project) as EditorNotificationsImpl - for (job in editorNotificationManager.fileToUpdateNotificationJob.values.toList()) { - try { - job.join() - } - catch (ignore: CancellationException) { - } + val jobs = parentJob.children.toList() + if (jobs.isEmpty()) { + break } + jobs.joinAll() + + // process all events in EDT withContext(Dispatchers.EDT) { yield() } } } + check(fileToUpdateNotificationJob.isEmpty()) } override fun updateNotifications(provider: EditorNotificationProvider) { for (file in FileEditorManager.getInstance(project).openFilesWithRemotes) { - for (editor in getEditors(file)) { - updateNotification(editor, provider, null) + for (editor in getEditors(file).toList()) { + updateNotification(fileEditor = editor, provider = provider, component = null) } } } override fun updateNotifications(file: VirtualFile) { - AppUIExecutor - .onUiThread(ModalityState.any()) - .expireWith(project) - .execute { - if (project.isDisposed || !file.isValid) { - return@execute - } - var editors = getEditors(file) - if (!ApplicationManager.getApplication().isHeadlessEnvironment) { - editors = editors.filter { fileEditor -> - val visible = UIUtil.isShowing(fileEditor.component) - if (!visible) { - fileEditor.putUserData(PENDING_UPDATE, java.lang.Boolean.TRUE) - } - visible - } - } - for (editor in editors) { - updateEditor(file, editor) - } - } - } - - private fun getEditors(file: VirtualFile): List { - return FileEditorManager.getInstance(project).getAllEditors(file).filter { it !is TextEditor || isEditorLoaded(it.editor) } - } - - private fun updateEditor(file: VirtualFile, fileEditor: FileEditor) { - // light project is not disposed in tests - if (project.isDisposed) { - return - } - - val job = project.coroutineScope.launch(start = CoroutineStart.LAZY) { + coroutineScope.launch(Dispatchers.EDT + ModalityState.any().asContextElement()) { if (!file.isValid) { return@launch } + doUpdateNotifications(file) + } + } + + @RequiresEdt + private fun doUpdateNotifications(file: VirtualFile) { + var editors = getEditors(file) + if (!ApplicationManager.getApplication().isHeadlessEnvironment) { + editors = editors.filter { fileEditor -> + val visible = UIUtil.isShowing(fileEditor.component) + if (!visible) { + fileEditor.putUserData(PENDING_UPDATE, java.lang.Boolean.TRUE) + } + visible + } + } + updateEditors(file, editors.toList()) + } + + private fun getEditors(file: VirtualFile): Sequence { + return FileEditorManager.getInstance(project).getAllEditors(file).asSequence().filter { it !is TextEditor || isEditorLoaded(it.editor) } + } + + private fun updateEditors(file: VirtualFile, fileEditors: List) { + val job = coroutineScope.launch(start = CoroutineStart.LAZY) { + // delay for debounce + delay(100) + + if (!file.isValid) { + return@launch + } + + // light project is not disposed in tests + if (project.isDisposed) { + return@launch + } + coroutineContext.ensureActive() - try { - val point = EditorNotificationProvider.EP_NAME.getPoint(project) as ExtensionPointImpl - for (adapter in point.sortedAdapters) { + val point = EditorNotificationProvider.EP_NAME.getPoint(project) as ExtensionPointImpl + for (adapter in point.sortedAdapters) { + coroutineContext.ensureActive() + + try { + val provider = adapter.createInstance(project) ?: continue + coroutineContext.ensureActive() - try { - val provider = adapter.createInstance(project) ?: continue + if (DumbService.isDumb(project) && !DumbService.isDumbAware(provider)) { + continue + } - coroutineContext.ensureActive() - - if (DumbService.isDumb(project) && !DumbService.isDumbAware(provider)) { - continue + val componentProvider = readAction { + if (file.isValid) { + provider.collectNotificationData(project, file) + } + else { + null + } + } ?: continue + withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) { + if (!file.isValid) { + return@withContext } - val componentProvider = readAction { - if (file.isValid) { - provider.collectNotificationData(project, file) - } - else { - null - } - } ?: continue - withContext(Dispatchers.EDT + ModalityState.any().asContextElement()) { + for (fileEditor in fileEditors) { val component = componentProvider.apply(fileEditor) - updateNotification(fileEditor, provider, component) + updateNotification(fileEditor = fileEditor, provider = provider, component = component) } } - catch (e: CancellationException) { - throw e - } - catch (e: Exception) { - val pluginException = if (e is PluginException) e else PluginException(e, adapter.pluginDescriptor.pluginId) - logger().error(pluginException) - } } - } - finally { - fileToUpdateNotificationJob.remove(file, coroutineContext.job) + catch (e: CancellationException) { + throw e + } + catch (e: Exception) { + val pluginException = if (e is PluginException) e else PluginException(e, adapter.pluginDescriptor.pluginId) + logger().error(pluginException) + } } } job.invokeOnCompletion { fileToUpdateNotificationJob.remove(file, job) } @@ -235,11 +236,11 @@ class EditorNotificationsImpl(private val project: Project) : EditorNotificatio } @RequiresEdt - private fun updateNotification(editor: FileEditor, provider: EditorNotificationProvider, component: JComponent?) { - val panels = getNotificationPanels(editor) + private fun updateNotification(fileEditor: FileEditor, provider: EditorNotificationProvider, component: JComponent?) { + val panels = fileEditorToMap.get(fileEditor) val providerClass = provider.javaClass - panels.get(providerClass)?.let { old -> - FileEditorManager.getInstance(project).removeTopComponent(editor, old) + panels?.get(providerClass)?.let { old -> + FileEditorManager.getInstance(project).removeTopComponent(fileEditor, old) } if (component != null) { if (component is EditorNotificationPanel) { @@ -248,9 +249,13 @@ class EditorNotificationsImpl(private val project: Project) : EditorNotificatio } } logNotificationShown(project, provider) - FileEditorManager.getInstance(project).addTopComponent(editor, component) + FileEditorManager.getInstance(project).addTopComponent(fileEditor, component) + + (panels ?: getNotificationPanels(fileEditor)).put(providerClass, component) + } + else { + panels?.remove(providerClass) } - panels.put(providerClass, component) } override fun updateAllNotifications() { @@ -258,14 +263,20 @@ class EditorNotificationsImpl(private val project: Project) : EditorNotificatio throw UnsupportedOperationException("Editor notifications aren't supported for default project") } + if (ApplicationManager.getApplication().isUnitTestMode) { + doUpdateAllNotifications() + } + else { + updateAllAlarm.cancelAndRequest() + } + } + + @RequiresEdt + private fun doUpdateAllNotifications() { val fileEditorManager = FileEditorManager.getInstance(project) ?: throw IllegalStateException("No FileEditorManager for $project") - updateMerger.queue(object : Update("update") { - override fun run() { - for (file in fileEditorManager.openFilesWithRemotes) { - updateNotifications(file) - } - } - }) + for (file in fileEditorManager.openFilesWithRemotes) { + doUpdateNotifications(file) + } } internal class RefactoringListenerProvider : RefactoringElementListenerProvider { @@ -277,10 +288,8 @@ class EditorNotificationsImpl(private val project: Project) : EditorNotificatio return object : RefactoringElementAdapter() { override fun elementRenamedOrMoved(newElement: PsiElement) { if (newElement is PsiFile) { - val vFile = newElement.getContainingFile().virtualFile - if (vFile != null) { - getInstance(element.getProject()).updateNotifications(vFile) - } + val vFile = newElement.getContainingFile().virtualFile ?: return + getInstance(element.getProject()).updateNotifications(vFile) } } diff --git a/plugins/editorconfig/src/org/editorconfig/configmanagement/editor/EditorConfigEditorNotificationProvider.java b/plugins/editorconfig/src/org/editorconfig/configmanagement/editor/EditorConfigEditorNotificationProvider.java index da49fdadee87..07704c4be599 100644 --- a/plugins/editorconfig/src/org/editorconfig/configmanagement/editor/EditorConfigEditorNotificationProvider.java +++ b/plugins/editorconfig/src/org/editorconfig/configmanagement/editor/EditorConfigEditorNotificationProvider.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.editorconfig.configmanagement.editor; import com.intellij.application.options.CodeStyle; @@ -20,14 +20,12 @@ import javax.swing.*; import java.util.function.Function; final class EditorConfigEditorNotificationProvider implements EditorNotificationProvider { - @Override - public @NotNull Function collectNotificationData(@NotNull Project project, + public @Nullable Function collectNotificationData(@NotNull Project project, @NotNull VirtualFile file) { - return file.getFileType().equals(EditorConfigFileType.INSTANCE) && - !getEditorConfigSettings(project).ENABLED ? - fileEditor -> new MyPanel(fileEditor, project) : - CONST_NULL; + return file.getFileType().equals(EditorConfigFileType.INSTANCE) && !getEditorConfigSettings(project).ENABLED + ? fileEditor -> new MyPanel(fileEditor, project) + : null; } private static final class MyPanel extends EditorNotificationPanel { diff --git a/plugins/git4idea/src/git4idea/ui/GitEditorPromo.kt b/plugins/git4idea/src/git4idea/ui/GitEditorPromo.kt index 0d6efb364cd7..e1d196d86070 100644 --- a/plugins/git4idea/src/git4idea/ui/GitEditorPromo.kt +++ b/plugins/git4idea/src/git4idea/ui/GitEditorPromo.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package git4idea.ui import com.intellij.codeInsight.hint.HintUtil @@ -11,46 +11,46 @@ import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.help.HelpManager import com.intellij.openapi.project.Project import com.intellij.openapi.ui.popup.IconButton -import com.intellij.openapi.util.Key import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.EditorNotificationPanel +import com.intellij.ui.EditorNotificationProvider import com.intellij.ui.EditorNotifications import com.intellij.ui.InplaceButton import git4idea.i18n.GitBundle import git4idea.repo.GitRepositoryFiles import git4idea.repo.GitRepositoryManager import java.awt.BorderLayout +import java.util.function.Function +import javax.swing.JComponent -private val KEY: Key = Key.create("GitEditorPromo") private const val PROMO_DISMISSED_KEY = "git.editor.promo.dismissed" -class GitEditorPromo : EditorNotifications.Provider() { - override fun getKey(): Key = KEY +private class GitEditorPromo : EditorNotificationProvider { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { + if (!isEnabled() || !CommandLineWaitingManager.getInstance().hasHookFor(file) || file.name != GitRepositoryFiles.COMMIT_EDITMSG) { + return null + } - override fun createNotificationPanel(file: VirtualFile, - fileEditor: FileEditor, - project: Project): EditorNotificationPanel? { - return if (isEnabled() && CommandLineWaitingManager.getInstance().hasHookFor(file) - && file.name == GitRepositoryFiles.COMMIT_EDITMSG) { - EditorNotificationPanel(HintUtil.PROMOTION_PANE_KEY, EditorNotificationPanel.Status.Info).apply { - icon(AllIcons.Ide.Gift) - text = GitBundle.message("editor.promo.commit.text", ApplicationNamesInfo.getInstance().fullProductName) - val repository = GitRepositoryManager.getInstance(project).repositories.find { it.repositoryFiles.isCommitMessageFile(file.path) } - if (repository != null) { - createActionLabel(GitBundle.message("editor.promo.commit.try.link"), IdeActions.ACTION_CHECKIN_PROJECT, false) + return Function { + val panel = EditorNotificationPanel(HintUtil.PROMOTION_PANE_KEY, EditorNotificationPanel.Status.Info) + panel.icon(AllIcons.Ide.Gift) + panel.text = GitBundle.message("editor.promo.commit.text", ApplicationNamesInfo.getInstance().fullProductName) + val repository = GitRepositoryManager.getInstance(project).repositories.find { it.repositoryFiles.isCommitMessageFile(file.path) } + if (repository == null) { + panel.createActionLabel(GitBundle.message("editor.promo.help.link")) { + HelpManager.getInstance().invokeHelp("Commit and push changes") } - else { - createActionLabel(GitBundle.message("editor.promo.help.link")) { - HelpManager.getInstance().invokeHelp("Commit and push changes") - } - } - add(InplaceButton(IconButton(GitBundle.message("editor.promo.close.link"), AllIcons.Actions.Close, AllIcons.Actions.CloseHovered)) { + } + else { + panel.createActionLabel(GitBundle.message("editor.promo.commit.try.link"), IdeActions.ACTION_CHECKIN_PROJECT, false) + } + panel.add( + InplaceButton(IconButton(GitBundle.message("editor.promo.close.link"), AllIcons.Actions.Close, AllIcons.Actions.CloseHovered)) { PropertiesComponent.getInstance().setValue(PROMO_DISMISSED_KEY, true) - EditorNotifications.getInstance(project).updateNotifications(this@GitEditorPromo) + EditorNotifications.getInstance(project).updateNotifications(this) }, BorderLayout.EAST) - } + panel } - else null } private fun isEnabled(): Boolean = !PropertiesComponent.getInstance().getBoolean(PROMO_DISMISSED_KEY) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/config/ConfigureGroovyLibraryNotificationProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/config/ConfigureGroovyLibraryNotificationProvider.java index dc28980538ad..b368bc00b90f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/config/ConfigureGroovyLibraryNotificationProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/config/ConfigureGroovyLibraryNotificationProvider.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.plugins.groovy.config; import com.intellij.openapi.compiler.CompilerManager; @@ -30,37 +30,36 @@ import java.util.function.Function; final class ConfigureGroovyLibraryNotificationProvider implements EditorNotificationProvider { @Override - public @NotNull Function collectNotificationData(@NotNull Project project, + public @Nullable Function collectNotificationData(@NotNull Project project, @NotNull VirtualFile file) { try { if (!file.getFileType().equals(GroovyFileType.GROOVY_FILE_TYPE)) { - return CONST_NULL; + return null; } // do not show the panel for Gradle build scripts // expecting groovy library to always be available at the gradle distribution if (StringUtil.endsWith(file.getName(), ".gradle") || CompilerManager.getInstance(project).isExcludedFromCompilation(file)) { - return CONST_NULL; + return null; } final Module module = ModuleUtilCore.findModuleForFile(file, project); - if (module == null || - isMavenModule(module)) { - return CONST_NULL; + if (module == null || isMavenModule(module)) { + return null; } for (GroovyFrameworkConfigNotification configNotification : GroovyFrameworkConfigNotification.EP_NAME.getExtensions()) { if (configNotification.hasFrameworkStructure(module)) { return configNotification.hasFrameworkLibrary(module) ? - CONST_NULL : + null : fileEditor -> createConfigureNotificationPanel(module, fileEditor); } } - return CONST_NULL; + return null; } catch (ProcessCanceledException | IndexNotReadyException ignored) { - return CONST_NULL; + return null; } } diff --git a/plugins/kotlin/base/fe10/highlighting/src/org/jetbrains/kotlin/idea/base/fe10/highlighting/suspender/KotlinHighlightingSuspendNotificationProvider.kt b/plugins/kotlin/base/fe10/highlighting/src/org/jetbrains/kotlin/idea/base/fe10/highlighting/suspender/KotlinHighlightingSuspendNotificationProvider.kt index e50837bd698a..26544e0c0751 100644 --- a/plugins/kotlin/base/fe10/highlighting/src/org/jetbrains/kotlin/idea/base/fe10/highlighting/suspender/KotlinHighlightingSuspendNotificationProvider.kt +++ b/plugins/kotlin/base/fe10/highlighting/src/org/jetbrains/kotlin/idea/base/fe10/highlighting/suspender/KotlinHighlightingSuspendNotificationProvider.kt @@ -12,12 +12,12 @@ import java.util.function.Function import javax.swing.JComponent internal class KotlinHighlightingSuspendNotificationProvider : EditorNotificationProvider { - override fun collectNotificationData(project: Project, file: VirtualFile): Function { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { if (!file.isKotlinFileType()) { - return EditorNotificationProvider.CONST_NULL + return null } - if (!KotlinHighlightingSuspender.getInstance(project).isSuspended(file)) return EditorNotificationProvider.CONST_NULL + if (!KotlinHighlightingSuspender.getInstance(project).isSuspended(file)) return null return Function { EditorNotificationPanel(it, EditorNotificationPanel.Status.Warning).apply { diff --git a/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/scripting/GradleScriptNotificationProvider.kt b/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/scripting/GradleScriptNotificationProvider.kt index 9b1a908241a6..12aa9f31f9dc 100644 --- a/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/scripting/GradleScriptNotificationProvider.kt +++ b/plugins/kotlin/gradle/gradle-java/src/org/jetbrains/kotlin/idea/gradleJava/scripting/GradleScriptNotificationProvider.kt @@ -13,7 +13,6 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.projectImport.ProjectImportProvider import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import com.intellij.ui.EditorNotificationProvider.CONST_NULL import org.jetbrains.annotations.Nls import org.jetbrains.kotlin.idea.base.scripting.KotlinBaseScriptingBundle import org.jetbrains.kotlin.idea.gradle.KotlinIdeaGradleBundle @@ -34,14 +33,14 @@ internal class GradleScriptNotificationProvider : EditorNotificationProvider { override fun collectNotificationData( project: Project, file: VirtualFile, - ): Function { + ): Function? { if (!isGradleKotlinScript(file) || !file.isKotlinFileType()) { - return CONST_NULL + return null } val standaloneScriptActions = GradleStandaloneScriptActionsManager.getInstance(project) val rootsManager = GradleBuildRootsManager.getInstance(project) - val scriptUnderRoot = rootsManager?.findScriptBuildRoot(file) ?: return CONST_NULL + val scriptUnderRoot = rootsManager?.findScriptBuildRoot(file) ?: return null // todo: this actions will be usefull only when gradle fix https://github.com/gradle/gradle/issues/12640 fun EditorNotificationPanel.showActionsToFixNotEvaluated() { diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/MultipleScriptDefinitionsChecker.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/MultipleScriptDefinitionsChecker.kt index 0a2723eb5003..0ff9fb2ee324 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/MultipleScriptDefinitionsChecker.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/MultipleScriptDefinitionsChecker.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.script.configuration @@ -13,7 +13,6 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiManager import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import com.intellij.ui.EditorNotificationProvider.* import com.intellij.ui.EditorNotifications import com.intellij.ui.HyperlinkLabel import org.jetbrains.annotations.Nls @@ -33,13 +32,13 @@ import javax.swing.JComponent class MultipleScriptDefinitionsChecker : EditorNotificationProvider { - override fun collectNotificationData(project: Project, file: VirtualFile): Function { - if (!file.isKotlinFileType()) return CONST_NULL + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { + if (!file.isKotlinFileType()) return null - val ktFile = PsiManager.getInstance(project).findFile(file).safeAs()?.takeIf(KtFile::isScript) ?: return CONST_NULL + val ktFile = PsiManager.getInstance(project).findFile(file).safeAs()?.takeIf(KtFile::isScript) ?: return null if (KotlinScriptingSettings.getInstance(project).suppressDefinitionsCheck || - !ScriptDefinitionsManager.getInstance(project).isReady()) return CONST_NULL + !ScriptDefinitionsManager.getInstance(project).isReady()) return null val allApplicableDefinitions = ScriptDefinitionsManager.getInstance(project) .getAllDefinitions() @@ -48,7 +47,7 @@ class MultipleScriptDefinitionsChecker : EditorNotificationProvider { KotlinScriptingSettings.getInstance(project).isScriptDefinitionEnabled(it) } .toList() - if (allApplicableDefinitions.size < 2 || areDefinitionsForGradleKts(allApplicableDefinitions)) return CONST_NULL + if (allApplicableDefinitions.size < 2 || areDefinitionsForGradleKts(allApplicableDefinitions)) return null return Function { fileEditor: FileEditor -> createNotification(fileEditor, project, allApplicableDefinitions) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportChecker.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportChecker.kt index 82a1de5d5f64..e10217d3a462 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportChecker.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/script/configuration/ScriptingSupportChecker.kt @@ -3,7 +3,6 @@ package org.jetbrains.kotlin.idea.script.configuration import com.intellij.ide.BrowserUtil import com.intellij.ide.scratch.ScratchUtil -import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project @@ -12,7 +11,6 @@ import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import org.jetbrains.annotations.Nls import org.jetbrains.kotlin.idea.base.resources.KotlinBundle import org.jetbrains.kotlin.idea.core.script.settings.KotlinScriptingSettings import org.jetbrains.kotlin.idea.util.KOTLIN_AWARE_SOURCE_ROOT_TYPES @@ -21,14 +19,14 @@ import java.util.function.Function import javax.swing.JComponent class ScriptingSupportChecker: EditorNotificationProvider { - override fun collectNotificationData(project: Project, file: VirtualFile): Function { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { if (!Registry.`is`("kotlin.scripting.support.warning") || file.isNonScript() || ScratchUtil.isScratch(file)) { - return EditorNotificationProvider.CONST_NULL + return null } // warning panel is hidden if (!KotlinScriptingSettings.getInstance(project).showSupportWarning) { - return EditorNotificationProvider.CONST_NULL + return null } val providers = ScriptingSupportCheckerProvider.CHECKER_PROVIDERS.getExtensionList(project) @@ -70,7 +68,7 @@ class ScriptingSupportChecker: EditorNotificationProvider { } } - return EditorNotificationProvider.CONST_NULL + return null } } diff --git a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinAlternativeSourceNotificationProvider.kt b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinAlternativeSourceNotificationProvider.kt index e36c7436a184..e6881f68bfa8 100644 --- a/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinAlternativeSourceNotificationProvider.kt +++ b/plugins/kotlin/jvm-debugger/core/src/org/jetbrains/kotlin/idea/debugger/core/KotlinAlternativeSourceNotificationProvider.kt @@ -20,7 +20,6 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiManager import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import com.intellij.ui.EditorNotificationProvider.CONST_NULL import com.intellij.xdebugger.impl.ui.DebuggerUIUtil import org.jetbrains.kotlin.idea.base.facet.platform.platform import org.jetbrains.kotlin.idea.base.indices.KotlinPackageIndexUtils.findFilesWithExactPackage @@ -35,33 +34,33 @@ import javax.swing.JComponent class KotlinAlternativeSourceNotificationProvider : EditorNotificationProvider { - override fun collectNotificationData(project: Project, file: VirtualFile): Function { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { if (!DebuggerSettings.getInstance().SHOW_ALTERNATIVE_SOURCE) { - return CONST_NULL + return null } val javaSession = DebuggerManagerEx.getInstanceEx(project).context.debuggerSession val session = javaSession?.xDebugSession if (session == null) { AlternativeSourceNotificationProvider.setFileProcessed(file, false) - return CONST_NULL + return null } val position = session.currentPosition if (file != position?.file) { AlternativeSourceNotificationProvider.setFileProcessed(file, false) - return CONST_NULL + return null } - if (DumbService.getInstance(project).isDumb) return CONST_NULL + if (DumbService.getInstance(project).isDumb) return null - val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return CONST_NULL + val ktFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return null val alternativeKtFiles = findAlternativeKtFiles(ktFile, project, javaSession) AlternativeSourceNotificationProvider.setFileProcessed(file, true) if (alternativeKtFiles.size <= 1) { - return CONST_NULL + return null } val currentFirstAlternatives: Collection = listOf(ktFile) + alternativeKtFiles.filter { it != ktFile } diff --git a/plugins/kotlin/jvm/src/org/jetbrains/kotlin/idea/inspections/JavaOutsideModuleDetector.kt b/plugins/kotlin/jvm/src/org/jetbrains/kotlin/idea/inspections/JavaOutsideModuleDetector.kt index 22ffee1b9fa4..65b7bc78c5f5 100644 --- a/plugins/kotlin/jvm/src/org/jetbrains/kotlin/idea/inspections/JavaOutsideModuleDetector.kt +++ b/plugins/kotlin/jvm/src/org/jetbrains/kotlin/idea/inspections/JavaOutsideModuleDetector.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.inspections @@ -11,7 +11,6 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import com.intellij.ui.EditorNotificationProvider.CONST_NULL import org.jetbrains.kotlin.idea.KotlinJvmBundle import org.jetbrains.kotlin.idea.base.util.isGradleModule import org.jetbrains.kotlin.idea.facet.KotlinFacet @@ -21,16 +20,16 @@ import javax.swing.JComponent class JavaOutsideModuleDetector : EditorNotificationProvider { - override fun collectNotificationData(project: Project, file: VirtualFile): Function { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { if (file.extension != JavaFileType.DEFAULT_EXTENSION && !FileTypeRegistry.getInstance().isFileOfType(file, JavaFileType.INSTANCE)) { - return CONST_NULL + return null } - val module = ModuleUtilCore.findModuleForFile(file, project)?.takeIf { it.isGradleModule } ?: return CONST_NULL - val facetSettings = KotlinFacet.get(module)?.configuration?.settings ?: return CONST_NULL + val module = ModuleUtilCore.findModuleForFile(file, project)?.takeIf { it.isGradleModule } ?: return null + val facetSettings = KotlinFacet.get(module)?.configuration?.settings ?: return null val filePath = file.path val nonKotlinPath = module.sourceRoots.map { it.path } - facetSettings.pureKotlinSourceFolders - if (nonKotlinPath.any { filePath.startsWith(it) }) return CONST_NULL + if (nonKotlinPath.any { filePath.startsWith(it) }) return null return Function { EditorNotificationPanel(it, EditorNotificationPanel.Status.Warning).apply { diff --git a/plugins/kotlin/plugin-updater/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.kt b/plugins/kotlin/plugin-updater/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.kt index 740d2400009d..5a1a6e79b7e3 100644 --- a/plugins/kotlin/plugin-updater/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.kt +++ b/plugins/kotlin/plugin-updater/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.kt @@ -7,7 +7,6 @@ import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.compiler.CompilerManager import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileEditor.OpenFileDescriptor -import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.DumbService @@ -18,7 +17,6 @@ import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.ui.popup.PopupStep import com.intellij.openapi.ui.popup.util.BaseListPopupStep import com.intellij.openapi.util.NlsContexts -import com.intellij.openapi.util.Ref import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile @@ -27,7 +25,6 @@ import com.intellij.ui.EditorNotificationProvider.* import org.jetbrains.annotations.Nls import org.jetbrains.annotations.NotNull import org.jetbrains.kotlin.idea.* -import org.jetbrains.kotlin.idea.base.facet.platform.platform import org.jetbrains.kotlin.idea.base.util.createComponentActionLabel import org.jetbrains.kotlin.idea.compiler.configuration.KotlinIdePlugin import org.jetbrains.kotlin.idea.compiler.configuration.KotlinPluginLayout @@ -39,8 +36,6 @@ import org.jetbrains.kotlin.idea.util.application.invokeLater import org.jetbrains.kotlin.idea.util.application.isUnitTestMode import org.jetbrains.kotlin.idea.util.isKotlinFileType import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion -import org.jetbrains.kotlin.platform.js.isJs -import org.jetbrains.kotlin.platform.jvm.isJvm import java.awt.event.ComponentAdapter import java.awt.event.ComponentEvent import java.util.function.Function @@ -215,9 +210,9 @@ class UnsupportedAbiVersionNotificationPanelProvider : EditorNotificationProvide } } - override fun collectNotificationData(project: Project, file: VirtualFile): Function { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { if (!file.isKotlinFileType()) { - return CONST_NULL + return null } try { if ( @@ -226,14 +221,14 @@ class UnsupportedAbiVersionNotificationPanelProvider : EditorNotificationProvide || CompilerManager.getInstance(project).isExcludedFromCompilation(file) || KotlinNotConfiguredSuppressedModulesState.isSuppressed(project) ) { - return CONST_NULL + return null } - val module = ModuleUtilCore.findModuleForFile(file, project) ?: return CONST_NULL + val module = ModuleUtilCore.findModuleForFile(file, project) ?: return null val badRoots: Collection> = getLibraryRootsWithIncompatibleAbi(module) .takeUnless(Collection>::isEmpty) - ?: return CONST_NULL + ?: return null return Function { doCreate(it, project, badRoots) } } catch (e: ProcessCanceledException) { @@ -242,7 +237,7 @@ class UnsupportedAbiVersionNotificationPanelProvider : EditorNotificationProvide DumbService.getInstance(project).runWhenSmart { updateNotifications(project) } } - return CONST_NULL + return null } private fun findBadRootsInRuntimeLibraries( diff --git a/plugins/kotlin/project-configuration/src/org/jetbrains/kotlin/idea/configuration/KotlinSetupEnvironmentNotificationProvider.kt b/plugins/kotlin/project-configuration/src/org/jetbrains/kotlin/idea/configuration/KotlinSetupEnvironmentNotificationProvider.kt index 89f71fd7f39d..bef750da6bea 100644 --- a/plugins/kotlin/project-configuration/src/org/jetbrains/kotlin/idea/configuration/KotlinSetupEnvironmentNotificationProvider.kt +++ b/plugins/kotlin/project-configuration/src/org/jetbrains/kotlin/idea/configuration/KotlinSetupEnvironmentNotificationProvider.kt @@ -20,7 +20,6 @@ import com.intellij.psi.PsiFile import com.intellij.psi.PsiManager import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import com.intellij.ui.EditorNotificationProvider.CONST_NULL import com.intellij.ui.EditorNotifications import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.idea.base.facet.platform.platform @@ -39,19 +38,19 @@ import javax.swing.JComponent // Code is partially copied from com.intellij.codeInsight.daemon.impl.SetupSDKNotificationProvider class KotlinSetupEnvironmentNotificationProvider : EditorNotificationProvider { - override fun collectNotificationData(project: Project, file: VirtualFile): Function { + override fun collectNotificationData(project: Project, file: VirtualFile): Function? { if (!file.isKotlinFileType()) { - return CONST_NULL + return null } - val psiFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return CONST_NULL + val psiFile = PsiManager.getInstance(project).findFile(file) as? KtFile ?: return null if (psiFile.language !== KotlinLanguage.INSTANCE) { - return CONST_NULL + return null } - val module = ModuleUtilCore.findModuleForPsiElement(psiFile) ?: return CONST_NULL + val module = ModuleUtilCore.findModuleForPsiElement(psiFile) ?: return null if (!ModuleRootManager.getInstance(module).fileIndex.isInSourceContent(file)) { - return CONST_NULL + return null } if (ModuleRootManager.getInstance(module).sdk == null && psiFile.platform.isJvm()) { @@ -68,7 +67,7 @@ class KotlinSetupEnvironmentNotificationProvider : EditorNotificationProvider { return createKotlinNotConfiguredPanel(module, getAbleToRunConfigurators(module).toList()) } - return CONST_NULL + return null } companion object { diff --git a/python/src/com/jetbrains/python/sdk/PyEditorNotificationProvider.java b/python/src/com/jetbrains/python/sdk/PyEditorNotificationProvider.java index f03af66bf510..be930ecac58f 100644 --- a/python/src/com/jetbrains/python/sdk/PyEditorNotificationProvider.java +++ b/python/src/com/jetbrains/python/sdk/PyEditorNotificationProvider.java @@ -100,14 +100,14 @@ public final class PyEditorNotificationProvider implements DumbAware, EditorNoti } @Override - public @NotNull Function collectNotificationData(@NotNull Project project, + public @Nullable Function collectNotificationData(@NotNull Project project, @NotNull VirtualFile file) { PyFile node = getPyFile(project, file); if (node == null) { - return CONST_NULL; + return null; } Module module = guessModule(node); - if (module == null || isFileIgnored(node)) return CONST_NULL; + if (module == null || isFileIgnored(node)) return null; final Sdk sdk = PythonSdkUtil.findPythonSdk(module); final boolean pyCharm = PythonIdeLanguageCustomization.isMainlyPythonIde(); @@ -165,7 +165,7 @@ public final class PyEditorNotificationProvider implements DumbAware, EditorNoti } } } - return CONST_NULL; + return null; } private static Function registerProblemWithCommonFixes(PyFile node, diff --git a/python/testSrc/com/jetbrains/python/sdk/PythonNoSdkEditorNotificationTest.kt b/python/testSrc/com/jetbrains/python/sdk/PythonNoSdkEditorNotificationTest.kt index 49fdb6740f2b..bdddf36c4ada 100644 --- a/python/testSrc/com/jetbrains/python/sdk/PythonNoSdkEditorNotificationTest.kt +++ b/python/testSrc/com/jetbrains/python/sdk/PythonNoSdkEditorNotificationTest.kt @@ -1,14 +1,18 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +@file:Suppress("ReplaceGetOrSet") + package com.jetbrains.python.sdk import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.util.Disposer import com.intellij.psi.PsiFile +import com.intellij.testFramework.PlatformTestUtil import com.intellij.testFramework.fixtures.CodeInsightTestFixture +import com.intellij.ui.EditorNotifications import com.intellij.ui.EditorNotificationsImpl -import com.intellij.util.ui.UIUtil import com.jetbrains.extensions.getSdk import com.jetbrains.python.fixtures.PyTestCase import org.assertj.core.api.AssertionsForClassTypes @@ -32,7 +36,7 @@ class PythonNoSdkEditorNotificationTest : PyTestCase() { } val editor = openFileInEditor(fileName, fileText, fixture) - val notificationPanel = getNotificationPanel(editor) + val notificationPanel = getNotificationPanel(editor, fixture.project) if (!sdkPresent && notificationPanel == null) { fail("\"No SDK notification\" expected") } @@ -45,13 +49,15 @@ class PythonNoSdkEditorNotificationTest : PyTestCase() { } } - private fun getNotificationPanel(fileEditor: FileEditor): JComponent? { - return EditorNotificationsImpl.getNotificationPanels(fileEditor).get(PyEditorNotificationProvider::class.java) + private fun getNotificationPanel(fileEditor: FileEditor, project: Project): JComponent? { + return (EditorNotifications.getInstance(project) as EditorNotificationsImpl).getNotificationPanels(fileEditor) + .get(PyEditorNotificationProvider::class.java) } - fun openFileInEditor(fileName: String, fileText: String, fixture: CodeInsightTestFixture) : FileEditor { - UIUtil.dispatchAllInvocationEvents() - EditorNotificationsImpl.completeAsyncTasks(fixture.getProject()) + private fun openFileInEditor(fileName: String, fileText: String, fixture: CodeInsightTestFixture) : FileEditor { + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue() + val editorNotifications = EditorNotifications.getInstance(fixture.project) as EditorNotificationsImpl + editorNotifications.completeAsyncTasks() val psiFile: PsiFile = fixture.configureByText(fileName, fileText) val fileEditorManager = FileEditorManager.getInstance(fixture.project) @@ -63,8 +69,8 @@ class PythonNoSdkEditorNotificationTest : PyTestCase() { val editors = fileEditorManager.openFile(virtualFile, true) AssertionsForClassTypes.assertThat(editors).hasSize(1) - UIUtil.dispatchAllInvocationEvents() - EditorNotificationsImpl.completeAsyncTasks(fixture.getProject()) + PlatformTestUtil.dispatchAllInvocationEventsInIdeEventQueue() + editorNotifications.completeAsyncTasks() return editors[0] }