Fix freeze on configure kotlin for the project

#KT-30541 Fixed
#KT-36289 Fixed
This commit is contained in:
Vladimir Dolzhenko
2020-03-30 07:17:52 +00:00
parent 2c69d82916
commit f011d2794a
12 changed files with 116 additions and 41 deletions
@@ -73,6 +73,8 @@ configure=Configure
kotlin.not.configured=Kotlin not configured kotlin.not.configured=Kotlin not configured
there.aren.t.configurators.available=There aren't configurators available there.aren.t.configurators.available=There aren't configurators available
all.modules.with.kotlin.files.are.configured=All modules with Kotlin files are configured 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} 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} updated.javascript.libraries.in.module.0=Updated JavaScript libraries in module {0}
added.0.to.library.configuration=Added {0} to library configuration added.0.to.library.configuration=Added {0} to library configuration
@@ -12,6 +12,7 @@ import com.intellij.openapi.ui.Messages
import com.intellij.util.PlatformUtils import com.intellij.util.PlatformUtils
import org.jetbrains.kotlin.idea.KotlinJvmBundle import org.jetbrains.kotlin.idea.KotlinJvmBundle
import org.jetbrains.kotlin.idea.configuration.* 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.idea.util.projectStructure.allModules
import org.jetbrains.kotlin.platform.js.isJs import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.platform.jvm.isJvm
@@ -23,14 +24,20 @@ 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 = 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<KotlinProjectConfigurator>()
}
val configurators = getApplicableConfigurators(project)
modules to configurators
}
if (modules.all(::isModuleConfigured)) { if (modules.all(::isModuleConfigured)) {
Messages.showInfoMessage(KotlinJvmBundle.message("all.modules.with.kotlin.files.are.configured"), e.presentation.text!!) Messages.showInfoMessage(KotlinJvmBundle.message("all.modules.with.kotlin.files.are.configured"), e.presentation.text!!)
return return
} }
val configurators = getApplicableConfigurators(project)
when { when {
configurators.size == 1 -> configurators.first().configure(project, emptyList()) configurators.size == 1 -> configurators.first().configure(project, emptyList())
configurators.isEmpty() -> Messages.showErrorDialog( configurators.isEmpty() -> Messages.showErrorDialog(
@@ -5,7 +5,9 @@
package org.jetbrains.kotlin.idea.configuration package org.jetbrains.kotlin.idea.configuration
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.extensions.Extensions
import com.intellij.openapi.module.Module import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project 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.JSLibraryKind
import org.jetbrains.kotlin.idea.framework.effectiveKind import org.jetbrains.kotlin.idea.framework.effectiveKind
import org.jetbrains.kotlin.idea.quickfix.KotlinAddRequiredModuleFix 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.application.runReadAction
import org.jetbrains.kotlin.idea.util.findFirstPsiJavaModule import org.jetbrains.kotlin.idea.util.findFirstPsiJavaModule
import org.jetbrains.kotlin.idea.util.isDev 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.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
import org.jetbrains.kotlin.utils.ifEmpty 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) 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" 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. * 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. * 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<Module> { fun getModulesWithKotlinFiles(project: Project): Collection<Module> {
if (!isUnitTestMode() && ApplicationManager.getApplication().isDispatchThread) {
LOG.error("getModulesWithKotlinFiles could be a heavy operation and should not be call on AWT thread")
}
if (!runReadAction { if (!runReadAction {
!project.isDisposed && !project.isDisposed &&
FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project)) FileTypeIndex.containsFileOfType(KotlinFileType.INSTANCE, GlobalSearchScope.projectScope(project))
@@ -20,10 +20,7 @@ import kotlin.reflect.KClass
object ConfigureKotlinNotificationManager : KotlinSingleNotificationManager<ConfigureKotlinNotification> { object ConfigureKotlinNotificationManager : KotlinSingleNotificationManager<ConfigureKotlinNotification> {
fun notify(project: Project, excludeModules: List<Module> = emptyList()) { fun notify(project: Project, excludeModules: List<Module> = emptyList()) {
val notificationState = ConfigureKotlinNotification.getNotificationState(project, excludeModules) notify(this, project, excludeModules)
if (notificationState != null) {
notify(project, ConfigureKotlinNotification(project, excludeModules, notificationState))
}
} }
fun getVisibleNotifications(project: Project): Array<out ConfigureKotlinNotification> { fun getVisibleNotifications(project: Project): Array<out ConfigureKotlinNotification> {
@@ -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.CreateLibraryDialogWithModules
import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils
import org.jetbrains.kotlin.idea.quickfix.askUpdateRuntime 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.application.runWriteAction
import org.jetbrains.kotlin.idea.util.projectStructure.sdk import org.jetbrains.kotlin.idea.util.projectStructure.sdk
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
@@ -68,10 +70,13 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro
val defaultPathToJar = getDefaultPathToJarFile(project) val defaultPathToJar = getDefaultPathToJarFile(project)
val showPathToJarPanel = needToChooseJarPath(project) val showPathToJarPanel = needToChooseJarPath(project)
var nonConfiguredModules = if (!ApplicationManager.getApplication().isUnitTestMode) var nonConfiguredModules = if (!isUnitTestMode()) {
getCanBeConfiguredModules(project, this) underModalProgress(project, KotlinJvmBundle.message("lookup.modules.configurations.progress.text")) {
else getCanBeConfiguredModules(project, this)
}
} else {
listOf(*ModuleManager.getInstance(project).modules) listOf(*ModuleManager.getInstance(project).modules)
}
nonConfiguredModules -= excludeModules nonConfiguredModules -= excludeModules
var modulesToConfigure = nonConfiguredModules var modulesToConfigure = nonConfiguredModules
@@ -85,7 +90,7 @@ abstract class KotlinWithLibraryConfigurator protected constructor() : KotlinPro
excludeModules excludeModules
) )
if (!ApplicationManager.getApplication().isUnitTestMode) { if (!isUnitTestMode()) {
dialog.show() dialog.show()
if (!dialog.isOK) return if (!dialog.isOK) return
} else { } else {
@@ -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<Module>) {
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())
}
@@ -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<Module>) {
nonBlocking(Callable {
ConfigureKotlinNotification.getNotificationState(project, excludeModules)
})
.expireWith(project)
.finishOnUiThread(ModalityState.any()) { notificationState ->
notificationState?.let {
manager.notify(project, ConfigureKotlinNotification(project, excludeModules, it))
}
}
.submit(AppExecutorUtil.getAppExecutorService())
}
@@ -22,7 +22,6 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.MessageType import com.intellij.openapi.ui.MessageType
import com.intellij.openapi.ui.popup.Balloon import com.intellij.openapi.ui.popup.Balloon
import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.util.Computable
import com.intellij.refactoring.BaseRefactoringProcessor import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.ui.awt.RelativePoint import com.intellij.ui.awt.RelativePoint
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
@@ -61,9 +60,9 @@ class ExtractionEngine(
val project = extractionData.project val project = extractionData.project
val adjustExtractionData = helper.adjustExtractionData(extractionData) val adjustExtractionData = helper.adjustExtractionData(extractionData)
val analysisResult = ProgressIndicatorUtils.underModalProgress(project, "Analyze extraction data...", Computable { val analysisResult = ProgressIndicatorUtils.underModalProgress(project, "Analyze extraction data...") {
adjustExtractionData.performAnalysis() adjustExtractionData.performAnalysis()
}) }
if (ApplicationManager.getApplication()!!.isUnitTestMode && analysisResult.status != AnalysisResult.Status.SUCCESS) { if (ApplicationManager.getApplication()!!.isUnitTestMode && analysisResult.status != AnalysisResult.Status.SUCCESS) {
throw BaseRefactoringProcessor.ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() }) throw BaseRefactoringProcessor.ConflictsInTestsException(analysisResult.messages.map { it.renderMessage() })
@@ -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.refactoring.rename.canonicalRender
import org.jetbrains.kotlin.idea.roots.isOutsideKotlinAwareSourceRoot import org.jetbrains.kotlin.idea.roots.isOutsideKotlinAwareSourceRoot
import org.jetbrains.kotlin.idea.util.* 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.idea.util.string.collapseSpaces
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.OVERRIDE_KEYWORD import org.jetbrains.kotlin.lexer.KtTokens.OVERRIDE_KEYWORD
@@ -859,31 +860,31 @@ fun checkSuperMethods(
val project = declaration.project val project = declaration.project
val (declarationDescriptor, overriddenElementsToDescriptor) = val (declarationDescriptor, overriddenElementsToDescriptor) =
ProgressIndicatorUtils.underModalProgress( underModalProgress(
project, project,
KotlinBundle.message("find.usages.progress.text.declaration.superMethods"), KotlinBundle.message("find.usages.progress.text.declaration.superMethods"))
Computable { {
val declarationDescriptor = declaration.unsafeResolveToDescriptor() as CallableDescriptor val declarationDescriptor = declaration.unsafeResolveToDescriptor() as CallableDescriptor
if (declarationDescriptor is LocalVariableDescriptor) return@Computable (declarationDescriptor to emptyMap<PsiElement, CallableDescriptor>()) if (declarationDescriptor is LocalVariableDescriptor) return@underModalProgress (declarationDescriptor to emptyMap<PsiElement, CallableDescriptor>())
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>() val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors( for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(
declarationDescriptor declarationDescriptor
)) { )) {
val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration( val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(
project, project,
overriddenDescriptor overriddenDescriptor
) ?: continue ) ?: continue
if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod || overriddenDeclaration is KtParameter) { if (overriddenDeclaration is KtNamedFunction || overriddenDeclaration is KtProperty || overriddenDeclaration is PsiMethod || overriddenDeclaration is KtParameter) {
overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor overriddenElementsToDescriptor[overriddenDeclaration] = overriddenDescriptor
}
} }
if (ignore != null) { }
overriddenElementsToDescriptor.keys.removeAll(ignore) if (ignore != null) {
} overriddenElementsToDescriptor.keys.removeAll(ignore)
(declarationDescriptor to overriddenElementsToDescriptor) }
}) (declarationDescriptor to overriddenElementsToDescriptor)
}
if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration) if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration)
@@ -9,7 +9,6 @@ import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Computable
import com.intellij.util.ExceptionUtil import com.intellij.util.ExceptionUtil
import org.jetbrains.annotations.Nls import org.jetbrains.annotations.Nls
import java.util.concurrent.CancellationException import java.util.concurrent.CancellationException
@@ -25,7 +24,7 @@ object ProgressIndicatorUtils {
fun <T> underModalProgress( fun <T> underModalProgress(
project: Project, project: Project,
@Nls progressTitle: String, @Nls progressTitle: String,
computable: Computable<T> computable: () -> T
): T = com.intellij.openapi.actionSystem.ex.ActionUtil.underModalProgress(project, progressTitle, computable) ): T = com.intellij.openapi.actionSystem.ex.ActionUtil.underModalProgress(project, progressTitle, computable)
@JvmStatic @JvmStatic
@@ -25,12 +25,12 @@ object ProgressIndicatorUtils {
fun <T> underModalProgress( fun <T> underModalProgress(
project: Project, project: Project,
@Nls progressTitle: String, @Nls progressTitle: String,
computable: Computable<T> computable: () -> T
): T { ): T {
val dumbService = DumbService.getInstance(project) val dumbService = DumbService.getInstance(project)
val useAlternativeResolve = dumbService.isAlternativeResolveEnabled val useAlternativeResolve = dumbService.isAlternativeResolveEnabled
val inReadAction = val inReadAction =
ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable.compute() } } ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable() } }
val process = val process =
if (useAlternativeResolve) ThrowableComputable { dumbService.computeWithAlternativeResolveEnabled(inReadAction) } else inReadAction if (useAlternativeResolve) ThrowableComputable { dumbService.computeWithAlternativeResolveEnabled(inReadAction) } else inReadAction
return ProgressManager.getInstance().runProcessWithProgressSynchronously(process, progressTitle, true, project) return ProgressManager.getInstance().runProcessWithProgressSynchronously(process, progressTitle, true, project)
@@ -25,12 +25,12 @@ object ProgressIndicatorUtils {
fun <T> underModalProgress( fun <T> underModalProgress(
project: Project, project: Project,
@Nls progressTitle: String, @Nls progressTitle: String,
computable: Computable<T> computable: () -> T
): T { ): T {
val dumbService = DumbService.getInstance(project) val dumbService = DumbService.getInstance(project)
val useAlternativeResolve = dumbService.isAlternativeResolveEnabled val useAlternativeResolve = dumbService.isAlternativeResolveEnabled
val inReadAction = val inReadAction =
ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable.compute() } } ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable() } }
val prioritizedRunnable = val prioritizedRunnable =
ThrowableComputable<T, RuntimeException> { ProgressManager.getInstance().computePrioritized(inReadAction) } ThrowableComputable<T, RuntimeException> { ProgressManager.getInstance().computePrioritized(inReadAction) }
val process = val process =