diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 9c5bd36cbe4..a4fcf4efa82 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -22,9 +22,6 @@ org.jetbrains.kotlin.idea.compiler.KotlinCompilerManager - - org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification - org.jetbrains.kotlin.idea.configuration.ui.NonConfiguredKotlinProjectComponent diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt index 568a171e3f1..a1f867ae19b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt @@ -41,7 +41,7 @@ abstract class ConfigureKotlinInProjectAction : AnAction() { val configurators = getApplicableConfigurators(project) when { - configurators.size == 1 -> configurators.first().configure(project) + configurators.size == 1 -> configurators.first().configure(project, emptyList()) configurators.isEmpty() -> Messages.showErrorDialog("There aren't configurators available", e.presentation.text!!) else -> { Messages.showErrorDialog("More than one configurator is available", e.presentation.text!!) diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt index bca67af98ff..fd0b3cf7d6b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt @@ -57,17 +57,14 @@ fun getModulesWithKotlinFiles(project: Project): Collection { fun showConfigureKotlinNotificationIfNeeded(module: Module) { if (isModuleConfigured(module)) return - showConfigureKotlinNotification(module.project) + ConfigureKotlinNotificationManager.notify(module.project) } -fun showConfigureKotlinNotificationIfNeeded(project: Project) { - if (isProjectConfigured(project)) return +fun showConfigureKotlinNotificationIfNeeded(project: Project, excludeModules: List = emptyList()) { + val modules = getModulesWithKotlinFiles(project) - excludeModules + if (modules.all { isModuleConfigured(it) }) return - showConfigureKotlinNotification(project) -} - -private fun showConfigureKotlinNotification(project: Project) { - ConfigureKotlinNotificationManager.notify(project) + ConfigureKotlinNotificationManager.notify(project, excludeModules) } fun getAbleToRunConfigurators(project: Project): Collection { @@ -95,8 +92,8 @@ fun getNonConfiguredModulesWithKotlinFiles(project: Project, configurator: Kotli return modules.filter { module -> configurator.isApplicable(module) && !configurator.isConfigured(module) } } -fun getNonConfiguredModules(project: Project): Collection { - val modulesWithKotlinFiles = getModulesWithKotlinFiles(project) +fun getNonConfiguredModules(project: Project, excludeModules: Collection = emptyList()): Collection { + val modulesWithKotlinFiles = getModulesWithKotlinFiles(project) - excludeModules return modulesWithKotlinFiles.filter { module -> getAbleToRunConfigurators(project).any { !it.isConfigured(module) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt index ee775d55a28..74561918745 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt @@ -18,12 +18,13 @@ package org.jetbrains.kotlin.idea.configuration import com.intellij.notification.Notification import com.intellij.notification.NotificationsManager +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification object ConfigureKotlinNotificationManager: KotlinSingleNotificationManager { - fun notify(project: Project) { - notify(project, ConfigureKotlinNotification(project, ConfigureKotlinNotification.getNotificationString(project))) + fun notify(project: Project, excludeModules: List = emptyList()) { + notify(project, ConfigureKotlinNotification(project, excludeModules)) } } diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinMavenConfigurator.java b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinMavenConfigurator.java index 070a4ce5b2c..92fd2c911a8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinMavenConfigurator.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinMavenConfigurator.java @@ -47,6 +47,7 @@ import org.jetbrains.kotlin.cli.common.KotlinVersion; import org.jetbrains.kotlin.idea.KotlinPluginUtil; import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion; +import java.util.Collection; import java.util.List; public abstract class KotlinMavenConfigurator implements KotlinProjectConfigurator { @@ -109,9 +110,9 @@ public abstract class KotlinMavenConfigurator implements KotlinProjectConfigurat } @Override - public void configure(@NotNull Project project) { + public void configure(@NotNull Project project, Collection excludeModules) { ConfigureDialogWithModulesAndVersion dialog = - new ConfigureDialogWithModulesAndVersion(project, this, KOTLIN_VERSIONS); + new ConfigureDialogWithModulesAndVersion(project, this, KOTLIN_VERSIONS, excludeModules); dialog.show(); if (!dialog.isOK()) return; diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.java b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.java index e93582f6540..808028fbd6e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.java @@ -22,6 +22,8 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.resolve.TargetPlatform; +import java.util.Collection; + public interface KotlinProjectConfigurator { ExtensionPointName EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.projectConfigurator"); @@ -29,7 +31,7 @@ public interface KotlinProjectConfigurator { boolean isApplicable(@NotNull Module module); - void configure(@NotNull Project project); + void configure(@NotNull Project project, Collection excludeModules); @NotNull String getPresentableText(); diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.java b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.java index 7316fe7616c..9cd539c9787 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.java @@ -49,6 +49,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrM import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; import java.io.File; +import java.util.Collection; import static org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtilsKt.showInfoNotification; @@ -84,9 +85,9 @@ public abstract class KotlinWithGradleConfigurator implements KotlinProjectConfi } @Override - public void configure(@NotNull Project project) { + public void configure(@NotNull Project project, Collection excludeModules) { ConfigureDialogWithModulesAndVersion dialog = - new ConfigureDialogWithModulesAndVersion(project, this, KOTLIN_VERSIONS); + new ConfigureDialogWithModulesAndVersion(project, this, KOTLIN_VERSIONS, excludeModules); dialog.show(); if (!dialog.isOK()) return; diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.java b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.java index 6c4adb5eac9..5e3c2b590f7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.java @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.idea.project.ProjectStructureUtil; import java.io.File; import java.util.Arrays; +import java.util.Collection; import java.util.List; import static org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtilsKt.showInfoNotification; @@ -75,7 +76,7 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf } @Override - public void configure(@NotNull Project project) { + public void configure(@NotNull Project project, Collection excludeModules) { String defaultPathToJar = getDefaultPathToJarFile(project); boolean showPathToJarPanel = needToChooseJarPath(project); @@ -83,6 +84,7 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf !ApplicationManager.getApplication().isUnitTestMode() ? ConfigureKotlinInProjectUtilsKt.getNonConfiguredModules(project, this) : Arrays.asList(ModuleManager.getInstance(project).getModules()); + nonConfiguredModules.removeAll(excludeModules); List modulesToConfigure = nonConfiguredModules; String copyLibraryIntoPath = null; @@ -91,7 +93,8 @@ public abstract class KotlinWithLibraryConfigurator implements KotlinProjectConf CreateLibraryDialogWithModules dialog = new CreateLibraryDialogWithModules( project, this, defaultPathToJar, showPathToJarPanel, getDialogTitle(), - getLibraryCaption()); + getLibraryCaption(), + excludeModules); if (!ApplicationManager.getApplication().isUnitTestMode()) { dialog.show(); diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/NonConfiguredKotlinProjectComponent.java b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/NonConfiguredKotlinProjectComponent.java index 2918ca389e3..72d520de3cf 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/NonConfiguredKotlinProjectComponent.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/NonConfiguredKotlinProjectComponent.java @@ -19,10 +19,14 @@ package org.jetbrains.kotlin.idea.configuration.ui; import com.intellij.notification.NotificationDisplayType; import com.intellij.notification.NotificationsConfiguration; import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtilsKt; +import org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotificationKt; + +import java.util.List; public class NonConfiguredKotlinProjectComponent extends AbstractProjectComponent { public static final String CONFIGURE_NOTIFICATION_GROUP_ID = "Configure Kotlin in Project"; @@ -44,7 +48,8 @@ public class NonConfiguredKotlinProjectComponent extends AbstractProjectComponen DumbService.getInstance(myProject).smartInvokeLater(new Runnable() { @Override public void run() { - ConfigureKotlinInProjectUtilsKt.showConfigureKotlinNotificationIfNeeded(myProject); + List modulesWithOutdatedRuntime = OutdatedKotlinRuntimeNotificationKt.checkOutdatedKotlinRuntime(myProject); + ConfigureKotlinInProjectUtilsKt.showConfigureKotlinNotificationIfNeeded(myProject, modulesWithOutdatedRuntime); } }); } diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/notifications/ConfigureKotlinNotification.java b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/notifications/ConfigureKotlinNotification.java index d8ffa6cf107..a8a43cb8b1a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/configuration/ui/notifications/ConfigureKotlinNotification.java +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/ui/notifications/ConfigureKotlinNotification.java @@ -30,19 +30,19 @@ import org.jetbrains.kotlin.idea.configuration.ui.NonConfiguredKotlinProjectComp import javax.swing.event.HyperlinkEvent; import java.util.Collection; +import java.util.List; import static kotlin.collections.CollectionsKt.first; public class ConfigureKotlinNotification extends Notification { private static final String TITLE = "Configure Kotlin"; - @NotNull private final String notificationText; - public ConfigureKotlinNotification( @NotNull final Project project, - @NotNull String notificationText + @NotNull final List excludeModules ) { - super(NonConfiguredKotlinProjectComponent.CONFIGURE_NOTIFICATION_GROUP_ID, TITLE, notificationText, NotificationType.WARNING, new NotificationListener() { + super(NonConfiguredKotlinProjectComponent.CONFIGURE_NOTIFICATION_GROUP_ID, TITLE, getNotificationString(project,excludeModules), + NotificationType.WARNING, new NotificationListener() { @Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { @@ -52,17 +52,15 @@ public class ConfigureKotlinNotification extends Notification { } notification.expire(); - configurator.configure(project); + configurator.configure(project, excludeModules); } } }); - - this.notificationText = notificationText; } @NotNull - public static String getNotificationString(Project project) { - Collection modules = ConfigureKotlinInProjectUtilsKt.getNonConfiguredModules(project); + public static String getNotificationString(Project project, Collection excludeModules) { + Collection modules = ConfigureKotlinInProjectUtilsKt.getNonConfiguredModules(project, excludeModules); final boolean isOnlyOneModule = modules.size() == 1; @@ -92,13 +90,13 @@ public class ConfigureKotlinNotification extends Notification { ConfigureKotlinNotification that = (ConfigureKotlinNotification) o; - if (!notificationText.equals(that.notificationText)) return false; + if (!getContent().equals(that.getContent())) return false; return true; } @Override public int hashCode() { - return notificationText.hashCode(); + return getContent().hashCode(); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/framework/ui/ChooseModulePanel.java b/idea/src/org/jetbrains/kotlin/idea/framework/ui/ChooseModulePanel.java index f3e798ad06c..ddb022fdf2e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/framework/ui/ChooseModulePanel.java +++ b/idea/src/org/jetbrains/kotlin/idea/framework/ui/ChooseModulePanel.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -43,7 +44,7 @@ public class ChooseModulePanel { @NotNull private final List modules; @NotNull private final List modulesWithKtFiles; - public ChooseModulePanel(@NotNull Project project, @NotNull KotlinProjectConfigurator configurator) { + public ChooseModulePanel(@NotNull Project project, @NotNull KotlinProjectConfigurator configurator, Collection excludeModules) { this.project = project; this.modules = ConfigureKotlinInProjectUtilsKt.getNonConfiguredModules(project, configurator); this.modulesWithKtFiles = ConfigureKotlinInProjectUtilsKt.getNonConfiguredModulesWithKotlinFiles(project, configurator); diff --git a/idea/src/org/jetbrains/kotlin/idea/framework/ui/ConfigureDialogWithModulesAndVersion.java b/idea/src/org/jetbrains/kotlin/idea/framework/ui/ConfigureDialogWithModulesAndVersion.java index 0284cd1e8b7..30b61ebe5c2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/framework/ui/ConfigureDialogWithModulesAndVersion.java +++ b/idea/src/org/jetbrains/kotlin/idea/framework/ui/ConfigureDialogWithModulesAndVersion.java @@ -62,7 +62,8 @@ public class ConfigureDialogWithModulesAndVersion extends DialogWrapper { public ConfigureDialogWithModulesAndVersion( @NotNull Project project, @NotNull KotlinProjectConfigurator configurator, - @NotNull final String[] kotlinVersions + @NotNull final String[] kotlinVersions, + @NotNull Collection excludeModules ) { super(project); @@ -90,7 +91,7 @@ public class ConfigureDialogWithModulesAndVersion extends DialogWrapper { processIcon.resume(); infoPanel.add(processIcon, BorderLayout.CENTER); - chooseModulePanel = new ChooseModulePanel(project, configurator); + chooseModulePanel = new ChooseModulePanel(project, configurator, excludeModules); chooseModulesPanelPlace.add(chooseModulePanel.getContentPane(), BorderLayout.CENTER); updateComponents(); diff --git a/idea/src/org/jetbrains/kotlin/idea/framework/ui/CreateLibraryDialogWithModules.java b/idea/src/org/jetbrains/kotlin/idea/framework/ui/CreateLibraryDialogWithModules.java index 1ebd81b87ca..264f7a6a392 100644 --- a/idea/src/org/jetbrains/kotlin/idea/framework/ui/CreateLibraryDialogWithModules.java +++ b/idea/src/org/jetbrains/kotlin/idea/framework/ui/CreateLibraryDialogWithModules.java @@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator; import java.awt.*; +import java.util.Collection; import java.util.List; public class CreateLibraryDialogWithModules extends CreateLibraryDialogBase { @@ -34,11 +35,12 @@ public class CreateLibraryDialogWithModules extends CreateLibraryDialogBase { @NotNull String defaultPath, boolean showPathPanel, @NotNull String title, - @NotNull String libraryCaption + @NotNull String libraryCaption, + @NotNull Collection excludeModules ) { super(project, defaultPath, title, libraryCaption); - chooseModulePanel = new ChooseModulePanel(project, configurator); + chooseModulePanel = new ChooseModulePanel(project, configurator, excludeModules); chooseModulesPanelPlace.add(chooseModulePanel.getContentPane(), BorderLayout.CENTER); chooseLibraryPathPlace.setVisible(showPathPanel); diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/AddKotlinLibQuickFix.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/AddKotlinLibQuickFix.kt index 5a5c58cf278..41510d05d49 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/AddKotlinLibQuickFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/AddKotlinLibQuickFix.kt @@ -44,7 +44,7 @@ import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion -import org.jetbrains.kotlin.idea.versions.findKotlinLibraries +import org.jetbrains.kotlin.idea.versions.findAllUsedLibraries import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile @@ -156,7 +156,7 @@ abstract class AddKotlinLibQuickFix(element: KtElement) : KotlinQuickFixAction() ?: return - for (library in findKotlinLibraries(project)) { + for (library in findAllUsedLibraries(project).keySet()) { val runtimeJar = JavaRuntimePresentationProvider.getRuntimeJar(library) ?: continue if (hasLibJarInLibrary(library)) continue diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt index 60cc39652a0..c89e6fa15b5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt @@ -35,6 +35,7 @@ import com.intellij.psi.search.ProjectScope import com.intellij.util.CommonProcessors import com.intellij.util.PathUtil.getLocalFile import com.intellij.util.PathUtil.getLocalPath +import com.intellij.util.containers.MultiMap import com.intellij.util.indexing.FileBasedIndex import com.intellij.util.indexing.ID import com.intellij.util.indexing.ScalarIndexExtension @@ -132,20 +133,18 @@ private fun updateJar( replaceFile(jarPath, localJar!!) } -fun findKotlinLibraries(project: Project): Collection { - val libraries = Sets.newHashSet() +fun findAllUsedLibraries(project: Project): MultiMap { + val libraries = MultiMap() for (module in ModuleManager.getInstance(project).modules) { val moduleRootManager = ModuleRootManager.getInstance(module) - for (entry in moduleRootManager.orderEntries) { - if (entry is LibraryOrderEntry) { - val library = entry.library ?: continue + for (entry in moduleRootManager.orderEntries.filterIsInstance()) { + val library = entry.library ?: continue - libraries.add(library) + libraries.putValue(library, module) - // TODO: search js libraries as well - } + // TODO: search js libraries as well } } diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt b/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt index f5ef1602915..c1105338e43 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt +++ b/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt @@ -16,17 +16,15 @@ package org.jetbrains.kotlin.idea.versions -import com.google.common.collect.Lists import com.intellij.ide.util.PropertiesComponent import com.intellij.notification.Notification import com.intellij.notification.NotificationListener import com.intellij.notification.NotificationType import com.intellij.notification.Notifications import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.AbstractProjectComponent +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.openapi.roots.libraries.Library -import com.intellij.openapi.startup.StartupManager import com.intellij.util.PathUtil.getLocalFile import com.intellij.util.text.VersionComparatorUtil import org.jetbrains.kotlin.idea.KotlinPluginUtil @@ -37,153 +35,147 @@ import org.jetbrains.kotlin.idea.util.application.runWriteAction import java.io.IOException import javax.swing.event.HyperlinkEvent -class OutdatedKotlinRuntimeNotification(project: Project) : AbstractProjectComponent(project) { +private data class VersionedLibrary(val library: Library, val version: String?) - private data class VersionedLibrary(val library: Library, val version: String?) +fun checkOutdatedKotlinRuntime(project: Project): List { + val pluginVersion = KotlinPluginUtil.getPluginVersion() + if ("@snapshot@" == pluginVersion) return emptyList() // plugin is run from sources, can't compare versions - override fun projectOpened() { - StartupManager.getInstance(myProject).registerPostStartupActivity(Runnable { - val pluginVersion = KotlinPluginUtil.getPluginVersion() - if ("@snapshot@" == pluginVersion) return@Runnable // plugin is run from sources, can't compare versions + // user already clicked suppress + if (pluginVersion == PropertiesComponent.getInstance(project).getValue(SUPPRESSED_PROPERTY_NAME)) { + return emptyList() + } - // user already clicked suppress - if (pluginVersion == PropertiesComponent.getInstance(myProject).getValue(SUPPRESSED_PROPERTY_NAME)) return@Runnable + val versionedOutdatedLibraries = findOutdatedKotlinLibraries(project) + if (versionedOutdatedLibraries.isEmpty()) { + return emptyList() + } - val versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject) - if (versionedOutdatedLibraries.isEmpty()) { - return@Runnable + val message: String = if (versionedOutdatedLibraries.size == 1) { + val versionedLibrary = versionedOutdatedLibraries.keys.first() + + val version = versionedLibrary.version + val readableVersion = version ?: "unknown" + val libraryName = versionedLibrary.library.name + + "

Your version of Kotlin runtime in '$libraryName' library is $readableVersion, while plugin version is $pluginVersion.

" + + "

Runtime library should be updated to avoid compatibility problems.

" + + "

Update Runtime Ignore

" + } + else { + val libraryNames = versionedOutdatedLibraries.keys.joinToString { it.library.name!! } + + "

Version of Kotlin runtime is outdated in several libraries ($libraryNames). Plugin version is $pluginVersion.

" + + "

Runtime libraries should be updated to avoid compatibility problems.

" + + "

Update All Ignore

" + } + + + Notifications.Bus.notify(Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Outdated Kotlin Runtime", message, + NotificationType.WARNING, NotificationListener { notification, event -> + if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) { + if ("update" == event.description) { + val outdatedLibraries = findOutdatedKotlinLibraries(project).map { it.key.library } + updateLibraries(project, outdatedLibraries) + suggestDeleteKotlinJsIfNeeded(project, outdatedLibraries) } - - val message: String = if (versionedOutdatedLibraries.size == 1) { - val versionedLibrary = versionedOutdatedLibraries.first() - - val version = versionedLibrary.version - val readableVersion = version ?: "unknown" - val libraryName = versionedLibrary.library.name - - "

Your version of Kotlin runtime in '$libraryName' library is $readableVersion, while plugin version is $pluginVersion.

" + - "

Runtime library should be updated to avoid compatibility problems.

" + - "

Update Runtime Ignore

" + else if ("ignore" == event.description) { + PropertiesComponent.getInstance(project).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion) } else { - val libraryNames = versionedOutdatedLibraries.joinToString { it.library.name!! } - - "

Version of Kotlin runtime is outdated in several libraries ($libraryNames). Plugin version is $pluginVersion.

" + - "

Runtime libraries should be updated to avoid compatibility problems.

" + - "

Update All Ignore

" + throw AssertionError() } + notification.expire() + } + }), project) + return versionedOutdatedLibraries.flatMap { it.value } +} - Notifications.Bus.notify(Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Outdated Kotlin Runtime", message, - NotificationType.WARNING, NotificationListener { notification, event -> - if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) { - if ("update" == event.description) { - val outdatedLibraries = findOutdatedKotlinLibraries(myProject).map { it.library } - updateLibraries(myProject, outdatedLibraries) - suggestDeleteKotlinJsIfNeeded(outdatedLibraries) - } - else if ("ignore" == event.description) { - PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion) - } - else { - throw AssertionError() - } - notification.expire() - } - }), myProject) - }) - } +private fun deleteKotlinJs(project: Project) { + ApplicationManager.getApplication().invokeLater { + runWriteAction { + val kotlinJsFile = project.baseDir.findFileByRelativePath("script/kotlin.js") ?: return@runWriteAction - private fun deleteKotlinJs() { - ApplicationManager.getApplication().invokeLater { - runWriteAction { - val kotlinJsFile = myProject.baseDir.findFileByRelativePath("script/kotlin.js") ?: return@runWriteAction - - val fileToDelete = getLocalFile(kotlinJsFile) - try { - val parent = fileToDelete.parent - fileToDelete.delete(this) - parent.refresh(false, true) - } - catch (ex: IOException) { - Notifications.Bus.notify( - Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Error", "Could not delete 'script/kotlin.js': " + ex.message, NotificationType.ERROR)) - } + val fileToDelete = getLocalFile(kotlinJsFile) + try { + val parent = fileToDelete.parent + fileToDelete.delete(null) + parent.refresh(false, true) + } + catch (ex: IOException) { + Notifications.Bus.notify( + Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Error", "Could not delete 'script/kotlin.js': " + ex.message, NotificationType.ERROR)) } } } - - private fun suggestDeleteKotlinJsIfNeeded(outdatedLibraries: Collection) { - myProject.baseDir.findFileByRelativePath("script/kotlin.js") ?: return - - var addNotification = false - for (library in outdatedLibraries) { - if (LibraryPresentationProviderUtil.isDetected(JSLibraryStdPresentationProvider.getInstance(), library)) { - val jsStdlibJar = JSLibraryStdPresentationProvider.getJsStdLibJar(library) - assert(jsStdlibJar != null) { "jslibFile should not be null" } - - if (jsStdlibJar!!.findFileByRelativePath("kotlin.js") == null) { - addNotification = true - break - } - } - } - if (!addNotification) return - - val message = "

File 'script/kotlin.js' was probably created by an older version of the Kotlin plugin.

" + - "

The new Kotlin plugin copies an up-to-date version of this file to the output directory automatically, so the old version of it can be deleted.

" + - "

Delete this file Ignore

" - - Notifications.Bus.notify(Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Outdated Kotlin Runtime", message, - NotificationType.WARNING, NotificationListener { notification, event -> - if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) { - if ("delete" == event.description) { - deleteKotlinJs() - } - else if ("ignore" == event.description) { - // pass - } - else { - throw AssertionError() - } - notification.expire() - } - }), myProject) - } - - companion object { - private val SUPPRESSED_PROPERTY_NAME = "oudtdated.runtime.suppressed.plugin.version" - private val OUTDATED_RUNTIME_GROUP_DISPLAY_ID = "Outdated Kotlin Runtime" - - private fun findOutdatedKotlinLibraries(project: Project): Collection { - val outdatedLibraries = Lists.newArrayList() - - for (library in findKotlinLibraries(project)) { - var libraryVersionProperties = LibraryPresentationProviderUtil.getLibraryProperties(JavaRuntimePresentationProvider.getInstance(), library) - if (libraryVersionProperties == null) { - libraryVersionProperties = LibraryPresentationProviderUtil.getLibraryProperties(JSLibraryStdPresentationProvider.getInstance(), library) - } - if (libraryVersionProperties == null) { - continue - } - val libraryVersion = libraryVersionProperties.versionString - - val runtimeVersion = bundledRuntimeVersion() - - val isOutdated = isRuntimeOutdated(libraryVersion, runtimeVersion) - - if (isOutdated) { - outdatedLibraries.add(VersionedLibrary(library, libraryVersion)) - } - } - - return outdatedLibraries - } - - fun isRuntimeOutdated(libraryVersion: String?, runtimeVersion: String): Boolean { - return libraryVersion == null || libraryVersion.startsWith("internal-") != runtimeVersion.startsWith("internal-") || - VersionComparatorUtil.compare(runtimeVersion, libraryVersion) > 0 - } - - } +} + +private fun suggestDeleteKotlinJsIfNeeded(project: Project, outdatedLibraries: Collection) { + project.baseDir.findFileByRelativePath("script/kotlin.js") ?: return + + var addNotification = false + for (library in outdatedLibraries) { + if (LibraryPresentationProviderUtil.isDetected(JSLibraryStdPresentationProvider.getInstance(), library)) { + val jsStdlibJar = JSLibraryStdPresentationProvider.getJsStdLibJar(library) + assert(jsStdlibJar != null) { "jslibFile should not be null" } + + if (jsStdlibJar!!.findFileByRelativePath("kotlin.js") == null) { + addNotification = true + break + } + } + } + if (!addNotification) return + + val message = "

File 'script/kotlin.js' was probably created by an older version of the Kotlin plugin.

" + + "

The new Kotlin plugin copies an up-to-date version of this file to the output directory automatically, so the old version of it can be deleted.

" + + "

Delete this file Ignore

" + + Notifications.Bus.notify(Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Outdated Kotlin Runtime", message, + NotificationType.WARNING, NotificationListener { notification, event -> + if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) { + if ("delete" == event.description) { + deleteKotlinJs(project) + } + else if ("ignore" == event.description) { + // pass + } + else { + throw AssertionError() + } + notification.expire() + } + }), project) +} + +private val SUPPRESSED_PROPERTY_NAME = "oudtdated.runtime.suppressed.plugin.version" +private val OUTDATED_RUNTIME_GROUP_DISPLAY_ID = "Outdated Kotlin Runtime" + +private fun findOutdatedKotlinLibraries(project: Project): Map> { + val outdatedLibraries = hashMapOf>() + + for ((library, modules) in findAllUsedLibraries(project).entrySet()) { + val libraryVersionProperties = + LibraryPresentationProviderUtil.getLibraryProperties(JavaRuntimePresentationProvider.getInstance(), library) ?: + LibraryPresentationProviderUtil.getLibraryProperties(JSLibraryStdPresentationProvider.getInstance(), library) ?: + continue + + val libraryVersion = libraryVersionProperties.versionString + + val runtimeVersion = bundledRuntimeVersion() + + val isOutdated = isRuntimeOutdated(libraryVersion, runtimeVersion) + + if (isOutdated) { + outdatedLibraries[VersionedLibrary(library, libraryVersion)] = modules + } + } + + return outdatedLibraries +} + +fun isRuntimeOutdated(libraryVersion: String?, runtimeVersion: String): Boolean { + return libraryVersion == null || libraryVersion.startsWith("internal-") != runtimeVersion.startsWith("internal-") || + VersionComparatorUtil.compare(runtimeVersion, libraryVersion) > 0 } diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.java b/idea/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.java index 5a5f0b55205..e0ba049a695 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.java +++ b/idea/src/org/jetbrains/kotlin/idea/versions/UnsupportedAbiVersionNotificationPanelProvider.java @@ -103,7 +103,7 @@ public class UnsupportedAbiVersionNotificationPanelProvider extends EditorNotifi private EditorNotificationPanel doCreate(final Collection badRoots) { EditorNotificationPanel answer = new ErrorNotificationPanel(); - Collection kotlinLibraries = KotlinRuntimeLibraryUtilKt.findKotlinLibraries(project); + Collection kotlinLibraries = KotlinRuntimeLibraryUtilKt.findAllUsedLibraries(project).keySet(); final Collection badRuntimeLibraries = Collections2.filter(kotlinLibraries, new Predicate() { @Override public boolean apply(@Nullable Library library) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/OutdatedKotlinRuntimeNotificationTest.kt b/idea/tests/org/jetbrains/kotlin/idea/OutdatedKotlinRuntimeNotificationTest.kt index 045a9a48e9e..e1482e4c877 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/OutdatedKotlinRuntimeNotificationTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/OutdatedKotlinRuntimeNotificationTest.kt @@ -17,8 +17,8 @@ package org.jetbrains.kotlin.idea import junit.framework.TestCase -import org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification import org.jetbrains.kotlin.idea.versions.bundledRuntimeVersion +import org.jetbrains.kotlin.idea.versions.isRuntimeOutdated import org.junit.Assert @@ -87,12 +87,12 @@ class KotlinRuntimeLibraryUtilTest : TestCase() { private fun outdated(plugin: String, library: String) { Assert.assertTrue("Should be outdated: plugin=$plugin, library=$library", - OutdatedKotlinRuntimeNotification.isRuntimeOutdated(library, bundledRuntimeVersion(plugin))) + isRuntimeOutdated(library, bundledRuntimeVersion(plugin))) } private fun notOutdated(plugin: String, library: String) { Assert.assertFalse("Should NOT be outdated: plugin=$plugin, library=$library", - OutdatedKotlinRuntimeNotification.isRuntimeOutdated(library, bundledRuntimeVersion(plugin))) + isRuntimeOutdated(library, bundledRuntimeVersion(plugin))) } private fun test(version: String, expected: String) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java index 626b8ec4c2b..873319fb51f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinTest.java @@ -127,7 +127,7 @@ public class ConfigureKotlinTest extends PlatformTestCase { new File(JAVA_CONFIGURATOR.getDefaultPathToJarFile(getProject()) + "/kotlin-runtime.jar")); assertNotConfigured(module, JAVA_CONFIGURATOR); - JAVA_CONFIGURATOR.configure(myProject); + JAVA_CONFIGURATOR.configure(myProject, Collections.emptyList()); assertProperlyConfigured(module, JAVA_CONFIGURATOR); } @@ -181,7 +181,7 @@ public class ConfigureKotlinTest extends PlatformTestCase { assertNotConfigured(module, configurator); } - configurator.configure(myProject); + configurator.configure(myProject, Collections.emptyList()); assertNoFilesInDefaultPaths();