Further reduce number of calls to getConfigurableModulesWithKotlinFiles
Also handle INRE and requeue check
This commit is contained in:
+4
-4
@@ -191,10 +191,10 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
||||
runReadAction {
|
||||
val configurator = findGradleModuleConfigurator()
|
||||
|
||||
val (modules, ableToRunConfigurators) = getConfigurationPossibilities(myProject)
|
||||
val (modules, ableToRunConfigurators) = getConfigurationPossibilitiesForConfigureNotification(myProject)
|
||||
assertTrue(ableToRunConfigurators.any { it is KotlinGradleModuleConfigurator })
|
||||
assertTrue(ableToRunConfigurators.any { it is KotlinJsGradleModuleConfigurator })
|
||||
val moduleNames = modules.map { it.name }
|
||||
val moduleNames = modules.map { it.baseModule.name }
|
||||
assertSameElements(moduleNames, "app")
|
||||
|
||||
val moduleNamesFromConfigurator = getCanBeConfiguredModules(myProject, configurator).map { it.name }
|
||||
@@ -232,7 +232,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
||||
importProject()
|
||||
|
||||
runReadAction {
|
||||
assertEmpty(getConfigurationPossibilities(myProject).first)
|
||||
assertEmpty(getConfigurationPossibilitiesForConfigureNotification(myProject).first)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ class GradleConfiguratorTest : GradleImportingTestCase() {
|
||||
importProject()
|
||||
|
||||
runReadAction {
|
||||
assertEmpty(getConfigurationPossibilities(myProject).first)
|
||||
assertEmpty(getConfigurationPossibilitiesForConfigureNotification(myProject).first)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+29
-15
@@ -91,6 +91,10 @@ fun isModuleConfigured(moduleSourceRootGroup: ModuleSourceRootGroup): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of modules which contain sources in Kotlin.
|
||||
* Note that this method is expensive and should not be called more often than strictly necessary.
|
||||
*/
|
||||
fun getModulesWithKotlinFiles(project: Project): Collection<Module> {
|
||||
if (!runReadAction {
|
||||
!project.isDisposed && FileTypeIndex.containsFileOfType (KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project))
|
||||
@@ -106,6 +110,10 @@ fun getModulesWithKotlinFiles(project: Project): Collection<Module> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of modules which contain sources in Kotlin, grouped by base module.
|
||||
* Note that this method is expensive and should not be called more often than strictly necessary.
|
||||
*/
|
||||
fun getConfigurableModulesWithKotlinFiles(project: Project): List<ModuleSourceRootGroup> {
|
||||
val modules = getModulesWithKotlinFiles(project)
|
||||
if (modules.isEmpty()) return emptyList()
|
||||
@@ -121,17 +129,13 @@ fun showConfigureKotlinNotificationIfNeeded(module: Module) {
|
||||
}
|
||||
|
||||
fun showConfigureKotlinNotificationIfNeeded(project: Project, excludeModules: List<Module> = emptyList()) {
|
||||
val notificationString = DumbService.getInstance(project).runReadActionInSmartMode(Computable {
|
||||
val modules = getConfigurableModulesWithKotlinFiles(project).exclude(excludeModules)
|
||||
if (modules.none(::isNotConfiguredNotificationRequired))
|
||||
null
|
||||
else
|
||||
ConfigureKotlinNotification.getNotificationString(project, excludeModules)
|
||||
val notificationState = DumbService.getInstance(project).runReadActionInSmartMode(Computable {
|
||||
ConfigureKotlinNotification.getNotificationState(project, excludeModules)
|
||||
})
|
||||
|
||||
if (notificationString != null) {
|
||||
if (notificationState != null) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
ConfigureKotlinNotificationManager.notify(project, ConfigureKotlinNotification(project, excludeModules, notificationString))
|
||||
ConfigureKotlinNotificationManager.notify(project, ConfigureKotlinNotification(project, excludeModules, notificationState))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,33 +181,43 @@ private fun KotlinProjectConfigurator.canConfigure(moduleSourceRootGroup: Module
|
||||
getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED &&
|
||||
(allConfigurators().toList() - this).none { it.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CONFIGURED }
|
||||
|
||||
/**
|
||||
* Returns a list of modules which contain sources in Kotlin and for which it's possible to run the given configurator.
|
||||
* Note that this method is expensive and should not be called more often than strictly necessary.
|
||||
*/
|
||||
fun getCanBeConfiguredModulesWithKotlinFiles(project: Project, configurator: KotlinProjectConfigurator): List<Module> {
|
||||
val modules = getConfigurableModulesWithKotlinFiles(project)
|
||||
return modules.filter { configurator.getStatus(it) == ConfigureKotlinStatus.CAN_BE_CONFIGURED }.map { it.baseModule }
|
||||
}
|
||||
|
||||
fun getConfigurationPossibilities(
|
||||
fun getConfigurationPossibilitiesForConfigureNotification(
|
||||
project: Project,
|
||||
excludeModules: Collection<Module> = emptyList()
|
||||
): Pair<Collection<Module>, Collection<KotlinProjectConfigurator>> {
|
||||
): Pair<Collection<ModuleSourceRootGroup>, Collection<KotlinProjectConfigurator>> {
|
||||
val modulesWithKotlinFiles = getConfigurableModulesWithKotlinFiles(project).exclude(excludeModules)
|
||||
val configurators = allConfigurators()
|
||||
|
||||
val runnableConfigurators = mutableSetOf<KotlinProjectConfigurator>()
|
||||
val configurableModules = mutableListOf<Module>()
|
||||
val configurableModules = mutableListOf<ModuleSourceRootGroup>()
|
||||
|
||||
// We need to return all modules for which at least one configurator is applicable, as well as all configurators which
|
||||
// are applicable for at least one module. At the same time we want to call getStatus() only once for each module/configurator pair.
|
||||
for (moduleSourceRootGroup in modulesWithKotlinFiles) {
|
||||
var moduleCanBeConfigured = false
|
||||
var moduleAlreadyConfigured = false
|
||||
for (configurator in configurators) {
|
||||
if (moduleCanBeConfigured && configurator in runnableConfigurators) continue
|
||||
if (configurator.getStatus(moduleSourceRootGroup) == ConfigureKotlinStatus.CAN_BE_CONFIGURED) {
|
||||
moduleCanBeConfigured = true
|
||||
runnableConfigurators.add(configurator)
|
||||
val status = configurator.getStatus(moduleSourceRootGroup)
|
||||
when (status) {
|
||||
ConfigureKotlinStatus.CAN_BE_CONFIGURED -> {
|
||||
moduleCanBeConfigured = true
|
||||
runnableConfigurators.add(configurator)
|
||||
}
|
||||
ConfigureKotlinStatus.CONFIGURED -> moduleAlreadyConfigured = true
|
||||
}
|
||||
}
|
||||
if (moduleCanBeConfigured) configurableModules.add(moduleSourceRootGroup.baseModule)
|
||||
if (moduleCanBeConfigured && !moduleAlreadyConfigured && !SuppressNotificationState.isKotlinNotConfiguredSuppressed(moduleSourceRootGroup))
|
||||
configurableModules.add(moduleSourceRootGroup)
|
||||
}
|
||||
|
||||
return configurableModules to runnableConfigurators
|
||||
|
||||
+28
-9
@@ -8,8 +8,11 @@ package org.jetbrains.kotlin.idea.configuration
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationsManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.IndexNotReadyException
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
@@ -17,9 +20,9 @@ import kotlin.reflect.KClass
|
||||
|
||||
object ConfigureKotlinNotificationManager: KotlinSingleNotificationManager<ConfigureKotlinNotification> {
|
||||
fun notify(project: Project, excludeModules: List<Module> = emptyList()) {
|
||||
val notificationString = ConfigureKotlinNotification.getNotificationString(project, excludeModules)
|
||||
if (notificationString != null) {
|
||||
notify(project, ConfigureKotlinNotification(project, excludeModules, notificationString))
|
||||
val notificationState = ConfigureKotlinNotification.getNotificationState(project, excludeModules)
|
||||
if (notificationState != null) {
|
||||
notify(project, ConfigureKotlinNotification(project, excludeModules, notificationState))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +50,7 @@ interface KotlinSingleNotificationManager<in T: Notification> {
|
||||
for (oldNotification in notifications) {
|
||||
if (oldNotification == notification) {
|
||||
isNotificationExists = true
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
oldNotification?.expire()
|
||||
}
|
||||
}
|
||||
@@ -59,20 +61,37 @@ interface KotlinSingleNotificationManager<in T: Notification> {
|
||||
private val checkInProgress = AtomicBoolean(false)
|
||||
|
||||
fun checkHideNonConfiguredNotifications(project: Project) {
|
||||
if (checkInProgress.get() || ConfigureKotlinNotificationManager.getVisibleNotifications(project).isEmpty()) return
|
||||
if (checkInProgress.get()) return
|
||||
val notification = ConfigureKotlinNotificationManager.getVisibleNotifications(project).firstOrNull() ?: return
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
if (!checkInProgress.compareAndSet(false, true)) return@executeOnPooledThread
|
||||
|
||||
DumbService.getInstance(project).waitForSmartMode()
|
||||
if (getConfigurableModulesWithKotlinFiles(project).none(::isNotConfiguredNotificationRequired)) {
|
||||
val moduleSourceRootMap = ModuleSourceRootMap(project)
|
||||
|
||||
val hideNotification = try {
|
||||
val moduleSourceRootGroups = notification.notificationState.notConfiguredModules
|
||||
.mapNotNull { ModuleManager.getInstance(project).findModuleByName(it) }
|
||||
.map { moduleSourceRootMap.getWholeModuleGroup(it) }
|
||||
moduleSourceRootGroups.none(::isNotConfiguredNotificationRequired)
|
||||
} catch (e: IndexNotReadyException) {
|
||||
checkInProgress.set(false)
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
checkHideNonConfiguredNotifications(project)
|
||||
}
|
||||
return@executeOnPooledThread
|
||||
}
|
||||
|
||||
if (hideNotification) {
|
||||
ApplicationManager.getApplication().invokeLater {
|
||||
ConfigureKotlinNotificationManager.expireOldNotifications(project)
|
||||
checkInProgress.set(false)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
checkInProgress.set(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val LOG = Logger.getInstance(ConfigureKotlinNotificationManager::class.java)
|
||||
|
||||
+18
-9
@@ -11,19 +11,25 @@ import com.intellij.notification.NotificationType
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.configuration.KotlinProjectConfigurator
|
||||
import org.jetbrains.kotlin.idea.configuration.getConfigurationPossibilities
|
||||
import org.jetbrains.kotlin.idea.configuration.getConfigurationPossibilitiesForConfigureNotification
|
||||
import org.jetbrains.kotlin.idea.configuration.getConfiguratorByName
|
||||
import org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComponent
|
||||
import javax.swing.event.HyperlinkEvent
|
||||
|
||||
data class ConfigureKotlinNotificationState(
|
||||
val notificationString: String,
|
||||
val notConfiguredModules: Collection<String>
|
||||
)
|
||||
|
||||
class ConfigureKotlinNotification(
|
||||
project: Project,
|
||||
excludeModules: List<Module>,
|
||||
notificationString: String
|
||||
val notificationState: ConfigureKotlinNotificationState
|
||||
) : Notification(
|
||||
KotlinConfigurationCheckerComponent.CONFIGURE_NOTIFICATION_GROUP_ID, "Configure Kotlin",
|
||||
notificationString,
|
||||
NotificationType.WARNING, NotificationListener { notification, event ->
|
||||
notificationState.notificationString,
|
||||
NotificationType.WARNING,
|
||||
NotificationListener { notification, event ->
|
||||
if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) {
|
||||
val configurator = getConfiguratorByName(event.description) ?: throw AssertionError("Missed action: " + event.description)
|
||||
notification.expire()
|
||||
@@ -46,18 +52,21 @@ class ConfigureKotlinNotification(
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getNotificationString(project: Project, excludeModules: Collection<Module>): String? {
|
||||
val (configurableModules, ableToRunConfigurators) = getConfigurationPossibilities(project, excludeModules)
|
||||
if (ableToRunConfigurators.isEmpty()) return null
|
||||
fun getNotificationState(project: Project, excludeModules: Collection<Module>): ConfigureKotlinNotificationState? {
|
||||
val (configurableModules, ableToRunConfigurators) = getConfigurationPossibilitiesForConfigureNotification(project, excludeModules)
|
||||
if (ableToRunConfigurators.isEmpty() || configurableModules.isEmpty()) return null
|
||||
|
||||
val isOnlyOneModule = configurableModules.size == 1
|
||||
|
||||
val modulesString = if (isOnlyOneModule) "'${configurableModules.first().name}' module" else "modules"
|
||||
val modulesString = if (isOnlyOneModule) "'${configurableModules.first().baseModule.name}' module" else "modules"
|
||||
val links = ableToRunConfigurators.joinToString(separator = "<br/>") { configurator ->
|
||||
getLink(configurator, isOnlyOneModule)
|
||||
}
|
||||
|
||||
return "Configure $modulesString in '${project.name}' project<br/> $links"
|
||||
return ConfigureKotlinNotificationState(
|
||||
"Configure $modulesString in '${project.name}' project<br/> $links",
|
||||
configurableModules.map { it.baseModule.name }
|
||||
)
|
||||
}
|
||||
|
||||
private fun getLink(configurator: KotlinProjectConfigurator, isOnlyOneModule: Boolean): String {
|
||||
|
||||
Reference in New Issue
Block a user