Migration detector for external systems (KT-23401)
This commit is contained in:
+2
@@ -25,10 +25,12 @@ import org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComp
|
|||||||
class KotlinExternalSystemSyncListener : ExternalSystemTaskNotificationListenerAdapter() {
|
class KotlinExternalSystemSyncListener : ExternalSystemTaskNotificationListenerAdapter() {
|
||||||
override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
|
override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
|
||||||
val project = id.findResolvedProject() ?: return
|
val project = id.findResolvedProject() ?: return
|
||||||
|
KotlinMigrationProjectComponent.getInstance(project).onImportAboutToStart()
|
||||||
KotlinConfigurationCheckerComponent.getInstance(project).syncStarted()
|
KotlinConfigurationCheckerComponent.getInstance(project).syncStarted()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEnd(id: ExternalSystemTaskId) {
|
override fun onEnd(id: ExternalSystemTaskId) {
|
||||||
|
// At this point changes might be still not applied to project structure yet.
|
||||||
val project = id.findResolvedProject() ?: return
|
val project = id.findResolvedProject() ?: return
|
||||||
KotlinConfigurationCheckerComponent.getInstance(project).syncDone()
|
KotlinConfigurationCheckerComponent.getInstance(project).syncDone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
|
|||||||
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
<interface-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</interface-class>
|
||||||
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
|
||||||
</component>
|
</component>
|
||||||
|
<component>
|
||||||
|
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
|
||||||
|
</component>
|
||||||
</project-components>
|
</project-components>
|
||||||
|
|
||||||
<application-components>
|
<application-components>
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* 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.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataImportListener
|
||||||
|
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||||
|
import com.intellij.openapi.module.ModuleManager
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ProjectRootManager
|
||||||
|
import com.intellij.openapi.roots.libraries.Library
|
||||||
|
import com.intellij.util.CommonProcessors
|
||||||
|
import com.intellij.util.text.VersionComparatorUtil
|
||||||
|
import org.jetbrains.kotlin.config.ApiVersion
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersion
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
|
||||||
|
import org.jetbrains.kotlin.idea.framework.GRADLE_SYSTEM_ID
|
||||||
|
import org.jetbrains.kotlin.idea.framework.MAVEN_SYSTEM_ID
|
||||||
|
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
|
import org.jetbrains.kotlin.idea.versions.LibInfo
|
||||||
|
|
||||||
|
class KotlinMigrationProjectComponent(val project: Project) {
|
||||||
|
private var old: MigrationState? = null
|
||||||
|
private var new: MigrationState? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
val connection = project.messageBus.connect()
|
||||||
|
connection.subscribe(ProjectDataImportListener.TOPIC, ProjectDataImportListener {
|
||||||
|
KotlinMigrationProjectComponent.getInstance(project).onImportFinished()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun onImportAboutToStart() {
|
||||||
|
old = MigrationState.build(project)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun onImportFinished() {
|
||||||
|
new = MigrationState.build(project)
|
||||||
|
|
||||||
|
val migrationInfo = prepareMigrationInfo(old, new) ?: return
|
||||||
|
|
||||||
|
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
// Notify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getInstance(project: Project): KotlinMigrationProjectComponent =
|
||||||
|
project.getComponent(KotlinMigrationProjectComponent::class.java)!!
|
||||||
|
|
||||||
|
private fun prepareMigrationInfo(old: MigrationState?, new: MigrationState?): MigrationInfo? {
|
||||||
|
if (old == null || new == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val oldLibraryVersion = old.stdlibInfo?.version
|
||||||
|
val newLibraryVersion = new.stdlibInfo?.version
|
||||||
|
|
||||||
|
if (oldLibraryVersion == null || newLibraryVersion == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VersionComparatorUtil.COMPARATOR.compare(newLibraryVersion, oldLibraryVersion) > 0 ||
|
||||||
|
old.apiVersion < new.apiVersion || old.languageVersion < new.languageVersion
|
||||||
|
) {
|
||||||
|
return MigrationInfo(
|
||||||
|
oldLibraryVersion, newLibraryVersion,
|
||||||
|
old.apiVersion, new.apiVersion,
|
||||||
|
old.languageVersion, new.languageVersion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MigrationState(
|
||||||
|
var stdlibInfo: LibInfo?,
|
||||||
|
var apiVersion: ApiVersion,
|
||||||
|
var languageVersion: LanguageVersion
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
fun build(project: Project): MigrationState {
|
||||||
|
val libraries = maxKotlinLibVersion(project)
|
||||||
|
val languageVersionSettings = collectMaxCompilerSettings(project)
|
||||||
|
return MigrationState(libraries, languageVersionSettings.apiVersion, languageVersionSettings.languageVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class MigrationInfo(
|
||||||
|
val oldStdlibVersion: String,
|
||||||
|
val newStdlibVersion: String,
|
||||||
|
val oldApiVersion: ApiVersion,
|
||||||
|
val newApiVersion: ApiVersion,
|
||||||
|
val oldLanguageVersion: LanguageVersion,
|
||||||
|
val newLanguageVersion: LanguageVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
private const val KOTLIN_GROUP_ID = "org.jetbrains.kotlin"
|
||||||
|
|
||||||
|
private fun maxKotlinLibVersion(project: Project): LibInfo? {
|
||||||
|
return runReadAction {
|
||||||
|
var maxStdlibInfo: LibInfo? = null
|
||||||
|
|
||||||
|
val allLibProcessor = CommonProcessors.CollectUniquesProcessor<Library>()
|
||||||
|
ProjectRootManager.getInstance(project).orderEntries().forEachLibrary(allLibProcessor)
|
||||||
|
|
||||||
|
for (library in allLibProcessor.results) {
|
||||||
|
if (!ExternalSystemApiUtil.isExternalSystemLibrary(library, GRADLE_SYSTEM_ID) &&
|
||||||
|
!ExternalSystemApiUtil.isExternalSystemLibrary(library, MAVEN_SYSTEM_ID)
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (library.name?.contains(" $KOTLIN_GROUP_ID:kotlin-stdlib") != true) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val libName = library.name ?: continue
|
||||||
|
|
||||||
|
val version = libName.substringAfterLastNullable(":") ?: continue
|
||||||
|
val artifactId = libName.substringBeforeLastNullable(":")?.substringAfterLastNullable(":") ?: continue
|
||||||
|
|
||||||
|
if (version.isBlank() || artifactId.isBlank()) continue
|
||||||
|
|
||||||
|
if (maxStdlibInfo == null || VersionComparatorUtil.COMPARATOR.compare(version, maxStdlibInfo.version) > 0) {
|
||||||
|
maxStdlibInfo = LibInfo(KOTLIN_GROUP_ID, artifactId, version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxStdlibInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectMaxCompilerSettings(project: Project): LanguageVersionSettings {
|
||||||
|
return runReadAction {
|
||||||
|
var maxApiVersion: ApiVersion? = null
|
||||||
|
var maxLanguageVersion: LanguageVersion? = null
|
||||||
|
|
||||||
|
for (module in ModuleManager.getInstance(project).modules) {
|
||||||
|
val languageVersionSettings = module.languageVersionSettings
|
||||||
|
|
||||||
|
if (maxApiVersion == null || languageVersionSettings.apiVersion > maxApiVersion) {
|
||||||
|
maxApiVersion = languageVersionSettings.apiVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLanguageVersion == null || languageVersionSettings.languageVersion > maxLanguageVersion) {
|
||||||
|
maxLanguageVersion = languageVersionSettings.languageVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LanguageVersionSettingsImpl(maxLanguageVersion ?: LanguageVersion.LATEST_STABLE, maxApiVersion ?: ApiVersion.LATEST_STABLE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.substringBeforeLastNullable(delimiter: String, missingDelimiterValue: String? = null): String? {
|
||||||
|
val index = lastIndexOf(delimiter)
|
||||||
|
return if (index == -1) missingDelimiterValue else substring(0, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.substringAfterLastNullable(delimiter: String, missingDelimiterValue: String? = null): String? {
|
||||||
|
val index = lastIndexOf(delimiter)
|
||||||
|
return if (index == -1) missingDelimiterValue else substring(index + 1, length)
|
||||||
|
}
|
||||||
@@ -280,7 +280,8 @@ val LOG = Logger.getInstance("org.jetbrains.kotlin.idea.versions.KotlinRuntimeLi
|
|||||||
|
|
||||||
data class LibInfo(
|
data class LibInfo(
|
||||||
val groupId: String,
|
val groupId: String,
|
||||||
val name: String
|
val name: String,
|
||||||
|
val version: String = "0.0.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeprecatedLibInfo(
|
data class DeprecatedLibInfo(
|
||||||
|
|||||||
@@ -280,7 +280,8 @@ val LOG = Logger.getInstance("org.jetbrains.kotlin.idea.versions.KotlinRuntimeLi
|
|||||||
|
|
||||||
data class LibInfo(
|
data class LibInfo(
|
||||||
val groupId: String,
|
val groupId: String,
|
||||||
val name: String
|
val name: String,
|
||||||
|
val version: String = "0.0.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DeprecatedLibInfo(
|
data class DeprecatedLibInfo(
|
||||||
|
|||||||
Reference in New Issue
Block a user