don't schedule EDT activity if editor notification provider is not applicable

GitOrigin-RevId: b117f5f780e5bc2a011451af37e768aa133acc1e
This commit is contained in:
Vladimir Krivosheev
2022-08-23 19:20:33 +00:00
committed by intellij-monorepo-bot
parent 3bcda88d84
commit cd70ce6049
24 changed files with 313 additions and 309 deletions
@@ -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<Boolean> FILE_PROCESSED_KEY = Key.create("AlternativeSourceCheckDone");
@Override
public @NotNull Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> 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) {
@@ -187,12 +187,12 @@ public final class SnippetEditorDecorator implements EditorNotificationProvider
}
@Override
public @NotNull Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> 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;
}
}
@@ -80,10 +80,10 @@ final class AttachSourcesNotificationProvider implements EditorNotificationProvi
}
@Override
public @NotNull Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
@NotNull VirtualFile file) {
if (!FileTypeRegistry.getInstance().isFileOfType(file, JavaClassFileType.INSTANCE)) {
return CONST_NULL;
return null;
}
String classFileInfo = getTextWithClassFileInfo(file);
@@ -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<in FileEditor, out JComponent?> {
): Function<in FileEditor, out JComponent?>? {
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 {
@@ -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];
}
@@ -67,21 +67,23 @@ public class UnknownSdkTrackerTest extends JavaCodeInsightFixtureTestCase {
public void testMissingModuleJdk() {
setModuleSdk("missingSDK", JavaSdk.getInstance());
final List<String> fixes = detectMissingSdks();
List<String> 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<String> fixes = detectMissingSdks();
List<String> 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<String> 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<String> detectMissingSdks() {
UnknownSdkTracker.getInstance(getProject()).updateUnknownSdks();
ArrayList<String> infos = new ArrayList<>();
List<String> infos = new ArrayList<>();
EditorNotificationPanel sdkNotification =
SdkSetupNotificationTestBase.runOnText(myFixture, "Sample.java", "class Sample { java.lang.String foo; }");
if (sdkNotification != null) {
@@ -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<EditorNotificationPanel>() {
private val KEY: Key<EditorNotificationPanel> = Key.create("RunConfigEditorNotificationProvider")
override fun getKey(): Key<EditorNotificationPanel> = 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<in FileEditor, out JComponent?>? {
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
}
}
}
@@ -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<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
@NotNull VirtualFile file) {
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> 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
@@ -22,13 +22,16 @@ import java.util.function.Function;
* </p>
*/
public interface EditorNotificationProvider {
ProjectExtensionPointName<EditorNotificationProvider> EP_NAME =
new ProjectExtensionPointName<>("com.intellij.editorNotificationProvider");
/**
* @deprecated Use {@code null}.
*/
@Deprecated
Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> CONST_NULL = __ -> null;
@RequiresReadLock
@NotNull Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
@NotNull VirtualFile file);
@Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
@NotNull VirtualFile file);
}
@@ -34,7 +34,7 @@ class PluginAdvertiserEditorNotificationProvider : EditorNotificationProvider,
override fun collectNotificationData(
project: Project,
file: VirtualFile,
): Function<in FileEditor, out JComponent?> {
): Function<in FileEditor, out JComponent?>? {
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
@@ -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<VirtualFile, Job>()
private val fileEditorToMap =
CollectionFactory.createConcurrentWeakMap<FileEditor, MutableMap<Class<out EditorNotificationProvider>, 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<MutableMap<Class<out EditorNotificationProvider>, JComponent?>>("editor.notification.provider")
private val PENDING_UPDATE = Key.create<Boolean>("pending.notification.update")
}
@VisibleForTesting
@JvmStatic
fun getNotificationPanels(editor: FileEditor): MutableMap<Class<out EditorNotificationProvider>, JComponent?> {
editor.getUserData(EDITOR_NOTIFICATION_PROVIDER)?.let {
return it
}
@VisibleForTesting
fun getNotificationPanels(fileEditor: FileEditor): MutableMap<Class<out EditorNotificationProvider>, 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<FileEditor> {
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<FileEditor> {
return FileEditorManager.getInstance(project).getAllEditors(file).asSequence().filter { it !is TextEditor || isEditorLoaded(it.editor) }
}
private fun updateEditors(file: VirtualFile, fileEditors: List<FileEditor>) {
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<EditorNotificationProvider>
for (adapter in point.sortedAdapters) {
val point = EditorNotificationProvider.EP_NAME.getPoint(project) as ExtensionPointImpl<EditorNotificationProvider>
for (adapter in point.sortedAdapters) {
coroutineContext.ensureActive()
try {
val provider = adapter.createInstance<EditorNotificationProvider>(project) ?: continue
coroutineContext.ensureActive()
try {
val provider = adapter.createInstance<EditorNotificationProvider>(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<EditorNotificationsImpl>().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<EditorNotificationsImpl>().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)
}
}
@@ -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<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> 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 {
@@ -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<EditorNotificationPanel> = Key.create("GitEditorPromo")
private const val PROMO_DISMISSED_KEY = "git.editor.promo.dismissed"
class GitEditorPromo : EditorNotifications.Provider<EditorNotificationPanel>() {
override fun getKey(): Key<EditorNotificationPanel> = KEY
private class GitEditorPromo : EditorNotificationProvider {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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)
@@ -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<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> 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;
}
}
@@ -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<in FileEditor, out JComponent?> {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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 {
@@ -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<in FileEditor, out JComponent?> {
): Function<in FileEditor, out JComponent?>? {
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() {
@@ -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<in FileEditor, out JComponent?> {
if (!file.isKotlinFileType()) return CONST_NULL
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
if (!file.isKotlinFileType()) return null
val ktFile = PsiManager.getInstance(project).findFile(file).safeAs<KtFile>()?.takeIf(KtFile::isScript) ?: return CONST_NULL
val ktFile = PsiManager.getInstance(project).findFile(file).safeAs<KtFile>()?.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)
@@ -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<in FileEditor, out JComponent?> {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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
}
}
@@ -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<in FileEditor, out JComponent?> {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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<KtFile> = listOf(ktFile) + alternativeKtFiles.filter { it != ktFile }
@@ -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<in FileEditor, out JComponent?> {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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 {
@@ -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<in FileEditor, out JComponent?> {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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<BinaryVersionedFile<BinaryVersion>> = getLibraryRootsWithIncompatibleAbi(module)
.takeUnless(Collection<BinaryVersionedFile<BinaryVersion>>::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(
@@ -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<in FileEditor, out JComponent?> {
override fun collectNotificationData(project: Project, file: VirtualFile): Function<in FileEditor, out JComponent?>? {
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 {
@@ -100,14 +100,14 @@ public final class PyEditorNotificationProvider implements DumbAware, EditorNoti
}
@Override
public @NotNull Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> collectNotificationData(@NotNull Project project,
public @Nullable Function<? super @NotNull FileEditor, ? extends @Nullable JComponent> 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<? super @NotNull FileEditor, ? extends @Nullable JComponent> registerProblemWithCommonFixes(PyFile node,
@@ -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]
}