Migration detector for external systems (KT-23401)

This commit is contained in:
Nikolay Krasko
2018-07-26 16:55:50 +03:00
parent 02796783d3
commit b4c88651f6
11 changed files with 204 additions and 2 deletions
@@ -25,10 +25,12 @@ import org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComp
class KotlinExternalSystemSyncListener : ExternalSystemTaskNotificationListenerAdapter() {
override fun onStart(id: ExternalSystemTaskId, workingDir: String) {
val project = id.findResolvedProject() ?: return
KotlinMigrationProjectComponent.getInstance(project).onImportAboutToStart()
KotlinConfigurationCheckerComponent.getInstance(project).syncStarted()
}
override fun onEnd(id: ExternalSystemTaskId) {
// At this point changes might be still not applied to project structure yet.
val project = id.findResolvedProject() ?: return
KotlinConfigurationCheckerComponent.getInstance(project).syncDone()
}
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-components>
<application-components>
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-components>
<application-components>
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-components>
<application-components>
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-components>
<application-components>
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-components>
<application-components>
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-components>
<application-components>
+3
View File
@@ -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>
<implementation-class>org.jetbrains.kotlin.idea.completion.CompletionBindingContextProvider</implementation-class>
</component>
<component>
<implementation-class>org.jetbrains.kotlin.idea.configuration.KotlinMigrationProjectComponent</implementation-class>
</component>
</project-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(
val groupId: String,
val name: String
val name: String,
val version: String = "0.0.0"
)
data class DeprecatedLibInfo(
@@ -280,7 +280,8 @@ val LOG = Logger.getInstance("org.jetbrains.kotlin.idea.versions.KotlinRuntimeLi
data class LibInfo(
val groupId: String,
val name: String
val name: String,
val version: String = "0.0.0"
)
data class DeprecatedLibInfo(