User: rednesto Date: 07 Nov 23 11:10 Revision: bd54763c7c2fd5257e7e618337dacdf4ec86b8f4 Summary: Update facets in a background task Fixes long UI freezes on huge projects with many modules/libraries Had to unbundle two coroutines libraries because they were conflicting with the platform's bundled library (likely because of the different classloaders) TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=8827&personal=false Index: build.gradle.kts =================================================================== --- build.gradle.kts (revision c0544a7aeec0b753bad39e6ed5ff0022dae85a46) +++ build.gradle.kts (revision bd54763c7c2fd5257e7e618337dacdf4ec86b8f4) @@ -95,7 +95,9 @@ // Kotlin implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) - implementation(libs.bundles.coroutines) + implementation(libs.bundles.coroutines) { + exclude(module = "kotlinx-coroutines-core-jvm") + } implementation(files(gradleToolingExtensionJar)) Index: gradle/libs.versions.toml =================================================================== --- gradle/libs.versions.toml (revision c0544a7aeec0b753bad39e6ed5ff0022dae85a46) +++ gradle/libs.versions.toml (revision bd54763c7c2fd5257e7e618337dacdf4ec86b8f4) @@ -40,6 +40,6 @@ junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junit-platform" } [bundles] -coroutines = ["coroutines-core", "coroutines-jdk8", "coroutines-swing"] +coroutines = ["coroutines-swing"] asm = ["asm", "asm-tree", "asm-analysis"] fuel = ["fuel", "fuel-coroutines"] Index: src/main/kotlin/facet/MinecraftFacet.kt =================================================================== --- src/main/kotlin/facet/MinecraftFacet.kt (revision c0544a7aeec0b753bad39e6ed5ff0022dae85a46) +++ src/main/kotlin/facet/MinecraftFacet.kt (revision bd54763c7c2fd5257e7e618337dacdf4ec86b8f4) @@ -35,6 +35,7 @@ import com.intellij.facet.FacetTypeRegistry import com.intellij.ide.projectView.ProjectView import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.application.runWriteActionAndWait import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleGrouper import com.intellij.openapi.module.ModuleManager @@ -122,18 +123,20 @@ roots.clear() val rootManager = ModuleRootManager.getInstance(module) + runWriteActionAndWait { - rootManager.contentEntries.asSequence() - .flatMap { entry -> entry.sourceFolders.asSequence() } - .filterNotNull { it.file } - .forEach { - when (it.rootType) { - JavaSourceRootType.SOURCE -> roots.put(SourceType.SOURCE, it.file) - JavaSourceRootType.TEST_SOURCE -> roots.put(SourceType.TEST_SOURCE, it.file) - JavaResourceRootType.RESOURCE -> roots.put(SourceType.RESOURCE, it.file) - JavaResourceRootType.TEST_RESOURCE -> roots.put(SourceType.TEST_RESOURCE, it.file) - } - } - } + rootManager.contentEntries.asSequence() + .flatMap { entry -> entry.sourceFolders.asSequence() } + .filterNotNull { it.file } + .forEach { + when (it.rootType) { + JavaSourceRootType.SOURCE -> roots.put(SourceType.SOURCE, it.file) + JavaSourceRootType.TEST_SOURCE -> roots.put(SourceType.TEST_SOURCE, it.file) + JavaResourceRootType.RESOURCE -> roots.put(SourceType.RESOURCE, it.file) + JavaResourceRootType.TEST_RESOURCE -> roots.put(SourceType.TEST_RESOURCE, it.file) + } + } + } + } private fun register(type: AbstractModuleType<*>): AbstractModule { type.performCreationSettingSetup(module.project) Index: src/main/kotlin/facet/MinecraftFacetDetector.kt =================================================================== --- src/main/kotlin/facet/MinecraftFacetDetector.kt (revision c0544a7aeec0b753bad39e6ed5ff0022dae85a46) +++ src/main/kotlin/facet/MinecraftFacetDetector.kt (revision bd54763c7c2fd5257e7e618337dacdf4ec86b8f4) @@ -31,6 +31,10 @@ import com.intellij.facet.FacetManager import com.intellij.facet.impl.ui.libraries.LibrariesValidatorContextImpl import com.intellij.framework.library.LibraryVersionProperties +import com.intellij.openapi.application.EDT +import com.intellij.openapi.application.runWriteActionAndWait +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project @@ -41,11 +45,17 @@ import com.intellij.openapi.roots.libraries.LibraryKind import com.intellij.openapi.roots.libraries.LibraryProperties import com.intellij.openapi.roots.ui.configuration.libraries.LibraryPresentationManager -import com.intellij.openapi.startup.StartupActivity +import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.util.Key +import com.intellij.platform.ide.progress.withBackgroundProgress +import com.intellij.platform.util.progress.forEachWithProgress +import com.intellij.util.application +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.jetbrains.plugins.gradle.util.GradleUtil -class MinecraftFacetDetector : StartupActivity { +class MinecraftFacetDetector : ProjectActivity { companion object { private val libraryVersionsKey = Key>("mcdev.libraryVersions") @@ -54,10 +64,15 @@ } } - override fun runActivity(project: Project) { + override suspend fun execute(project: Project) { + withBackgroundProgress(project, "Detecting Minecraft Frameworks", cancellable = false) { - MinecraftModuleRootListener.doCheck(project) - } + MinecraftModuleRootListener.doCheck(project) + } + } + @Service(Service.Level.PROJECT) + private class FacetDetectorScopeProvider(val scope: CoroutineScope) + private object MinecraftModuleRootListener : ModuleRootListener { override fun rootsChanged(event: ModuleRootEvent) { if (event.isCausedByFileTypesChange) { @@ -65,28 +80,40 @@ } val project = event.source as? Project ?: return + project.service().scope.launch(Dispatchers.EDT) { + withBackgroundProgress(project, "Detecting Minecraft Frameworks", cancellable = false) { - doCheck(project) - } + doCheck(project) + } + } + } - fun doCheck(project: Project) { + suspend fun doCheck(project: Project) { val moduleManager = ModuleManager.getInstance(project) var needsReimport = false - for (module in moduleManager.modules) { + moduleManager.modules.asList().forEachWithProgress(false) { module -> val facetManager = FacetManager.getInstance(module) val minecraftFacet = facetManager.getFacetByType(MinecraftFacet.ID) + val action = { - if (minecraftFacet == null) { - checkNoFacet(module) - } else { - checkExistingFacet(module, minecraftFacet) - if (ProjectReimporter.needsReimport(minecraftFacet)) { - needsReimport = true - } - } - } + if (minecraftFacet == null) { + checkNoFacet(module) + } else { + checkExistingFacet(module, minecraftFacet) + if (ProjectReimporter.needsReimport(minecraftFacet)) { + needsReimport = true + } + } + } + if (application.isUnitTestMode) { + action() + } else { + runWriteActionAndWait(action) + } + } + if (needsReimport) { ProjectReimporter.reimport(project) }