diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt index 49bdf49df08..60cc39652a0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/versions/KotlinRuntimeLibraryUtil.kt @@ -25,6 +25,7 @@ import com.intellij.openapi.roots.LibraryOrderEntry import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.roots.libraries.Library +import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.JarFileSystem @@ -45,7 +46,6 @@ import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil import org.jetbrains.kotlin.idea.project.ProjectStructureUtil -import org.jetbrains.kotlin.idea.versions.OutdatedKotlinRuntimeNotification.showRuntimeJarNotFoundDialog import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion import org.jetbrains.kotlin.utils.KotlinJavascriptMetadataUtils import org.jetbrains.kotlin.utils.PathUtil @@ -122,7 +122,7 @@ private fun updateJar( } if (!jarPath.exists()) { - showRuntimeJarNotFoundDialog(project, libraryJarDescriptor.jarName).run() + showRuntimeJarNotFoundDialog(project, libraryJarDescriptor.jarName) return } @@ -258,3 +258,9 @@ private fun collectAllKeys(id: ID, modules: List): Collecti return allKeys } + +fun showRuntimeJarNotFoundDialog(project: Project, jarName: String) { + Messages.showErrorDialog(project, + jarName + " is not found. Make sure plugin is properly installed.", + "No Runtime Found") +} diff --git a/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt b/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt index 258c29bcb9c..f5ef1602915 100644 --- a/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt +++ b/idea/src/org/jetbrains/kotlin/idea/versions/OutdatedKotlinRuntimeNotification.kt @@ -14,263 +14,176 @@ * limitations under the License. */ -package org.jetbrains.kotlin.idea.versions; +package org.jetbrains.kotlin.idea.versions -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; -import com.intellij.framework.library.LibraryVersionProperties; -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.project.Project; -import com.intellij.openapi.roots.libraries.Library; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.Function; -import com.intellij.util.text.VersionComparatorUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.idea.KotlinPluginUtil; -import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider; -import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider; -import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil; +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.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 +import org.jetbrains.kotlin.idea.framework.JSLibraryStdPresentationProvider +import org.jetbrains.kotlin.idea.framework.JavaRuntimePresentationProvider +import org.jetbrains.kotlin.idea.framework.LibraryPresentationProviderUtil +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import java.io.IOException +import javax.swing.event.HyperlinkEvent -import javax.swing.event.HyperlinkEvent; -import java.io.IOException; -import java.util.Collection; -import java.util.List; +class OutdatedKotlinRuntimeNotification(project: Project) : AbstractProjectComponent(project) { -import static com.intellij.util.PathUtil.getLocalFile; + private data class VersionedLibrary(val library: Library, val version: String?) -public class OutdatedKotlinRuntimeNotification extends AbstractProjectComponent { - private static final String SUPPRESSED_PROPERTY_NAME = "oudtdated.runtime.suppressed.plugin.version"; - private static final String OUTDATED_RUNTIME_GROUP_DISPLAY_ID = "Outdated Kotlin Runtime"; + 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 - public OutdatedKotlinRuntimeNotification(Project project) { - super(project); - } + // user already clicked suppress + if (pluginVersion == PropertiesComponent.getInstance(myProject).getValue(SUPPRESSED_PROPERTY_NAME)) return@Runnable - private static class VersionedLibrary extends Pair { - public VersionedLibrary(@NotNull Library library, @Nullable String version) { - super(library, version); - } - - @NotNull - public Library getLibrary() { - return first; - } - - @Nullable - public String getVersion() { - return second; - } - } - - @Override - public void projectOpened() { - StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() { - @Override - public void run() { - final String pluginVersion = KotlinPluginUtil.getPluginVersion(); - if ("@snapshot@".equals(pluginVersion)) return; // plugin is run from sources, can't compare versions - - // user already clicked suppress - if (pluginVersion.equals(PropertiesComponent.getInstance(myProject).getValue(SUPPRESSED_PROPERTY_NAME))) return; - - Collection versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject); - if (versionedOutdatedLibraries.isEmpty()) { - return; - } - - Collection outdatedLibraries = extractLibraries(versionedOutdatedLibraries); - - String message; - if (versionedOutdatedLibraries.size() == 1) { - VersionedLibrary versionedLibrary = versionedOutdatedLibraries.iterator().next(); - - String version = versionedLibrary.getVersion(); - String readableVersion = version == null ? "unknown" : version; - String libraryName = versionedLibrary.getLibrary().getName(); - - message = String.format( - "

Your version of Kotlin runtime in '%s' library is %s, while plugin version is %s.

" + - "

Runtime library should be updated to avoid compatibility problems.

" + - "

Update Runtime Ignore

", - libraryName, - readableVersion, - pluginVersion); - } - else { - String libraryNames = StringUtil.join(outdatedLibraries, new Function() { - @Override - public String fun(Library library) { - return library.getName(); - } - }, ", "); - - message = String.format( - "

Version of Kotlin runtime is outdated in several libraries (%s). Plugin version is %s.

" + - "

Runtime libraries should be updated to avoid compatibility problems.

" + - "

Update All Ignore

", - libraryNames, - pluginVersion); - } - - - Notifications.Bus.notify(new Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Outdated Kotlin Runtime", message, - NotificationType.WARNING, new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - if ("update".equals(event.getDescription())) { - Collection versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject); - Collection outdatedLibraries = extractLibraries(versionedOutdatedLibraries); - KotlinRuntimeLibraryUtilKt.updateLibraries(myProject, outdatedLibraries); - suggestDeleteKotlinJsIfNeeded(outdatedLibraries); - } - else if ("ignore".equals(event.getDescription())) { - PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion); - } - else { - throw new AssertionError(); - } - notification.expire(); - } - } - }), myProject); + val versionedOutdatedLibraries = findOutdatedKotlinLibraries(myProject) + if (versionedOutdatedLibraries.isEmpty()) { + return@Runnable } - }); - } - private void deleteKotlinJs() { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - VirtualFile kotlinJsFile = myProject.getBaseDir().findFileByRelativePath("script/kotlin.js"); - if (kotlinJsFile == null) return; + val message: String = if (versionedOutdatedLibraries.size == 1) { + val versionedLibrary = versionedOutdatedLibraries.first() - VirtualFile fileToDelete = getLocalFile(kotlinJsFile); - try { - VirtualFile parent = fileToDelete.getParent(); - fileToDelete.delete(this); - parent.refresh(false, true); - } - catch (IOException ex) { - Notifications.Bus.notify( - new Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Error", "Could not delete 'script/kotlin.js': " + ex.getMessage(), NotificationType.ERROR)); - } - } - }); + 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.joinToString { it.library.name!! } - private void suggestDeleteKotlinJsIfNeeded(Collection outdatedLibraries) { - VirtualFile kotlinJsFile = myProject.getBaseDir().findFileByRelativePath("script/kotlin.js"); - if (kotlinJsFile == null) return; - - boolean addNotification = false; - for(Library library : outdatedLibraries) { - if (LibraryPresentationProviderUtil.isDetected(JSLibraryStdPresentationProvider.getInstance(), library)) { - VirtualFile jsStdlibJar = JSLibraryStdPresentationProvider.getJsStdLibJar(library); - assert jsStdlibJar != null : "jslibFile should not be null"; - - if (jsStdlibJar.findFileByRelativePath("kotlin.js") == null) { - addNotification = true; - break; - } + "

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

" } - } - if (!addNotification) return; - String message = String.format( - "

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(new Notification(OUTDATED_RUNTIME_GROUP_DISPLAY_ID, "Outdated Kotlin Runtime", message, - NotificationType.WARNING, new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - if ("delete".equals(event.getDescription())) { - deleteKotlinJs(); + 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".equals(event.getDescription())) { - // pass + else if ("ignore" == event.description) { + PropertiesComponent.getInstance(myProject).setValue(SUPPRESSED_PROPERTY_NAME, pluginVersion) } else { - throw new AssertionError(); + throw AssertionError() } - notification.expire(); + notification.expire() + } + }), myProject) + }) + } + + 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)) } } - }), myProject); + } } - private static Collection extractLibraries(Collection libraries) { - return Collections2.transform(libraries, new com.google.common.base.Function() { - @Override - public Library apply(@Nullable VersionedLibrary versionedLibrary) { - assert versionedLibrary != null; - return versionedLibrary.getLibrary(); - } - }); - } + private fun suggestDeleteKotlinJsIfNeeded(outdatedLibraries: Collection) { + myProject.baseDir.findFileByRelativePath("script/kotlin.js") ?: return - @NotNull - private static Collection findOutdatedKotlinLibraries(@NotNull Project project) { - List outdatedLibraries = Lists.newArrayList(); + 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" } - for (Library library : KotlinRuntimeLibraryUtilKt.findKotlinLibraries(project)) { - LibraryVersionProperties libraryVersionProperties = - LibraryPresentationProviderUtil.getLibraryProperties(JavaRuntimePresentationProvider.getInstance(), library); - if (libraryVersionProperties == null) { - libraryVersionProperties = - LibraryPresentationProviderUtil.getLibraryProperties(JSLibraryStdPresentationProvider.getInstance(), library); - } - if (libraryVersionProperties == null) { - continue; - } - String libraryVersion = libraryVersionProperties.getVersionString(); - - String runtimeVersion = KotlinRuntimeLibraryUtilKt.bundledRuntimeVersion(); - - boolean isOutdated = isRuntimeOutdated(libraryVersion, runtimeVersion); - - if (isOutdated) { - outdatedLibraries.add(new VersionedLibrary(library, libraryVersion)); + if (jsStdlibJar!!.findFileByRelativePath("kotlin.js") == null) { + addNotification = true + break + } } } + if (!addNotification) return - return outdatedLibraries; - } + 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

" - public static boolean isRuntimeOutdated(String libraryVersion, String runtimeVersion) { - return libraryVersion == null - || libraryVersion.startsWith("internal-") != runtimeVersion.startsWith("internal-") - || VersionComparatorUtil.compare(runtimeVersion, libraryVersion) > 0; - } - - @NotNull - public static Runnable showRuntimeJarNotFoundDialog(@NotNull final Project project, final @NotNull String jarName) { - return new Runnable() { - @Override - public void run() { - Messages.showErrorDialog(project, - jarName + " is not found. Make sure plugin is properly installed.", - "No Runtime Found"); + 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 + } + } }