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
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
@@ -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<KotlinProjectConfigurator>()
}
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(
@@ -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<Module> {
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))
@@ -20,10 +20,7 @@ import kotlin.reflect.KClass
object ConfigureKotlinNotificationManager : KotlinSingleNotificationManager<ConfigureKotlinNotification> {
fun notify(project: Project, excludeModules: List<Module> = 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<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.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 {
@@ -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.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() })
@@ -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<PsiElement, CallableDescriptor>())
if (declarationDescriptor is LocalVariableDescriptor) return@underModalProgress (declarationDescriptor to emptyMap<PsiElement, CallableDescriptor>())
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
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<PsiElement, CallableDescriptor>()
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)
@@ -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 <T> underModalProgress(
project: Project,
@Nls progressTitle: String,
computable: Computable<T>
computable: () -> T
): T = com.intellij.openapi.actionSystem.ex.ActionUtil.underModalProgress(project, progressTitle, computable)
@JvmStatic
@@ -25,12 +25,12 @@ object ProgressIndicatorUtils {
fun <T> underModalProgress(
project: Project,
@Nls progressTitle: String,
computable: Computable<T>
computable: () -> T
): T {
val dumbService = DumbService.getInstance(project)
val useAlternativeResolve = dumbService.isAlternativeResolveEnabled
val inReadAction =
ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable.compute() } }
ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable() } }
val process =
if (useAlternativeResolve) ThrowableComputable { dumbService.computeWithAlternativeResolveEnabled(inReadAction) } else inReadAction
return ProgressManager.getInstance().runProcessWithProgressSynchronously(process, progressTitle, true, project)
@@ -25,12 +25,12 @@ object ProgressIndicatorUtils {
fun <T> underModalProgress(
project: Project,
@Nls progressTitle: String,
computable: Computable<T>
computable: () -> T
): T {
val dumbService = DumbService.getInstance(project)
val useAlternativeResolve = dumbService.isAlternativeResolveEnabled
val inReadAction =
ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable.compute() } }
ThrowableComputable<T, RuntimeException> { runReadAction { return@runReadAction computable() } }
val prioritizedRunnable =
ThrowableComputable<T, RuntimeException> { ProgressManager.getInstance().computePrioritized(inReadAction) }
val process =