Skip to content

Commit f370d74

Browse files
committed
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.
1 parent 0971af8 commit f370d74

File tree

11 files changed

+283
-93
lines changed

11 files changed

+283
-93
lines changed

core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ inline fun <K, V> MutableMap<K, V>.getOrPutNullable(key: K, defaultValue: () ->
5555

5656
inline fun <T, C: Collection<T>> C.ifEmpty(body: () -> C): C = if (isEmpty()) body() else this
5757

58+
inline fun <K, V, M: Map<K, V>> M.ifEmpty(body: () -> M): M = if (isEmpty()) body() else this
59+
5860
inline fun <T> Array<out T>.ifEmpty(body: () -> Array<out T>): Array<out T> = if (isEmpty()) body() else this
5961

6062
fun <T: Any> MutableCollection<T>.addIfNotNull(t: T?) {

idea/idea-maven/src/org/jetbrains/kotlin/idea/maven/configuration/KotlinMavenConfigurator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ abstract class KotlinMavenConfigurator
4848
override val name: String,
4949
override val presentableText: String) : KotlinProjectConfigurator {
5050

51-
override fun getStatus(module: Module): ConfigureKotlinStatus {
51+
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
52+
val module = moduleSourceRootGroup.baseModule
5253
if (!KotlinPluginUtil.isMavenModule(module))
5354
return ConfigureKotlinStatus.NON_APPLICABLE
5455

idea/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.idea.configuration.*
2626
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
2727
import org.jetbrains.kotlin.js.resolve.JsPlatform
2828
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
29-
import org.jetbrains.kotlin.utils.ifEmpty
3029

3130
abstract class ConfigureKotlinInProjectAction : AnAction() {
3231

@@ -35,7 +34,7 @@ abstract class ConfigureKotlinInProjectAction : AnAction() {
3534
override fun actionPerformed(e: AnActionEvent) {
3635
val project = e.project ?: return
3736

38-
val modules = getConfigurableModulesWithKotlinFiles(project).ifEmpty { project.allModules() }
37+
val modules = getConfigurableModules(project)
3938
if (modules.all(::isModuleConfigured)) {
4039
Messages.showInfoMessage("All modules with Kotlin files are configured", e.presentation.text!!)
4140
return

idea/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt

Lines changed: 46 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.idea.configuration
1818

1919
import com.intellij.openapi.application.ApplicationManager
2020
import com.intellij.openapi.extensions.Extensions
21-
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
2221
import com.intellij.openapi.module.Module
23-
import com.intellij.openapi.module.ModuleManager
2422
import com.intellij.openapi.project.DumbService
2523
import com.intellij.openapi.project.Project
2624
import com.intellij.openapi.roots.DependencyScope
@@ -30,6 +28,7 @@ import com.intellij.psi.search.GlobalSearchScope
3028
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
3129
import org.jetbrains.kotlin.idea.KotlinFileType
3230
import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification
31+
import org.jetbrains.kotlin.idea.util.application.runReadAction
3332
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
3433
import org.jetbrains.kotlin.idea.versions.getKotlinJvmRuntimeMarkerClass
3534
import org.jetbrains.kotlin.idea.versions.hasKotlinJsKjsmFile
@@ -96,9 +95,9 @@ fun getRepositoryForVersion(version: String): RepositoryDescription? = when {
9695
else -> null
9796
}
9897

99-
fun isModuleConfigured(module: Module): Boolean {
98+
fun isModuleConfigured(moduleSourceRootGroup: ModuleSourceRootGroup): Boolean {
10099
return allConfigurators().any {
101-
it.getStatus(module) == ConfigureKotlinStatus.CONFIGURED
100+
it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CONFIGURED
102101
}
103102
}
104103

@@ -107,57 +106,67 @@ fun getModulesWithKotlinFiles(project: Project): Collection<Module> {
107106
return emptyList()
108107
}
109108

110-
if (!FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project))) {
109+
if (!runReadAction { FileTypeIndex.containsFileOfType (KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project)) }) {
111110
return emptyList()
112111
}
113112

114113
return project.allModules()
115114
.filter { module ->
116-
FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, module.getModuleScope(true))
115+
runReadAction {
116+
FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, module.getModuleScope(true))
117+
}
117118
}
118119
}
119120

120-
fun getConfigurableModulesWithKotlinFiles(project: Project): Collection<Module> {
121+
fun getConfigurableModulesWithKotlinFiles(project: Project): List<ModuleSourceRootGroup> {
121122
val modules = getModulesWithKotlinFiles(project)
122-
if (modules.isEmpty()) return modules
123+
if (modules.isEmpty()) return emptyList()
123124

124-
val pathMap = ModuleManager.getInstance(project).modules.asList().buildExternalPathMap()
125-
return modules.mapTo(HashSet()) { module ->
126-
val externalPath = module.externalProjectPath
127-
if (externalPath == null) module else (pathMap[externalPath] ?: module)
128-
}
125+
return ModuleSourceRootMap(project).groupByBaseModules(modules)
129126
}
130127

131128
fun showConfigureKotlinNotificationIfNeeded(module: Module) {
132-
if (isModuleConfigured(module)) return
129+
val moduleGroup = ModuleSourceRootMap(module.project).toModuleGroup(module)
130+
if (isModuleConfigured(moduleGroup)) return
133131

134132
ConfigureKotlinNotificationManager.notify(module.project)
135133
}
136134

137135
fun showConfigureKotlinNotificationIfNeeded(project: Project, excludeModules: List<Module> = emptyList()) {
138-
ApplicationManager.getApplication().executeOnPooledThread {
139-
val notificationString = DumbService.getInstance(project).runReadActionInSmartMode(Computable {
140-
val modules = getConfigurableModulesWithKotlinFiles(project) - excludeModules
141-
if (modules.all(::isModuleConfigured)) null else ConfigureKotlinNotification.getNotificationString(project, excludeModules)
142-
})
143-
if (notificationString != null) {
144-
ApplicationManager.getApplication().invokeLater {
145-
ConfigureKotlinNotificationManager.notify(project, ConfigureKotlinNotification(project, excludeModules, notificationString))
146-
}
136+
val notificationString = DumbService.getInstance(project).runReadActionInSmartMode(Computable {
137+
val modules = getConfigurableModulesWithKotlinFiles(project).exclude(excludeModules)
138+
if (modules.all(::isModuleConfigured))
139+
null
140+
else
141+
ConfigureKotlinNotification.getNotificationString(project, excludeModules)
142+
})
143+
144+
if (notificationString != null) {
145+
ApplicationManager.getApplication().invokeLater {
146+
ConfigureKotlinNotificationManager.notify(project, ConfigureKotlinNotification(project, excludeModules, notificationString))
147147
}
148148
}
149149
}
150150

151151
fun getAbleToRunConfigurators(project: Project): Collection<KotlinProjectConfigurator> {
152-
val modules = getConfigurableModulesWithKotlinFiles(project).ifEmpty { project.allModules() }
152+
val modules = getConfigurableModules(project)
153153

154154
return allConfigurators().filter { configurator ->
155-
modules.any { module -> configurator.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
155+
modules.any { configurator.getStatus(it) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
156+
}
157+
}
158+
159+
fun getConfigurableModules(project: Project): List<ModuleSourceRootGroup> {
160+
return getConfigurableModulesWithKotlinFiles(project).ifEmpty {
161+
ModuleSourceRootMap(project).groupByBaseModules(project.allModules())
156162
}
157163
}
158164

159165
fun getAbleToRunConfigurators(module: Module): Collection<KotlinProjectConfigurator> {
160-
return allConfigurators().filter { it.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
166+
val moduleGroup = ModuleSourceRootMap(module.project).toModuleGroup(module)
167+
return allConfigurators().filter {
168+
it.getStatus(moduleGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED
169+
}
161170
}
162171

163172
fun getConfiguratorByName(name: String): KotlinProjectConfigurator? {
@@ -167,56 +176,26 @@ fun getConfiguratorByName(name: String): KotlinProjectConfigurator? {
167176
fun allConfigurators() = Extensions.getExtensions(KotlinProjectConfigurator.EP_NAME)
168177

169178
fun getCanBeConfiguredModules(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
170-
return project.allModules()
171-
.filter { module -> configurator.canConfigure(module) }
172-
.excludeSourceRootModules()
173-
}
174-
175-
private fun KotlinProjectConfigurator.canConfigure(module: Module) =
176-
getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED &&
177-
(allConfigurators().toList() - this).none { it.getStatus(module) == ConfigureKotlinStatus.CONFIGURED }
178-
179-
fun Collection<Module>.excludeSourceRootModules(): List<Module> {
180-
val pathMap = buildExternalPathMap()
181-
return filter { it.externalProjectId == null || it.externalProjectPath == null } + pathMap.values
179+
return ModuleSourceRootMap(project).groupByBaseModules(project.allModules())
180+
.filter { configurator.canConfigure(it) }
181+
.map { it.baseModule }
182182
}
183183

184-
fun Collection<Module>.buildExternalPathMap(): Map<String, Module> {
185-
val pathMap = mutableMapOf<String, Module>()
186-
for (module in this) {
187-
val externalId = module.externalProjectId
188-
val externalPath = module.externalProjectPath
189-
if (externalId != null && externalPath != null) {
190-
val previousModule = pathMap[externalPath]
191-
// the module without the source root suffix will have the shortest name
192-
if (previousModule == null || isSourceRootPrefix(externalId, previousModule.externalProjectId!!)) {
193-
pathMap[externalPath] = module
194-
}
195-
}
196-
}
197-
return pathMap
198-
}
199-
200-
private fun isSourceRootPrefix(externalId: String, previousModuleExternalId: String)
201-
= externalId.length < previousModuleExternalId.length && previousModuleExternalId.startsWith(externalId)
202-
203-
val Module.externalProjectId: String?
204-
get() = ExternalSystemApiUtil.getExternalProjectId(this)
205-
206-
val Module.externalProjectPath: String?
207-
get() = ExternalSystemApiUtil.getExternalProjectPath(this)
184+
private fun KotlinProjectConfigurator.canConfigure(moduleSourceRootGroup: ModuleSourceRootGroup) =
185+
getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED &&
186+
(allConfigurators().toList() - this).none { it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CONFIGURED }
208187

209188
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
210189
val modules = getConfigurableModulesWithKotlinFiles(project)
211-
return modules.filter { module -> configurator.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
190+
return modules.filter { configurator.getStatus(it) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }.map { it.baseModule }
212191
}
213192

214193
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, excludeModules: Collection<Module> = emptyList()): Collection<Module> {
215-
val modulesWithKotlinFiles = getConfigurableModulesWithKotlinFiles(project) - excludeModules
194+
val modulesWithKotlinFiles = getConfigurableModulesWithKotlinFiles(project).exclude(excludeModules)
216195
val configurators = allConfigurators()
217-
return modulesWithKotlinFiles.filter { module ->
218-
configurators.any { it.getStatus(module) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
219-
}
196+
return modulesWithKotlinFiles.filter { moduleSourceRootGroup ->
197+
configurators.any { it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }
198+
}.map { it.baseModule }
220199
}
221200

222201
fun hasAnyKotlinRuntimeInScope(module: Module): Boolean {

idea/src/org/jetbrains/kotlin/idea/configuration/KotlinProjectConfigurator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ enum class ConfigureKotlinStatus {
3737

3838
interface KotlinProjectConfigurator {
3939

40-
fun getStatus(module: Module): ConfigureKotlinStatus
40+
fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus
4141

4242
@JvmSuppressWildcards fun configure(project: Project, excludeModules: Collection<Module>)
4343

idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithGradleConfigurator.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@ import java.util.*
4646

4747
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
4848

49-
override fun getStatus(module: Module): ConfigureKotlinStatus {
49+
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
50+
val module = moduleSourceRootGroup.baseModule
5051
if (!isApplicable(module)) {
5152
return ConfigureKotlinStatus.NON_APPLICABLE
5253
}
5354

54-
if (hasAnyKotlinRuntimeInScope(module)) {
55+
if (moduleSourceRootGroup.sourceRootModules.all(::hasAnyKotlinRuntimeInScope)) {
5556
return ConfigureKotlinStatus.CONFIGURED
5657
}
5758

idea/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
5151

5252
protected val libraryKind: PersistentLibraryKind<*>? = libraryType?.kind
5353

54-
override fun getStatus(module: Module): ConfigureKotlinStatus {
54+
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
55+
val module = moduleSourceRootGroup.baseModule
5556
if (!isApplicable(module)) {
5657
return ConfigureKotlinStatus.NON_APPLICABLE
5758
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2010-2017 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.jetbrains.kotlin.idea.configuration
18+
19+
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil
20+
import com.intellij.openapi.module.Module
21+
import com.intellij.openapi.project.Project
22+
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
23+
24+
class ModuleSourceRootGroup(val baseModule: Module,
25+
val sourceRootModules: List<Module>)
26+
27+
class ModuleSourceRootMap(val modules: Collection<Module>) {
28+
private val baseModuleByExternalPath: Map<String, Module>
29+
private val allModulesByExternalPath: Map<String, List<Module>>
30+
31+
constructor(project: Project): this(project.allModules())
32+
33+
init {
34+
allModulesByExternalPath = modules
35+
.filter { it.externalProjectPath != null && it.externalProjectId != null }
36+
.groupBy { it.externalProjectPath!! }
37+
38+
baseModuleByExternalPath = allModulesByExternalPath
39+
.mapValues { (path, modules) ->
40+
modules.reduce { m1, m2 ->
41+
if (isSourceRootPrefix(m2.externalProjectId!!, m1.externalProjectId!!)) m2 else m1
42+
}
43+
}
44+
}
45+
46+
fun groupByBaseModules(modules: Collection<Module>): List<ModuleSourceRootGroup> {
47+
return modules
48+
.groupBy { module ->
49+
val externalPath = module.externalProjectPath
50+
if (externalPath == null) module else (baseModuleByExternalPath[externalPath] ?: module)
51+
}
52+
.map { (module, sourceRootModules) ->
53+
ModuleSourceRootGroup(module,
54+
if (sourceRootModules.size > 1) sourceRootModules - module else sourceRootModules)
55+
}
56+
}
57+
58+
fun toModuleGroup(module: Module): ModuleSourceRootGroup = groupByBaseModules(listOf(module)).single()
59+
}
60+
61+
private fun isSourceRootPrefix(externalId: String, previousModuleExternalId: String)
62+
= externalId.length < previousModuleExternalId.length && previousModuleExternalId.startsWith(externalId)
63+
64+
val Module.externalProjectId: String?
65+
get() = ExternalSystemApiUtil.getExternalProjectId(this)
66+
67+
val Module.externalProjectPath: String?
68+
get() = ExternalSystemApiUtil.getExternalProjectPath(this)
69+
70+
fun List<ModuleSourceRootGroup>.exclude(excludeModules: Collection<Module>): List<ModuleSourceRootGroup> {
71+
return mapNotNull {
72+
if (it.baseModule in excludeModules)
73+
null
74+
else {
75+
val remainingSourceRootModules = it.sourceRootModules - excludeModules
76+
if (remainingSourceRootModules.isEmpty())
77+
null
78+
else
79+
ModuleSourceRootGroup(it.baseModule, remainingSourceRootModules)
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)