From 4be5c395aa5177b05a7ef8d6df0b3f9fa2539800 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 18 Jul 2018 13:41:08 +0300 Subject: [PATCH] Show popup about too old bundled compiler in Gradle and Maven (KT-25546) --- .../kotlin/idea/facet/KotlinFacet.kt | 3 +- .../gradle/GradleConfiguratorTest.kt | 12 ++ .../ui/KotlinConfigurationCheckerComponent.kt | 9 +- .../kotlin/idea/maven/MavenImportListener.kt | 23 +++ idea/src/META-INF/maven.xml | 6 + .../actions/ConfigurePluginUpdatesAction.kt | 4 + .../outdatedBundledCompilerNotification.kt | 187 ++++++++++++++++++ .../jetbrains/kotlin/idea/facet/facetUtils.kt | 7 +- .../kotlin/idea/facet/facetUtils.kt.as31 | 7 +- .../kotlin/idea/facet/facetUtils.kt.as32 | 7 +- 10 files changed, 254 insertions(+), 11 deletions(-) create mode 100644 idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/MavenImportListener.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/configuration/outdatedBundledCompilerNotification.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinFacet.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinFacet.kt index b04746683e1..1f0d97c06a1 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinFacet.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/facet/KotlinFacet.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.idea.facet import com.intellij.facet.Facet import com.intellij.facet.FacetManager -import com.intellij.facet.FacetTypeRegistry import com.intellij.openapi.module.Module class KotlinFacet( @@ -21,4 +20,4 @@ class KotlinFacet( return FacetManager.getInstance(module).getFacetByType(KotlinFacetType.TYPE_ID) } } -} \ No newline at end of file +} diff --git a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt index b89580dba23..0c2b6798b1a 100644 --- a/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt +++ b/idea/idea-gradle/tests/org/jetbrains/kotlin/idea/codeInsight/gradle/GradleConfiguratorTest.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.test.testFramework.runInEdtAndWait import org.jetbrains.kotlin.test.testFramework.runWriteAction +import org.junit.Assert import org.junit.Ignore import org.junit.Test @@ -61,6 +62,17 @@ class GradleConfiguratorTest : GradleImportingTestCase() { assertEquals(ConfigureKotlinStatus.BROKEN, findJsGradleModuleConfigurator().getStatus(moduleGroup)) } } + + Assert.assertEquals( + """ +

The compiler bundled to Kotlin plugin (1.0.0) is older than external compiler used for building modules:

+ +

This may cause different set of errors and warnings reported in IDE.

+

Update Ignore

+ """.trimIndent().lines().joinToString(separator = ""), + createOutdatedBundledCompilerMessage(myProject, "1.0.0")) } @Test diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinConfigurationCheckerComponent.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinConfigurationCheckerComponent.kt index e129a635508..514d7cfd0d4 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinConfigurationCheckerComponent.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ui/KotlinConfigurationCheckerComponent.kt @@ -21,14 +21,13 @@ import com.intellij.notification.NotificationDisplayType import com.intellij.notification.NotificationsConfiguration import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.AbstractProjectComponent +import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootEvent import com.intellij.openapi.roots.ModuleRootListener import com.intellij.openapi.startup.StartupManager -import org.jetbrains.kotlin.idea.configuration.checkHideNonConfiguredNotifications -import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles -import org.jetbrains.kotlin.idea.configuration.showConfigureKotlinNotificationIfNeeded +import org.jetbrains.kotlin.idea.configuration.* import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies import org.jetbrains.kotlin.idea.versions.collectModulesWithOutdatedRuntime import org.jetbrains.kotlin.idea.versions.findOutdatedKotlinLibraries @@ -65,6 +64,10 @@ class KotlinConfigurationCheckerComponent(project: Project) : AbstractProjectCom checkHideNonConfiguredNotifications(project) } }) + + connection.subscribe(ProjectDataImportListener.TOPIC, ProjectDataImportListener { + notifyOutdatedBundledCompilerIfNecessary(project) + }) } override fun projectOpened() { diff --git a/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/MavenImportListener.kt b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/MavenImportListener.kt new file mode 100644 index 00000000000..c77b8d4fe4f --- /dev/null +++ b/idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/MavenImportListener.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.maven + +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import org.jetbrains.idea.maven.project.MavenImportListener +import org.jetbrains.idea.maven.project.MavenProject +import org.jetbrains.kotlin.idea.configuration.notifyOutdatedBundledCompilerIfNecessary + +class MavenImportListener(val project: Project) { + init { + project.messageBus.connect().subscribe( + MavenImportListener.TOPIC, + MavenImportListener { _: Collection, _: List -> + notifyOutdatedBundledCompilerIfNecessary(project) + } + ) + } +} \ No newline at end of file diff --git a/idea/src/META-INF/maven.xml b/idea/src/META-INF/maven.xml index e35a48f21d8..34e623f2d02 100644 --- a/idea/src/META-INF/maven.xml +++ b/idea/src/META-INF/maven.xml @@ -1,4 +1,10 @@ + + + org.jetbrains.kotlin.idea.maven.MavenImportListener + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt index 201497dea95..c953bae0c18 100644 --- a/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/actions/ConfigurePluginUpdatesAction.kt @@ -27,4 +27,8 @@ class ConfigurePluginUpdatesAction : DumbAwareAction() { val project = e.getData(CommonDataKeys.PROJECT) ShowSettingsUtilImpl.showSettingsDialog(project, KotlinUpdatesSettingsConfigurable.ID, "") } + + companion object { + val ACTION_ID = "KotlinConfigureUpdates" + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/configuration/outdatedBundledCompilerNotification.kt b/idea/src/org/jetbrains/kotlin/idea/configuration/outdatedBundledCompilerNotification.kt new file mode 100644 index 00000000000..06bb3b7caf3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/configuration/outdatedBundledCompilerNotification.kt @@ -0,0 +1,187 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.configuration + +import com.intellij.ide.DataManager +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.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.config.KotlinCompilerVersion +import org.jetbrains.kotlin.config.LanguageVersion +import org.jetbrains.kotlin.idea.KotlinPluginUtil +import org.jetbrains.kotlin.idea.actions.ConfigurePluginUpdatesAction +import org.jetbrains.kotlin.idea.project.languageVersionSettings +import org.jetbrains.kotlin.psi.UserDataProperty +import javax.swing.event.HyperlinkEvent + +var Module.externalCompilerVersion: String? by UserDataProperty(Key.create("EXTERNAL_COMPILER_VERSION")) + +fun notifyOutdatedBundledCompilerIfNecessary(project: Project) { + val pluginVersion = KotlinPluginUtil.getPluginVersion() + if (pluginVersion == PropertiesComponent.getInstance(project).getValue(SUPPRESSED_OUTDATED_COMPILER_PROPERTY_NAME)) { + return + } + + val message: String = createOutdatedBundledCompilerMessage(project) ?: return + + if (ApplicationManager.getApplication().isUnitTestMode) { + return + } + + Notifications.Bus.notify( + Notification( + OUTDATED_BUNDLED_COMPILER_GROUP_DISPLAY_ID, OUTDATED_BUNDLED_COMPILER_GROUP_DISPLAY_ID, message, + NotificationType.WARNING, NotificationListener { notification, event -> + if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) { + when { + "update" == event.description -> { + val action = ActionManager.getInstance().getAction(ConfigurePluginUpdatesAction.ACTION_ID) + val dataContext = DataManager.getInstance().dataContextFromFocus.result + val actionEvent = AnActionEvent.createFromAnAction(action, null, ActionPlaces.ACTION_SEARCH, dataContext) + action.actionPerformed(actionEvent) + } + "ignore" == event.description -> { + if (!project.isDisposed) { + PropertiesComponent.getInstance(project).setValue(SUPPRESSED_OUTDATED_COMPILER_PROPERTY_NAME, pluginVersion) + } + } + else -> { + throw AssertionError() + } + } + notification.expire() + } + } + ), + project + ) +} + +fun createOutdatedBundledCompilerMessage(project: Project, bundledCompilerVersion: String = KotlinCompilerVersion.VERSION): String? { + val bundledCompilerMajorVersion = MajorVersion.create(bundledCompilerVersion) ?: return null + + val usedCompilerInfo = ArrayList() + for (module in ModuleManager.getInstance(project).modules) { + val externalCompilerVersion = module.externalCompilerVersion ?: continue + val externalCompilerMajorVersion = MajorVersion.create(externalCompilerVersion) ?: continue + + usedCompilerInfo.add( + ModuleCompilerInfo( + module, + externalCompilerVersion, + externalCompilerMajorVersion = externalCompilerMajorVersion, + languageMajorVersion = MajorVersion.create(module.languageVersionSettings.languageVersion) + ) + ) + } + + val selectedBaseModules = HashSet() + val selectedNewerModulesInfos = ArrayList() + val moduleSourceRootMap = ModuleSourceRootMap(project) + for (moduleCompilerInfo in usedCompilerInfo) { + val languageMajorVersion = moduleCompilerInfo.languageMajorVersion + val externalCompilerMajorVersion = moduleCompilerInfo.externalCompilerMajorVersion + + if (externalCompilerMajorVersion > bundledCompilerMajorVersion && languageMajorVersion > bundledCompilerMajorVersion) { + val wholeModuleGroup = moduleSourceRootMap.getWholeModuleGroup(moduleCompilerInfo.module) + if (!selectedBaseModules.contains(wholeModuleGroup.baseModule)) { + // Remap to base module + selectedNewerModulesInfos.add( + ModuleCompilerInfo( + wholeModuleGroup.baseModule, + moduleCompilerInfo.externalCompilerVersion, + externalCompilerMajorVersion = externalCompilerMajorVersion, + languageMajorVersion = languageMajorVersion + ) + ) + selectedBaseModules.add(wholeModuleGroup.baseModule) + } + } + + if (selectedNewerModulesInfos.size > NUMBER_OF_MODULES_TO_SHOW) { + break + } + } + + if (selectedNewerModulesInfos.isEmpty()) { + return null + } + + var modulesStr = + selectedNewerModulesInfos.take(NUMBER_OF_MODULES_TO_SHOW).joinToString(separator = "") { + "
  • ${it.module.name} (${it.externalCompilerVersion})

  • " + } + + if (selectedNewerModulesInfos.size > NUMBER_OF_MODULES_TO_SHOW) { + modulesStr += "
  • ...
  • " + } + + //
    are going to be replaced to \n for the `Event Log` view. Remove last
    , to avoid additional empty line. + modulesStr = modulesStr.removeSuffix("
    ") + + return """ +

    The compiler bundled to Kotlin plugin ($bundledCompilerVersion) is older than external compiler used for building modules:

    +
      $modulesStr
    +

    This may cause different set of errors and warnings reported in IDE.

    +

    Update Ignore

    """ + .trimIndent().lines().joinToString(separator = "").replace("
    ", "\n") +} + +private class ModuleCompilerInfo( + val module: Module, + val externalCompilerVersion: String, + val externalCompilerMajorVersion: MajorVersion, + val languageMajorVersion: MajorVersion) + +private data class MajorVersion(val major: Int, val minor: Int) : Comparable { + override fun compareTo(other: MajorVersion): Int { + if (major > other.major) return 1 + if (major < other.major) return -1 + + if (minor > other.minor) return 1 + if (minor < other.minor) return -1 + + return 0 + } + + override fun toString(): String = "$major.$minor" + + companion object { + fun create(languageVersion: LanguageVersion): MajorVersion { + return MajorVersion(languageVersion.major, languageVersion.minor) + } + + fun create(versionStr: String): MajorVersion? { + if (versionStr == "@snapshot@") { + return MajorVersion(Int.MAX_VALUE, Int.MAX_VALUE) + } + + val regex = "(\\d+)\\.(\\d+).*".toRegex() + + val matchResult = regex.matchEntire(versionStr) ?: return null + + val major: Int = matchResult.groupValues[1].toInt() + val minor: Int = matchResult.groupValues[2].toInt() + + return MajorVersion(major, minor) + } + } +} + +private const val NUMBER_OF_MODULES_TO_SHOW = 2 +private const val SUPPRESSED_OUTDATED_COMPILER_PROPERTY_NAME = "oudtdated.bundled.kotlin.compiler" +private const val OUTDATED_BUNDLED_COMPILER_GROUP_DISPLAY_ID = "Outdated Bundled Kotlin Compiler" \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt index e5f9304e988..c7f7ca18acb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumen import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings +import org.jetbrains.kotlin.idea.configuration.externalCompilerVersion import org.jetbrains.kotlin.idea.framework.KotlinSdkType import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.versions.* @@ -57,7 +58,7 @@ fun KotlinFacetSettings.initializeIfNeeded( module: Module, rootModel: ModuleRootModel?, platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies - languageVersion: String? = null + compilerVersion: String? = null ) { val project = module.project @@ -80,7 +81,7 @@ fun KotlinFacetSettings.initializeIfNeeded( if (shouldInferLanguageLevel) { languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null) - ?: getDefaultLanguageLevel(module, languageVersion) + ?: getDefaultLanguageLevel(module, compilerVersion) } if (shouldInferAPILevel) { @@ -162,6 +163,8 @@ fun KotlinFacet.configureFacet( } this.coroutineSupport = coroutineSupport } + + module.externalCompilerVersion = compilerVersion } fun KotlinFacet.noVersionAutoAdvance() { diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as31 b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as31 index a45334f80db..00b8def9508 100644 --- a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as31 +++ b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as31 @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumen import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings +import org.jetbrains.kotlin.idea.configuration.externalCompilerVersion import org.jetbrains.kotlin.idea.framework.KotlinSdkType import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.versions.* @@ -57,7 +58,7 @@ fun KotlinFacetSettings.initializeIfNeeded( module: Module, rootModel: ModuleRootModel?, platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies - languageVersion: String? = null + compilerVersion: String? = null ) { val project = module.project @@ -80,7 +81,7 @@ fun KotlinFacetSettings.initializeIfNeeded( if (shouldInferLanguageLevel) { languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null) - ?: getDefaultLanguageLevel(module, languageVersion) + ?: getDefaultLanguageLevel(module, compilerVersion) } if (shouldInferAPILevel) { @@ -162,6 +163,8 @@ fun KotlinFacet.configureFacet( } this.coroutineSupport = coroutineSupport } + + module.externalCompilerVersion = compilerVersion } fun KotlinFacet.noVersionAutoAdvance() { diff --git a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 index a45334f80db..00b8def9508 100644 --- a/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 +++ b/idea/src/org/jetbrains/kotlin/idea/facet/facetUtils.kt.as32 @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JsCompilerArgumen import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCommonCompilerArgumentsHolder import org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerSettings +import org.jetbrains.kotlin.idea.configuration.externalCompilerVersion import org.jetbrains.kotlin.idea.framework.KotlinSdkType import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.versions.* @@ -57,7 +58,7 @@ fun KotlinFacetSettings.initializeIfNeeded( module: Module, rootModel: ModuleRootModel?, platformKind: TargetPlatformKind<*>? = null, // if null, detect by module dependencies - languageVersion: String? = null + compilerVersion: String? = null ) { val project = module.project @@ -80,7 +81,7 @@ fun KotlinFacetSettings.initializeIfNeeded( if (shouldInferLanguageLevel) { languageLevel = (if (useProjectSettings) LanguageVersion.fromVersionString(commonArguments.languageVersion) else null) - ?: getDefaultLanguageLevel(module, languageVersion) + ?: getDefaultLanguageLevel(module, compilerVersion) } if (shouldInferAPILevel) { @@ -162,6 +163,8 @@ fun KotlinFacet.configureFacet( } this.coroutineSupport = coroutineSupport } + + module.externalCompilerVersion = compilerVersion } fun KotlinFacet.noVersionAutoAdvance() {