From 341bd3a555516cd7b363ab96c9e245116859e7ac Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 25 Jan 2013 19:32:24 +0400 Subject: [PATCH] Notifications about unsupported ABI versions --- ...gureKotlinLibraryNotificationProvider.java | 162 ++++++++++++++++-- .../quickfix/KotlinRuntimeLibraryUtil.java | 5 +- .../OutdatedKotlinRuntimeNotification.java | 21 ++- 3 files changed, 163 insertions(+), 25 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java index 7d658de130e..7b7c64902e3 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ConfigureKotlinLibraryNotificationProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2012 JetBrains s.r.o. + * Copyright 2010-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.jetbrains.jet.plugin.quickfix; +import com.intellij.ProjectTopics; +import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.fileChooser.FileChooserDescriptor; @@ -23,33 +25,61 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.fileChooser.FileChooserFactory; import com.intellij.openapi.fileChooser.FileTextField; 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; import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootAdapter; +import com.intellij.openapi.roots.ModuleRootEvent; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.EditorNotificationPanel; import com.intellij.ui.EditorNotifications; +import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.plugin.JetFileType; +import org.jetbrains.jet.plugin.versions.OutdatedKotlinRuntimeNotification; import javax.swing.*; +import java.awt.*; import java.io.File; import java.io.IOException; +import java.text.MessageFormat; +import java.util.Collection; public class ConfigureKotlinLibraryNotificationProvider extends EditorNotifications.Provider { private static final Key KEY = Key.create("configure.kotlin.library"); private final Project myProject; + private final Runnable updateNotifications = new Runnable() { + @Override + public void run() { + updateNotifications(); + } + }; public ConfigureKotlinLibraryNotificationProvider(Project project) { myProject = project; + MessageBusConnection connection = myProject.getMessageBus().connect(); + connection.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() { + @Override + public void rootsChanged(ModuleRootEvent event) { + DumbService.getInstance(myProject).runWhenSmart(updateNotifications); + } + }); } @Override @@ -69,21 +99,26 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati if (module == null) return null; if (!KotlinRuntimeLibraryUtil.isModuleAlreadyConfigured(module)) { - return createNotificationPanel(module); + return createConfigureRuntimeLibraryNotificationPanel(module); + } + + Collection badRoots = KotlinRuntimeLibraryUtil.getLibraryRootsWithAbiIncompatibleKotlinClasses(myProject); + if (!badRoots.isEmpty()) { + return createUnsupportedAbiVersionNotificationPanel(badRoots); } } catch (ProcessCanceledException e) { // Ignore } catch (IndexNotReadyException e) { - // Ignore + DumbService.getInstance(myProject).runWhenSmart(updateNotifications); + return null; } return null; } - - private EditorNotificationPanel createNotificationPanel(final Module module) { + private EditorNotificationPanel createConfigureRuntimeLibraryNotificationPanel(final Module module) { final EditorNotificationPanel answer = new EditorNotificationPanel(); answer.setText("Kotlin is not configured for module '" + module.getName() + "'"); @@ -108,21 +143,73 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati Library library = KotlinRuntimeLibraryUtil.findOrCreateRuntimeLibrary(myProject, new UiFindRuntimeLibraryHandler()); if (library == null) return; - KotlinRuntimeLibraryUtil.setUpKotlinRuntimeLibrary(module, library, new Runnable() { - @Override - public void run() { - updateNotifications(); - } - }); + KotlinRuntimeLibraryUtil.setUpKotlinRuntimeLibrary(module, library, updateNotifications); } private void setUpJSModule(@NotNull Module module) { - JsModuleSetUp.doSetUpModule(module, new Runnable() { + JsModuleSetUp.doSetUpModule(module, updateNotifications); + } + + private EditorNotificationPanel createUnsupportedAbiVersionNotificationPanel(final Collection badRoots) { + final EditorNotificationPanel answer = new ErrorNotificationPanel(); + + VirtualFile kotlinRuntimeJar = KotlinRuntimeLibraryUtil.getLocalKotlinRuntimeJar(myProject); + if (kotlinRuntimeJar != null && badRoots.contains(kotlinRuntimeJar)) { + int otherBadRootsCount = badRoots.size() - 1; + String kotlinRuntimeJarName = kotlinRuntimeJar.getPresentableName(); + String text = MessageFormat.format("Kotlin runtime library jar ''{0}'' " + + "{1,choice,0#|1# and one other jar|1< and {1} other jars} " + + "{1,choice,0#has|0", + kotlinRuntimeJarName, + otherBadRootsCount); + answer.setText(text); + answer.createActionLabel("Update " + kotlinRuntimeJarName, new Runnable() { + @Override + public void run() { + KotlinRuntimeLibraryUtil.updateRuntime(myProject, + OutdatedKotlinRuntimeNotification.showRuntimeJarNotFoundDialog(myProject)); + } + }); + if (otherBadRootsCount > 0) { + createShowPathsActionLabel(answer, "Show all"); + } + } + else if (badRoots.size() == 1) { + final VirtualFile root = badRoots.iterator().next(); + String presentableName = root.getPresentableName(); + answer.setText("Kotlin library '" + presentableName + "' " + + "has an unsupported format. Please update the library or the plugin"); + + answer.createActionLabel("Go to " + presentableName, new Runnable() { + @Override + public void run() { + navigateToLibraryRoot(myProject, root); + } + }); + } + else { + answer.setText("Some Kotlin libraries attached to this project have unsupported format. Please update the libraries or the plugin"); + + createShowPathsActionLabel(answer, "Show paths"); + } + return answer; + } + + private void createShowPathsActionLabel(EditorNotificationPanel answer, String labelText) { + final Ref label = new Ref(null); + Runnable action = new Runnable() { @Override public void run() { - updateNotifications(); + Collection badRoots = + KotlinRuntimeLibraryUtil.getLibraryRootsWithAbiIncompatibleKotlinClasses(myProject); + assert !badRoots.isEmpty() : "This action should only be called when bad roots are present"; + + LibraryRootsPopupModel listPopupModel = new LibraryRootsPopupModel("Unsupported format", myProject, badRoots); + ListPopup popup = JBPopupFactory.getInstance().createListPopup(listPopupModel); + popup.showUnderneathOf(label.get()); } - }); + }; + label.set(answer.createActionLabel(labelText, action)); } private void updateNotifications() { @@ -188,5 +275,52 @@ public class ConfigureKotlinLibraryNotificationProvider extends EditorNotificati public void ioExceptionOnCopyingJar(@NotNull IOException e) { Messages.showErrorDialog(myProject, "Error copying jar: " + e.getLocalizedMessage(), "Error Copying File"); } + + } + + private static void navigateToLibraryRoot(Project project, @NotNull VirtualFile root) { + new OpenFileDescriptor(project, root).navigate(true); + } + + private static class LibraryRootsPopupModel extends BaseListPopupStep { + + private final Project project; + + public LibraryRootsPopupModel(@NotNull String title, @NotNull Project project, @NotNull Collection roots) { + super(title, roots.toArray(new VirtualFile[roots.size()])); + this.project = project; + } + + @NotNull + @Override + public String getTextFor(VirtualFile root) { + String relativePath = VfsUtilCore.getRelativePath(root, project.getBaseDir(), '/'); + return relativePath != null ? relativePath : root.getPath(); + } + + @Override + public Icon getIconFor(VirtualFile aValue) { + if (aValue.isDirectory()) { + return AllIcons.Nodes.Folder; + } + return AllIcons.FileTypes.Archive; + } + + @Override + public PopupStep onChosen(VirtualFile selectedValue, boolean finalChoice) { + navigateToLibraryRoot(project, selectedValue); + return FINAL_CHOICE; + } + + @Override + public boolean isSpeedSearchEnabled() { + return true; + } + } + + private static class ErrorNotificationPanel extends EditorNotificationPanel { + public ErrorNotificationPanel() { + myLabel.setIcon(AllIcons.General.Error); + } } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java b/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java index b62b91b5e0d..d3c388eb203 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/KotlinRuntimeLibraryUtil.java @@ -222,8 +222,7 @@ public class KotlinRuntimeLibraryUtil { public static void updateRuntime( @NotNull final Project project, - @NotNull final Runnable jarNotFoundHandler, - @Nullable final Runnable continuation + @NotNull final Runnable jarNotFoundHandler ) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override @@ -242,7 +241,7 @@ public class KotlinRuntimeLibraryUtil { catch (IOException e) { throw new AssertionError(e); } - runtimeJar.refresh(true, true, continuation); + runtimeJar.refresh(true, true); } }); } diff --git a/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java b/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java index bccaf1d5ebd..01cc032275f 100644 --- a/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java +++ b/idea/src/org/jetbrains/jet/plugin/versions/OutdatedKotlinRuntimeNotification.java @@ -66,14 +66,7 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { if ("update".equals(event.getDescription())) { - KotlinRuntimeLibraryUtil.updateRuntime(myProject, new Runnable() { - @Override - public void run() { - Messages.showErrorDialog(myProject, - "kotlin-runtime.jar is not found. Make sure plugin is properly installed.", - "No Runtime Found"); - } - }, null); + KotlinRuntimeLibraryUtil.updateRuntime(myProject, showRuntimeJarNotFoundDialog(myProject)); } else if ("ignore".equals(event.getDescription())) { PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion); @@ -86,4 +79,16 @@ public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent } }), myProject); } + + @NotNull + public static Runnable showRuntimeJarNotFoundDialog(@NotNull final Project project) { + return new Runnable() { + @Override + public void run() { + Messages.showErrorDialog(project, + "kotlin-runtime.jar is not found. Make sure plugin is properly installed.", + "No Runtime Found"); + } + }; + } }