From f011d2794a574974e1e1cbb9522bbd3c90f1ac1a Mon Sep 17 00:00:00 2001 From: Vladimir Dolzhenko Date: Mon, 30 Mar 2020 07:17:52 +0000 Subject: [PATCH] Fix freeze on configure kotlin for the project #KT-30541 Fixed #KT-36289 Fixed --- .../messages/KotlinJvmBundle.properties | 2 + .../actions/ConfigureKotlinInProjectAction.kt | 13 ++++-- .../ConfigureKotlinInProjectUtils.kt | 10 +++++ .../ConfigureKotlinNotificationManager.kt | 5 +-- .../KotlinWithLibraryConfigurator.kt | 13 ++++-- .../kotlin/idea/configuration/compat.kt | 28 ++++++++++++ .../kotlin/idea/configuration/compat.kt.192 | 27 ++++++++++++ .../extractionEngine/ExtractionEngine.kt | 5 +-- .../idea/refactoring/kotlinRefactoringUtil.kt | 43 ++++++++++--------- .../idea/util/ProgressIndicatorUtils.kt | 3 +- .../idea/util/ProgressIndicatorUtils.kt.191 | 4 +- .../idea/util/ProgressIndicatorUtils.kt.192 | 4 +- 12 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt create mode 100644 idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt.192 diff --git a/idea/idea-jvm/resources/messages/KotlinJvmBundle.properties b/idea/idea-jvm/resources/messages/KotlinJvmBundle.properties index 57fd3fe2380..88aad4bdaf0 100644 --- a/idea/idea-jvm/resources/messages/KotlinJvmBundle.properties +++ b/idea/idea-jvm/resources/messages/KotlinJvmBundle.properties @@ -73,6 +73,8 @@ configure=Configure kotlin.not.configured=Kotlin not configured there.aren.t.configurators.available=There aren't configurators available all.modules.with.kotlin.files.are.configured=All modules with Kotlin files are configured +lookup.project.configurators.progress.text=Looking up for project configurators ... +lookup.modules.configurations.progress.text=Looking up for modules configurations ... added.0.requirement.to.module.info.in.1=Added {0} requirement to module-info in {1} updated.javascript.libraries.in.module.0=Updated JavaScript libraries in module {0} added.0.to.library.configuration=Added {0} to library configuration diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt index 6d0bba9c3af..c1f9b913aea 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/actions/ConfigureKotlinInProjectAction.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.ui.Messages import com.intellij.util.PlatformUtils import org.jetbrains.kotlin.idea.KotlinJvmBundle import org.jetbrains.kotlin.idea.configuration.* +import org.jetbrains.kotlin.idea.util.ProgressIndicatorUtils.underModalProgress import org.jetbrains.kotlin.idea.util.projectStructure.allModules import org.jetbrains.kotlin.platform.js.isJs import org.jetbrains.kotlin.platform.jvm.isJvm @@ -23,14 +24,20 @@ abstract class ConfigureKotlinInProjectAction : AnAction() { override fun actionPerformed(e: AnActionEvent) { val project = e.project ?: return - val modules = getConfigurableModules(project) + val (modules, configurators) = underModalProgress(project, KotlinJvmBundle.message("lookup.project.configurators.progress.text")) { + val modules = getConfigurableModules(project) + if (modules.all(::isModuleConfigured)) { + return@underModalProgress modules to emptyList() + } + val configurators = getApplicableConfigurators(project) + modules to configurators + } + if (modules.all(::isModuleConfigured)) { Messages.showInfoMessage(KotlinJvmBundle.message("all.modules.with.kotlin.files.are.configured"), e.presentation.text!!) return } - val configurators = getApplicableConfigurators(project) - when { configurators.size == 1 -> configurators.first().configure(project, emptyList()) configurators.isEmpty() -> Messages.showErrorDialog( diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt index 0b712b87ca6..618d9039036 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinInProjectUtils.kt @@ -5,7 +5,9 @@ package org.jetbrains.kotlin.idea.configuration +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project @@ -28,6 +30,7 @@ import org.jetbrains.kotlin.idea.core.util.getKotlinJvmRuntimeMarkerClass import org.jetbrains.kotlin.idea.framework.JSLibraryKind import org.jetbrains.kotlin.idea.framework.effectiveKind import org.jetbrains.kotlin.idea.quickfix.KotlinAddRequiredModuleFix +import org.jetbrains.kotlin.idea.util.application.isUnitTestMode import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.findFirstPsiJavaModule import org.jetbrains.kotlin.idea.util.isDev @@ -42,6 +45,8 @@ import org.jetbrains.kotlin.idea.vfilefinder.IDEVirtualFileFinder import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME import org.jetbrains.kotlin.utils.ifEmpty +private val LOG = Logger.getInstance("#org.jetbrains.kotlin.idea.configuration.ConfigureKotlinInProjectUtils") + data class RepositoryDescription(val id: String, val name: String, val url: String, val bintrayUrl: String?, val isSnapshot: Boolean) const val LAST_SNAPSHOT_VERSION = "1.4-SNAPSHOT" @@ -122,8 +127,13 @@ 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. + * + * DO NOT CALL THIS ON AWT THREAD */ fun getModulesWithKotlinFiles(project: Project): Collection { + if (!isUnitTestMode() && ApplicationManager.getApplication().isDispatchThread) { + LOG.error("getModulesWithKotlinFiles could be a heavy operation and should not be call on AWT thread") + } if (!runReadAction { !project.isDisposed && FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project)) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt index 0c19bce1fef..f3e6c527771 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/ConfigureKotlinNotificationManager.kt @@ -20,10 +20,7 @@ import kotlin.reflect.KClass object ConfigureKotlinNotificationManager : KotlinSingleNotificationManager { fun notify(project: Project, excludeModules: List = emptyList()) { - val notificationState = ConfigureKotlinNotification.getNotificationState(project, excludeModules) - if (notificationState != null) { - notify(project, ConfigureKotlinNotification(project, excludeModules, notificationState)) - } + notify(this, project, excludeModules) } fun getVisibleNotifications(project: Project): Array { diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt index 70cbf481f60..956689f94af 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/KotlinWithLibraryConfigurator.kt @@ -30,6 +30,8 @@ import org.jetbrains.kotlin.idea.facet.toApiVersion import org.jetbrains.kotlin.idea.framework.ui.CreateLibraryDialogWithModules import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils import org.jetbrains.kotlin.idea.quickfix.askUpdateRuntime +import org.jetbrains.kotlin.idea.util.ProgressIndicatorUtils.underModalProgress +import org.jetbrains.kotlin.idea.util.application.isUnitTestMode import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.idea.util.projectStructure.sdk import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor @@ -68,10 +70,13 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro val defaultPathToJar = getDefaultPathToJarFile(project) val showPathToJarPanel = needToChooseJarPath(project) - var nonConfiguredModules = if (!ApplicationManager.getApplication().isUnitTestMode) - getCanBeConfiguredModules(project, this) - else + var nonConfiguredModules = if (!isUnitTestMode()) { + underModalProgress(project, KotlinJvmBundle.message("lookup.modules.configurations.progress.text")) { + getCanBeConfiguredModules(project, this) + } + } else { listOf(*ModuleManager.getInstance(project).modules) + } nonConfiguredModules -= excludeModules var modulesToConfigure = nonConfiguredModules @@ -85,7 +90,7 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro excludeModules ) - if (!ApplicationManager.getApplication().isUnitTestMode) { + if (!isUnitTestMode()) { dialog.show() if (!dialog.isOK) return } else { diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt new file mode 100644 index 00000000000..e2d8fc3f846 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.ModalityState +import com.intellij.openapi.application.ReadAction.nonBlocking +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.util.concurrency.AppExecutorUtil +import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification +import java.util.concurrent.Callable + +fun notify(manager: ConfigureKotlinNotificationManager, project: Project, excludeModules: List) { + nonBlocking(Callable { + ConfigureKotlinNotification.getNotificationState(project, excludeModules) + }) + .expireWith(project) + .coalesceBy(manager) + .finishOnUiThread(ModalityState.any()) { notificationState -> + notificationState?.let { + manager.notify(project, ConfigureKotlinNotification(project, excludeModules, it)) + } + } + .submit(AppExecutorUtil.getAppExecutorService()) +} \ No newline at end of file diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt.192 b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt.192 new file mode 100644 index 00000000000..ad180f89755 --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/configuration/compat.kt.192 @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.ModalityState +import com.intellij.openapi.application.ReadAction.nonBlocking +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.util.concurrency.AppExecutorUtil +import org.jetbrains.kotlin.idea.configuration.ui.notifications.ConfigureKotlinNotification +import java.util.concurrent.Callable + +fun notify(manager: ConfigureKotlinNotificationManager, project: Project, excludeModules: List) { + nonBlocking(Callable { + ConfigureKotlinNotification.getNotificationState(project, excludeModules) + }) + .expireWith(project) + .finishOnUiThread(ModalityState.any()) { notificationState -> + notificationState?.let { + manager.notify(project, ConfigureKotlinNotification(project, excludeModules, it)) + } + } + .submit(AppExecutorUtil.getAppExecutorService()) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionEngine.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionEngine.kt index bce78d01268..d58847c6ded 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionEngine.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/ExtractionEngine.kt @@ -22,7 +22,6 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.ui.MessageType import com.intellij.openapi.ui.popup.Balloon import com.intellij.openapi.ui.popup.JBPopupFactory -import com.intellij.openapi.util.Computable import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.ui.awt.RelativePoint import org.jetbrains.kotlin.idea.KotlinBundle @@ -61,9 +60,9 @@ class ExtractionEngine( val project = extractionData.project val adjustExtractionData = helper.adjustExtractionData(extractionData) - val analysisResult = ProgressIndicatorUtils.underModalProgress(project, "Analyze extraction data...", Computable { + val analysisResult = ProgressIndicatorUtils.underModalProgress(project, "Analyze extraction data...") { adjustExtractionData.performAnalysis() - }) + } if (ApplicationManager.getApplication()!!.isUnitTestMode && analysisResult.status != AnalysisResult.Status.SUCCESS) { throw BaseRefactoringProcessor.ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() }) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt index ee62793dfc4..d3811e4ba6d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/kotlinRefactoringUtil.kt @@ -78,6 +78,7 @@ import org.jetbrains.kotlin.idea.refactoring.memberInfo.KtPsiClassWrapper import org.jetbrains.kotlin.idea.refactoring.rename.canonicalRender import org.jetbrains.kotlin.idea.roots.isOutsideKotlinAwareSourceRoot import org.jetbrains.kotlin.idea.util.* +import org.jetbrains.kotlin.idea.util.ProgressIndicatorUtils.underModalProgress import org.jetbrains.kotlin.idea.util.string.collapseSpaces import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens.OVERRIDE_KEYWORD @@ -859,31 +860,31 @@ fun checkSuperMethods( val project = declaration.project val (declarationDescriptor, overriddenElementsToDescriptor) = - ProgressIndicatorUtils.underModalProgress( + underModalProgress( project, - KotlinBundle.message("find.usages.progress.text.declaration.superMethods"), - Computable { - val declarationDescriptor = declaration.unsafeResolveToDescriptor() as CallableDescriptor + KotlinBundle.message("find.usages.progress.text.declaration.superMethods")) + { + val declarationDescriptor = declaration.unsafeResolveToDescriptor() as CallableDescriptor - if (declarationDescriptor is LocalVariableDescriptor) return@Computable (declarationDescriptor to emptyMap()) + if (declarationDescriptor is LocalVariableDescriptor) return@underModalProgress (declarationDescriptor to emptyMap()) - val overriddenElementsToDescriptor = HashMap() - for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors( - declarationDescriptor - )) { - val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration( - project, - overriddenDescriptor - ) ?: continue - if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod || overriddenDeclaration is KtParameter) { - overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor - } + val overriddenElementsToDescriptor = HashMap() + for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors( + declarationDescriptor + )) { + val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration( + project, + overriddenDescriptor + ) ?: continue + if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod || overriddenDeclaration is KtParameter) { + overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor } - if (ignore != null) { - overriddenElementsToDescriptor.keys.removeAll(ignore) - } - (declarationDescriptor to overriddenElementsToDescriptor) - }) + } + if (ignore != null) { + overriddenElementsToDescriptor.keys.removeAll(ignore) + } + (declarationDescriptor to overriddenElementsToDescriptor) + } if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration) diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt index 88ec6be652e..b0318c7a0ca 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt @@ -9,7 +9,6 @@ import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project -import com.intellij.openapi.util.Computable import com.intellij.util.ExceptionUtil import org.jetbrains.annotations.Nls import java.util.concurrent.CancellationException @@ -25,7 +24,7 @@ object ProgressIndicatorUtils { fun underModalProgress( project: Project, @Nls progressTitle: String, - computable: Computable + computable: () -> T ): T = com.intellij.openapi.actionSystem.ex.ActionUtil.underModalProgress(project, progressTitle, computable) @JvmStatic diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 index 992db1fc059..e35ee37dadc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 +++ b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.191 @@ -25,12 +25,12 @@ object ProgressIndicatorUtils { fun underModalProgress( project: Project, @Nls progressTitle: String, - computable: Computable + computable: () -> T ): T { val dumbService = DumbService.getInstance(project) val useAlternativeResolve = dumbService.isAlternativeResolveEnabled val inReadAction = - ThrowableComputable { runReadAction { return@runReadAction computable.compute() } } + ThrowableComputable { runReadAction { return@runReadAction computable() } } val process = if (useAlternativeResolve) ThrowableComputable { dumbService.computeWithAlternativeResolveEnabled(inReadAction) } else inReadAction return ProgressManager.getInstance().runProcessWithProgressSynchronously(process, progressTitle, true, project) diff --git a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 index 6018c1b387d..b2c485f9383 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 +++ b/idea/src/org/jetbrains/kotlin/idea/util/ProgressIndicatorUtils.kt.192 @@ -25,12 +25,12 @@ object ProgressIndicatorUtils { fun underModalProgress( project: Project, @Nls progressTitle: String, - computable: Computable + computable: () -> T ): T { val dumbService = DumbService.getInstance(project) val useAlternativeResolve = dumbService.isAlternativeResolveEnabled val inReadAction = - ThrowableComputable { runReadAction { return@runReadAction computable.compute() } } + ThrowableComputable { runReadAction { return@runReadAction computable() } } val prioritizedRunnable = ThrowableComputable { ProgressManager.getInstance().computePrioritized(inReadAction) } val process =