Correctly detect stdlib in Gradle projects; move detection out of EDT
To detect whether a module has an stdlib in its dependencies, look at source root modules and not at base module.
This commit is contained in:
@@ -55,6 +55,8 @@ inline fun <K, V> MutableMap<K, V>.getOrPutNullable(key: K, defaultValue: () ->
|
|||||||
|
|
||||||
inline fun <T, C: Collection<T>> C.ifEmpty(body: () -> C): C = if (isEmpty()) body() else this
|
inline fun <T, C: Collection<T>> C.ifEmpty(body: () -> C): C = if (isEmpty()) body() else this
|
||||||
|
|
||||||
|
inline fun <K, V, M: Map<K, V>> M.ifEmpty(body: () -> M): M = if (isEmpty()) body() else this
|
||||||
|
|
||||||
inline fun <T> Array<out T>.ifEmpty(body: () -> Array<out T>): Array<out T> = if (isEmpty()) body() else this
|
inline fun <T> Array<out T>.ifEmpty(body: () -> Array<out T>): Array<out T> = if (isEmpty()) body() else this
|
||||||
|
|
||||||
fun <T: Any> MutableCollection<T>.addIfNotNull(t: T?) {
|
fun <T: Any> MutableCollection<T>.addIfNotNull(t: T?) {
|
||||||
|
|||||||
+2
-1
@@ -48,7 +48,8 @@ abstract class KotlinMavenConfigurator
|
|||||||
override val name: String,
|
override val name: String,
|
||||||
override val presentableText: String) : KotlinProjectConfigurator {
|
override val presentableText: String) : KotlinProjectConfigurator {
|
||||||
|
|
||||||
override fun getStatus(module: Module): ConfigureKotlinStatus {
|
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
||||||
|
val module = moduleSourceRootGroup.baseModule
|
||||||
if (!KotlinPluginUtil.isMavenModule(module))
|
if (!KotlinPluginUtil.isMavenModule(module))
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
return ConfigureKotlinStatus.NON_APPLICABLE
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.idea.configuration.*
|
|||||||
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
||||||
import org.jetbrains.kotlin.js.resolve.JsPlatform
|
import org.jetbrains.kotlin.js.resolve.JsPlatform
|
||||||
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
|
||||||
import org.jetbrains.kotlin.utils.ifEmpty
|
|
||||||
|
|
||||||
abstract class ConfigureKotlinInProjectAction : AnAction() {
|
abstract class ConfigureKotlinInProjectAction : AnAction() {
|
||||||
|
|
||||||
@@ -35,7 +34,7 @@ abstract class ConfigureKotlinInProjectAction : AnAction() {
|
|||||||
override fun actionPerformed(e: AnActionEvent) {
|
override fun actionPerformed(e: AnActionEvent) {
|
||||||
val project = e.project ?: return
|
val project = e.project ?: return
|
||||||
|
|
||||||
val modules = getConfigurableModulesWithKotlinFiles(project).ifEmpty { project.allModules() }
|
val modules = getConfigurableModules(project)
|
||||||
if (modules.all(::isModuleConfigured)) {
|
if (modules.all(::isModuleConfigured)) {
|
||||||
Messages.showInfoMessage("All modules with Kotlin files are configured", e.presentation.text!!)
|
Messages.showInfoMessage("All modules with Kotlin files are configured", e.presentation.text!!)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.idea.configuration
|
|||||||
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
import com.intellij.openapi.extensions.Extensions
|
import com.intellij.openapi.extensions.Extensions
|
||||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
|
||||||
import com.intellij.openapi.module.Module
|
import com.intellij.openapi.module.Module
|
||||||
import com.intellij.openapi.module.ModuleManager
|
|
||||||
import com.intellij.openapi.project.DumbService
|
import com.intellij.openapi.project.DumbService
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.DependencyScope
|
import com.intellij.openapi.roots.DependencyScope
|
||||||
@@ -30,6 +28,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
|||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification
|
import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
||||||
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
|
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
|
||||||
import org.jetbrains.kotlin.idea.versions.hasKotlinJsKjsmFile
|
import org.jetbrains.kotlin.idea.versions.hasKotlinJsKjsmFile
|
||||||
@@ -96,9 +95,9 @@ fun getRepositoryForVersion(version: String): RepositoryDescription? = when {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isModuleConfigured(module: Module): Boolean {
|
fun isModuleConfigured(moduleSourceRootGroup: ModuleSourceRootGroup): Boolean {
|
||||||
return allConfigurators().any {
|
return allConfigurators().any {
|
||||||
it.getStatus(module) == ConfigureKotlinStatus.CONFIGURED
|
it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CONFIGURED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,57 +106,67 @@ fun getModulesWithKotlinFiles(project: Project): Collection<Module> {
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project))) {
|
if (!runReadAction { FileTypeIndex.containsFileOfType (KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project)) }) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
return project.allModules()
|
return project.allModules()
|
||||||
.filter { module ->
|
.filter { module ->
|
||||||
FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, module.getModuleScope(true))
|
runReadAction {
|
||||||
|
FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, module.getModuleScope(true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getConfigurableModulesWithKotlinFiles(project: Project): Collection<Module> {
|
fun getConfigurableModulesWithKotlinFiles(project: Project): List<ModuleSourceRootGroup> {
|
||||||
val modules = getModulesWithKotlinFiles(project)
|
val modules = getModulesWithKotlinFiles(project)
|
||||||
if (modules.isEmpty()) return modules
|
if (modules.isEmpty()) return emptyList()
|
||||||
|
|
||||||
val pathMap = ModuleManager.getInstance(project).modules.asList().buildExternalPathMap()
|
return ModuleSourceRootMap(project).groupByBaseModules(modules)
|
||||||
return modules.mapTo(HashSet()) { module ->
|
|
||||||
val externalPath = module.externalProjectPath
|
|
||||||
if (externalPath == null) module else (pathMap[externalPath] ?: module)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showConfigureKotlinNotificationIfNeeded(module: Module) {
|
fun showConfigureKotlinNotificationIfNeeded(module: Module) {
|
||||||
if (isModuleConfigured(module)) return
|
val moduleGroup = ModuleSourceRootMap(module.project).toModuleGroup(module)
|
||||||
|
if (isModuleConfigured(moduleGroup)) return
|
||||||
|
|
||||||
ConfigureKotlinNotificationManager.notify(module.project)
|
ConfigureKotlinNotificationManager.notify(module.project)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showConfigureKotlinNotificationIfNeeded(project: Project, excludeModules: List<Module> = emptyList()) {
|
fun showConfigureKotlinNotificationIfNeeded(project: Project, excludeModules: List<Module> = emptyList()) {
|
||||||
ApplicationManager.getApplication().executeOnPooledThread {
|
val notificationString = DumbService.getInstance(project).runReadActionInSmartMode(Computable {
|
||||||
val notificationString = DumbService.getInstance(project).runReadActionInSmartMode(Computable {
|
val modules = getConfigurableModulesWithKotlinFiles(project).exclude(excludeModules)
|
||||||
val modules = getConfigurableModulesWithKotlinFiles(project) - excludeModules
|
if (modules.all(::isModuleConfigured))
|
||||||
if (modules.all(::isModuleConfigured)) null else ConfigureKotlinNotification.getNotificationString(project, excludeModules)
|
null
|
||||||
})
|
else
|
||||||
if (notificationString != null) {
|
ConfigureKotlinNotification.getNotificationString(project, excludeModules)
|
||||||
ApplicationManager.getApplication().invokeLater {
|
})
|
||||||
ConfigureKotlinNotificationManager.notify(project, ConfigureKotlinNotification(project, excludeModules, notificationString))
|
|
||||||
}
|
if (notificationString != null) {
|
||||||
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
ConfigureKotlinNotificationManager.notify(project, ConfigureKotlinNotification(project, excludeModules, notificationString))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAbleToRunConfigurators(project: Project): Collection<KotlinProjectConfigurator> {
|
fun getAbleToRunConfigurators(project: Project): Collection<KotlinProjectConfigurator> {
|
||||||
val modules = getConfigurableModulesWithKotlinFiles(project).ifEmpty { project.allModules() }
|
val modules = getConfigurableModules(project)
|
||||||
|
|
||||||
return allConfigurators().filter { configurator ->
|
return allConfigurators().filter { configurator ->
|
||||||
modules.any { module -> configurator.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
|
modules.any { configurator.getStatus(it) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getConfigurableModules(project: Project): List<ModuleSourceRootGroup> {
|
||||||
|
return getConfigurableModulesWithKotlinFiles(project).ifEmpty {
|
||||||
|
ModuleSourceRootMap(project).groupByBaseModules(project.allModules())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAbleToRunConfigurators(module: Module): Collection<KotlinProjectConfigurator> {
|
fun getAbleToRunConfigurators(module: Module): Collection<KotlinProjectConfigurator> {
|
||||||
return allConfigurators().filter { it.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
|
val moduleGroup = ModuleSourceRootMap(module.project).toModuleGroup(module)
|
||||||
|
return allConfigurators().filter {
|
||||||
|
it.getStatus(moduleGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getConfiguratorByName(name: String): KotlinProjectConfigurator? {
|
fun getConfiguratorByName(name: String): KotlinProjectConfigurator? {
|
||||||
@@ -167,56 +176,26 @@ fun getConfiguratorByName(name: String): KotlinProjectConfigurator? {
|
|||||||
fun allConfigurators() = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
|
fun allConfigurators() = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
|
||||||
|
|
||||||
fun getCanBeConfiguredModules(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
|
fun getCanBeConfiguredModules(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
|
||||||
return project.allModules()
|
return ModuleSourceRootMap(project).groupByBaseModules(project.allModules())
|
||||||
.filter { module -> configurator.canConfigure(module) }
|
.filter { configurator.canConfigure(it) }
|
||||||
.excludeSourceRootModules()
|
.map { it.baseModule }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinProjectConfigurator.canConfigure(module: Module) =
|
private fun KotlinProjectConfigurator.canConfigure(moduleSourceRootGroup: ModuleSourceRootGroup) =
|
||||||
getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED &&
|
getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED &&
|
||||||
(allConfigurators().toList() - this).none { it.getStatus(module) == ConfigureKotlinStatus.CONFIGURED }
|
(allConfigurators().toList() - this).none { it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CONFIGURED }
|
||||||
|
|
||||||
fun Collection<Module>.excludeSourceRootModules(): List<Module> {
|
|
||||||
val pathMap = buildExternalPathMap()
|
|
||||||
return filter { it.externalProjectId == null || it.externalProjectPath == null } + pathMap.values
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Collection<Module>.buildExternalPathMap(): Map<String, Module> {
|
|
||||||
val pathMap = mutableMapOf<String, Module>()
|
|
||||||
for (module in this) {
|
|
||||||
val externalId = module.externalProjectId
|
|
||||||
val externalPath = module.externalProjectPath
|
|
||||||
if (externalId != null && externalPath != null) {
|
|
||||||
val previousModule = pathMap[externalPath]
|
|
||||||
// the module without the source root suffix will have the shortest name
|
|
||||||
if (previousModule == null || isSourceRootPrefix(externalId, previousModule.externalProjectId!!)) {
|
|
||||||
pathMap[externalPath] = module
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pathMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isSourceRootPrefix(externalId: String, previousModuleExternalId: String)
|
|
||||||
= externalId.length < previousModuleExternalId.length && previousModuleExternalId.startsWith(externalId)
|
|
||||||
|
|
||||||
val Module.externalProjectId: String?
|
|
||||||
get() = ExternalSystemApiUtil.getExternalProjectId(this)
|
|
||||||
|
|
||||||
val Module.externalProjectPath: String?
|
|
||||||
get() = ExternalSystemApiUtil.getExternalProjectPath(this)
|
|
||||||
|
|
||||||
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
|
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
|
||||||
val modules = getConfigurableModulesWithKotlinFiles(project)
|
val modules = getConfigurableModulesWithKotlinFiles(project)
|
||||||
return modules.filter { module -> configurator.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
|
return modules.filter { configurator.getStatus(it) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }.map { it.baseModule }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, excludeModules: Collection<Module> = emptyList()): Collection<Module> {
|
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, excludeModules: Collection<Module> = emptyList()): Collection<Module> {
|
||||||
val modulesWithKotlinFiles = getConfigurableModulesWithKotlinFiles(project) - excludeModules
|
val modulesWithKotlinFiles = getConfigurableModulesWithKotlinFiles(project).exclude(excludeModules)
|
||||||
val configurators = allConfigurators()
|
val configurators = allConfigurators()
|
||||||
return modulesWithKotlinFiles.filter { module ->
|
return modulesWithKotlinFiles.filter { moduleSourceRootGroup ->
|
||||||
configurators.any { it.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
|
configurators.any { it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
|
||||||
}
|
}.map { it.baseModule }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasAnyKotlinRuntimeInScope(module: Module): Boolean {
|
fun hasAnyKotlinRuntimeInScope(module: Module): Boolean {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ enum class ConfigureKotlinStatus {
|
|||||||
|
|
||||||
interface KotlinProjectConfigurator {
|
interface KotlinProjectConfigurator {
|
||||||
|
|
||||||
fun getStatus(module: Module): ConfigureKotlinStatus
|
fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus
|
||||||
|
|
||||||
@JvmSuppressWildcards fun configure(project: Project, excludeModules: Collection<Module>)
|
@JvmSuppressWildcards fun configure(project: Project, excludeModules: Collection<Module>)
|
||||||
|
|
||||||
|
|||||||
@@ -46,12 +46,13 @@ import java.util.*
|
|||||||
|
|
||||||
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
|
||||||
|
|
||||||
override fun getStatus(module: Module): ConfigureKotlinStatus {
|
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
||||||
|
val module = moduleSourceRootGroup.baseModule
|
||||||
if (!isApplicable(module)) {
|
if (!isApplicable(module)) {
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
return ConfigureKotlinStatus.NON_APPLICABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAnyKotlinRuntimeInScope(module)) {
|
if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) {
|
||||||
return ConfigureKotlinStatus.CONFIGURED
|
return ConfigureKotlinStatus.CONFIGURED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
|
|||||||
|
|
||||||
protected val libraryKind: PersistentLibraryKind<*>? = libraryType?.kind
|
protected val libraryKind: PersistentLibraryKind<*>? = libraryType?.kind
|
||||||
|
|
||||||
override fun getStatus(module: Module): ConfigureKotlinStatus {
|
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
||||||
|
val module = moduleSourceRootGroup.baseModule
|
||||||
if (!isApplicable(module)) {
|
if (!isApplicable(module)) {
|
||||||
return ConfigureKotlinStatus.NON_APPLICABLE
|
return ConfigureKotlinStatus.NON_APPLICABLE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.configuration
|
||||||
|
|
||||||
|
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
||||||
|
|
||||||
|
class ModuleSourceRootGroup(val baseModule: Module,
|
||||||
|
val sourceRootModules: List<Module>)
|
||||||
|
|
||||||
|
class ModuleSourceRootMap(val modules: Collection<Module>) {
|
||||||
|
private val baseModuleByExternalPath: Map<String, Module>
|
||||||
|
private val allModulesByExternalPath: Map<String, List<Module>>
|
||||||
|
|
||||||
|
constructor(project: Project): this(project.allModules())
|
||||||
|
|
||||||
|
init {
|
||||||
|
allModulesByExternalPath = modules
|
||||||
|
.filter { it.externalProjectPath != null && it.externalProjectId != null }
|
||||||
|
.groupBy { it.externalProjectPath!! }
|
||||||
|
|
||||||
|
baseModuleByExternalPath = allModulesByExternalPath
|
||||||
|
.mapValues { (path, modules) ->
|
||||||
|
modules.reduce { m1, m2 ->
|
||||||
|
if (isSourceRootPrefix(m2.externalProjectId!!, m1.externalProjectId!!)) m2 else m1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun groupByBaseModules(modules: Collection<Module>): List<ModuleSourceRootGroup> {
|
||||||
|
return modules
|
||||||
|
.groupBy { module ->
|
||||||
|
val externalPath = module.externalProjectPath
|
||||||
|
if (externalPath == null) module else (baseModuleByExternalPath[externalPath] ?: module)
|
||||||
|
}
|
||||||
|
.map { (module, sourceRootModules) ->
|
||||||
|
ModuleSourceRootGroup(module,
|
||||||
|
if (sourceRootModules.size > 1) sourceRootModules - module else sourceRootModules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toModuleGroup(module: Module): ModuleSourceRootGroup = groupByBaseModules(listOf(module)).single()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSourceRootPrefix(externalId: String, previousModuleExternalId: String)
|
||||||
|
= externalId.length < previousModuleExternalId.length && previousModuleExternalId.startsWith(externalId)
|
||||||
|
|
||||||
|
val Module.externalProjectId: String?
|
||||||
|
get() = ExternalSystemApiUtil.getExternalProjectId(this)
|
||||||
|
|
||||||
|
val Module.externalProjectPath: String?
|
||||||
|
get() = ExternalSystemApiUtil.getExternalProjectPath(this)
|
||||||
|
|
||||||
|
fun List<ModuleSourceRootGroup>.exclude(excludeModules: Collection<Module>): List<ModuleSourceRootGroup> {
|
||||||
|
return mapNotNull {
|
||||||
|
if (it.baseModule in excludeModules)
|
||||||
|
null
|
||||||
|
else {
|
||||||
|
val remainingSourceRootModules = it.sourceRootModules - excludeModules
|
||||||
|
if (remainingSourceRootModules.isEmpty())
|
||||||
|
null
|
||||||
|
else
|
||||||
|
ModuleSourceRootGroup(it.baseModule, remainingSourceRootModules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-13
@@ -23,13 +23,10 @@ import com.intellij.openapi.application.ApplicationManager
|
|||||||
import com.intellij.openapi.components.AbstractProjectComponent
|
import com.intellij.openapi.components.AbstractProjectComponent
|
||||||
import com.intellij.openapi.project.DumbService
|
import com.intellij.openapi.project.DumbService
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.roots.ModuleRootAdapter
|
|
||||||
import com.intellij.openapi.roots.ModuleRootEvent
|
import com.intellij.openapi.roots.ModuleRootEvent
|
||||||
|
import com.intellij.openapi.roots.ModuleRootListener
|
||||||
import com.intellij.openapi.startup.StartupManager
|
import com.intellij.openapi.startup.StartupManager
|
||||||
import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinNotificationManager
|
import org.jetbrains.kotlin.idea.configuration.*
|
||||||
import org.jetbrains.kotlin.idea.configuration.getModulesWithKotlinFiles
|
|
||||||
import org.jetbrains.kotlin.idea.configuration.isModuleConfigured
|
|
||||||
import org.jetbrains.kotlin.idea.configuration.showConfigureKotlinNotificationIfNeeded
|
|
||||||
import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies
|
import org.jetbrains.kotlin.idea.project.getAndCacheLanguageLevelByDependencies
|
||||||
import org.jetbrains.kotlin.idea.versions.collectModulesWithOutdatedRuntime
|
import org.jetbrains.kotlin.idea.versions.collectModulesWithOutdatedRuntime
|
||||||
import org.jetbrains.kotlin.idea.versions.findOutdatedKotlinLibraries
|
import org.jetbrains.kotlin.idea.versions.findOutdatedKotlinLibraries
|
||||||
@@ -44,10 +41,11 @@ class KotlinConfigurationCheckerComponent(project: Project) : AbstractProjectCom
|
|||||||
NotificationsConfiguration.getNotificationsConfiguration().register(CONFIGURE_NOTIFICATION_GROUP_ID, NotificationDisplayType.STICKY_BALLOON, true)
|
NotificationsConfiguration.getNotificationsConfiguration().register(CONFIGURE_NOTIFICATION_GROUP_ID, NotificationDisplayType.STICKY_BALLOON, true)
|
||||||
|
|
||||||
val connection = project.messageBus.connect()
|
val connection = project.messageBus.connect()
|
||||||
connection.subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootAdapter() {
|
connection.subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootListener {
|
||||||
override fun rootsChanged(event: ModuleRootEvent?) {
|
override fun rootsChanged(event: ModuleRootEvent?) {
|
||||||
if (notificationPostponed && !isSyncing) {
|
if (notificationPostponed && !isSyncing) {
|
||||||
DumbService.getInstance(myProject).smartInvokeLater {
|
ApplicationManager.getApplication().executeOnPooledThread {
|
||||||
|
DumbService.getInstance(myProject).waitForSmartMode()
|
||||||
if (!isSyncing) {
|
if (!isSyncing) {
|
||||||
notificationPostponed = false
|
notificationPostponed = false
|
||||||
showConfigureKotlinNotificationIfNeeded(myProject,
|
showConfigureKotlinNotificationIfNeeded(myProject,
|
||||||
@@ -59,7 +57,7 @@ class KotlinConfigurationCheckerComponent(project: Project) : AbstractProjectCom
|
|||||||
if (ConfigureKotlinNotificationManager.getVisibleNotifications(project).isNotEmpty()) {
|
if (ConfigureKotlinNotificationManager.getVisibleNotifications(project).isNotEmpty()) {
|
||||||
ApplicationManager.getApplication().executeOnPooledThread {
|
ApplicationManager.getApplication().executeOnPooledThread {
|
||||||
DumbService.getInstance(myProject).waitForSmartMode()
|
DumbService.getInstance(myProject).waitForSmartMode()
|
||||||
if (getModulesWithKotlinFiles(project).all(::isModuleConfigured)) {
|
if (getConfigurableModulesWithKotlinFiles(project).all(::isModuleConfigured)) {
|
||||||
ApplicationManager.getApplication().invokeLater {
|
ApplicationManager.getApplication().invokeLater {
|
||||||
ConfigureKotlinNotificationManager.expireOldNotifications(project)
|
ConfigureKotlinNotificationManager.expireOldNotifications(project)
|
||||||
}
|
}
|
||||||
@@ -74,18 +72,22 @@ class KotlinConfigurationCheckerComponent(project: Project) : AbstractProjectCom
|
|||||||
super.projectOpened()
|
super.projectOpened()
|
||||||
|
|
||||||
StartupManager.getInstance(myProject).registerPostStartupActivity {
|
StartupManager.getInstance(myProject).registerPostStartupActivity {
|
||||||
DumbService.getInstance(myProject).smartInvokeLater {
|
ApplicationManager.getApplication().executeOnPooledThread {
|
||||||
|
DumbService.getInstance(myProject).waitForSmartMode()
|
||||||
|
|
||||||
for (module in getModulesWithKotlinFiles(myProject)) {
|
for (module in getModulesWithKotlinFiles(myProject)) {
|
||||||
module.getAndCacheLanguageLevelByDependencies()
|
module.getAndCacheLanguageLevelByDependencies()
|
||||||
}
|
}
|
||||||
|
|
||||||
val libraries = findOutdatedKotlinLibraries(myProject)
|
val libraries = findOutdatedKotlinLibraries(myProject)
|
||||||
if (!libraries.isEmpty()) {
|
if (!libraries.isEmpty()) {
|
||||||
notifyOutdatedKotlinRuntime(myProject, libraries)
|
ApplicationManager.getApplication().invokeLater {
|
||||||
|
notifyOutdatedKotlinRuntime(myProject, libraries)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (syncDepth.get() == 0) {
|
if (!isSyncing) {
|
||||||
showConfigureKotlinNotificationIfNeeded(myProject,
|
val excludeModules = collectModulesWithOutdatedRuntime(libraries)
|
||||||
collectModulesWithOutdatedRuntime(libraries))
|
showConfigureKotlinNotificationIfNeeded(myProject, excludeModules)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
notificationPostponed = true
|
notificationPostponed = true
|
||||||
|
|||||||
@@ -60,11 +60,12 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
|||||||
myProject.baseDir.createChildData(null, "build.gradle")
|
myProject.baseDir.createChildData(null, "build.gradle")
|
||||||
|
|
||||||
val module = ModuleManager.getInstance(myProject).findModuleByName("app")!!
|
val module = ModuleManager.getInstance(myProject).findModuleByName("app")!!
|
||||||
|
val moduleGroup = ModuleSourceRootMap(myProject).toModuleGroup(module)
|
||||||
// We have a Kotlin runtime in build.gradle but not in the classpath, so it doesn't make sense
|
// We have a Kotlin runtime in build.gradle but not in the classpath, so it doesn't make sense
|
||||||
// to suggest configuring it
|
// to suggest configuring it
|
||||||
assertEquals(ConfigureKotlinStatus.BROKEN, findGradleModuleConfigurator().getStatus(module))
|
assertEquals(ConfigureKotlinStatus.BROKEN, findGradleModuleConfigurator().getStatus(moduleGroup))
|
||||||
// Don't offer the JS configurator if the JVM configuration exists but is broken
|
// Don't offer the JS configurator if the JVM configuration exists but is broken
|
||||||
assertEquals(ConfigureKotlinStatus.BROKEN, findJsGradleModuleConfigurator().getStatus(module))
|
assertEquals(ConfigureKotlinStatus.BROKEN, findJsGradleModuleConfigurator().getStatus(moduleGroup))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,6 +213,68 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testListNonConfiguredModules_Configured() {
|
||||||
|
createProjectSubFile("settings.gradle", "include ':app'")
|
||||||
|
createProjectSubFile("app/build.gradle", """
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1.3"
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
createProjectSubFile("app/src/main/java/foo.kt", "")
|
||||||
|
|
||||||
|
importProject()
|
||||||
|
|
||||||
|
runReadAction {
|
||||||
|
assertEmpty(getCanBeConfiguredModulesWithKotlinFiles(myProject))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testListNonConfiguredModules_ConfiguredOnlyTest() {
|
||||||
|
createProjectSubFile("settings.gradle", "include ':app'")
|
||||||
|
createProjectSubFile("app/build.gradle", """
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testCompile "org.jetbrains.kotlin:kotlin-stdlib:1.1.3"
|
||||||
|
}
|
||||||
|
""".trimIndent())
|
||||||
|
createProjectSubFile("app/src/test/java/foo.kt", "")
|
||||||
|
|
||||||
|
importProject()
|
||||||
|
|
||||||
|
runReadAction {
|
||||||
|
assertEmpty(getCanBeConfiguredModulesWithKotlinFiles(myProject))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testAddNonKotlinLibraryGSK() {
|
fun testAddNonKotlinLibraryGSK() {
|
||||||
val buildScript = createProjectSubFile("build.gradle.kts",
|
val buildScript = createProjectSubFile("build.gradle.kts",
|
||||||
@@ -751,5 +814,4 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
|||||||
}
|
}
|
||||||
""".trimIndent(), LoadTextUtil.loadText(buildScript).toString())
|
""".trimIndent(), LoadTextUtil.loadText(buildScript).toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.codeInsight.gradle
|
|||||||
|
|
||||||
import com.intellij.openapi.application.Result
|
import com.intellij.openapi.application.Result
|
||||||
import com.intellij.openapi.application.WriteAction
|
import com.intellij.openapi.application.WriteAction
|
||||||
|
import com.intellij.openapi.application.runReadAction
|
||||||
import com.intellij.openapi.projectRoots.JavaSdk
|
import com.intellij.openapi.projectRoots.JavaSdk
|
||||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||||
@@ -27,9 +28,13 @@ import com.intellij.openapi.util.text.StringUtil
|
|||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
import org.jetbrains.kotlin.config.*
|
import org.jetbrains.kotlin.config.*
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.ConfigureKotlinStatus
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.ModuleSourceRootMap
|
||||||
|
import org.jetbrains.kotlin.idea.configuration.allConfigurators
|
||||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||||
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
import org.jetbrains.kotlin.idea.framework.CommonLibraryKind
|
||||||
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
import org.jetbrains.kotlin.idea.framework.JSLibraryKind
|
||||||
|
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -46,9 +51,6 @@ class GradleFacetImportTest : GradleImportingTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testJvmImport() {
|
fun testJvmImport() {
|
||||||
createProjectSubFile("build.gradle", """
|
createProjectSubFile("build.gradle", """
|
||||||
group 'Again'
|
|
||||||
version '1.0-SNAPSHOT'
|
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -97,6 +99,48 @@ class GradleFacetImportTest : GradleImportingTestCase() {
|
|||||||
Assert.assertEquals("-Xdump-declarations-to=tmpTest",
|
Assert.assertEquals("-Xdump-declarations-to=tmpTest",
|
||||||
compilerSettings!!.additionalArguments)
|
compilerSettings!!.additionalArguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertAllModulesConfigured()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testJvmImportWithPlugin() {
|
||||||
|
createProjectSubFile("build.gradle", """
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.jvm" version "1.1.3"
|
||||||
|
}
|
||||||
|
|
||||||
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
|
apply plugin: 'java'
|
||||||
|
|
||||||
|
sourceCompatibility = 1.8
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:1.1.3"
|
||||||
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
|
}
|
||||||
|
|
||||||
|
compileKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
compileTestKotlin {
|
||||||
|
kotlinOptions.jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
importProject()
|
||||||
|
|
||||||
|
assertAllModulesConfigured()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -227,6 +271,8 @@ class GradleFacetImportTest : GradleImportingTestCase() {
|
|||||||
Assert.assertEquals("-Xdump-declarations-to=tmpTest",
|
Assert.assertEquals("-Xdump-declarations-to=tmpTest",
|
||||||
compilerSettings!!.additionalArguments)
|
compilerSettings!!.additionalArguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertAllModulesConfigured()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -436,6 +482,8 @@ class GradleFacetImportTest : GradleImportingTestCase() {
|
|||||||
val rootManager = ModuleRootManager.getInstance(getModule("project_main"))
|
val rootManager = ModuleRootManager.getInstance(getModule("project_main"))
|
||||||
val stdlib = rootManager.orderEntries.filterIsInstance<LibraryOrderEntry>().single().library
|
val stdlib = rootManager.orderEntries.filterIsInstance<LibraryOrderEntry>().single().library
|
||||||
assertEquals(JSLibraryKind, (stdlib as LibraryEx).kind)
|
assertEquals(JSLibraryKind, (stdlib as LibraryEx).kind)
|
||||||
|
|
||||||
|
assertAllModulesConfigured()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -511,6 +559,8 @@ class GradleFacetImportTest : GradleImportingTestCase() {
|
|||||||
Assert.assertEquals("-main callTest",
|
Assert.assertEquals("-main callTest",
|
||||||
compilerSettings!!.additionalArguments)
|
compilerSettings!!.additionalArguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertAllModulesConfigured()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1160,4 +1210,15 @@ class GradleFacetImportTest : GradleImportingTestCase() {
|
|||||||
}.execute()
|
}.execute()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assertAllModulesConfigured() {
|
||||||
|
runReadAction {
|
||||||
|
for (moduleGroup in ModuleSourceRootMap(myProject).groupByBaseModules(myProject.allModules())) {
|
||||||
|
val configurator = allConfigurators().find {
|
||||||
|
it.getStatus(moduleGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED
|
||||||
|
}
|
||||||
|
Assert.assertNull("Configurator $configurator tells that ${moduleGroup.baseModule} can be configured", configurator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user