[java] rewrite UnloadedModulesCompilationCheckinHandler to run computations on a background thread (IDEA-308600)

GitOrigin-RevId: 8e63afc07ae258aa557bf0d7e49c6c259725c90a
This commit is contained in:
Nikolay Chashnikov
2023-06-30 11:10:38 +02:00
committed by intellij-monorepo-bot
parent 86d86261f0
commit 4167f4a603
2 changed files with 65 additions and 58 deletions

View File

@@ -1,33 +1,33 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.compiler.impl.vcs package com.intellij.compiler.impl.vcs
import com.intellij.CommonBundle import com.intellij.build.BuildContentManager
import com.intellij.compiler.CompilerWorkspaceConfiguration import com.intellij.compiler.CompilerWorkspaceConfiguration
import com.intellij.compiler.impl.ModuleCompileScope import com.intellij.compiler.impl.ModuleCompileScope
import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.readAction
import com.intellij.openapi.compiler.CompilerManager import com.intellij.openapi.compiler.CompilerManager
import com.intellij.openapi.compiler.JavaCompilerBundle import com.intellij.openapi.compiler.JavaCompilerBundle
import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectFileIndex import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.roots.impl.DirectoryIndex import com.intellij.openapi.roots.impl.DirectoryIndex
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vcs.CheckinProjectPanel import com.intellij.openapi.vcs.CheckinProjectPanel
import com.intellij.openapi.vcs.changes.CommitContext import com.intellij.openapi.vcs.changes.CommitContext
import com.intellij.openapi.vcs.changes.CommitExecutor
import com.intellij.openapi.vcs.changes.ui.BooleanCommitOption.Companion.create import com.intellij.openapi.vcs.changes.ui.BooleanCommitOption.Companion.create
import com.intellij.openapi.vcs.checkin.CheckinHandler import com.intellij.openapi.vcs.checkin.*
import com.intellij.openapi.vcs.checkin.CheckinHandlerFactory
import com.intellij.openapi.vcs.ui.RefreshableOnComponent import com.intellij.openapi.vcs.ui.RefreshableOnComponent
import com.intellij.openapi.wm.ToolWindowId
import com.intellij.openapi.wm.ToolWindowManager import com.intellij.openapi.wm.ToolWindowManager
import com.intellij.util.PairConsumer import com.intellij.util.io.await
import com.intellij.xml.util.XmlStringUtil import kotlinx.coroutines.Dispatchers
import java.util.concurrent.atomic.AtomicReference import kotlinx.coroutines.withContext
import java.util.concurrent.CompletableFuture
class UnloadedModulesCompilationCheckinHandler(private val project: Project, class UnloadedModulesCompilationCheckinHandler(private val project: Project,
private val checkinPanel: CheckinProjectPanel) : CheckinHandler() { private val checkinPanel: CheckinProjectPanel) : CheckinHandler(), CommitCheck {
override fun getBeforeCheckinConfigurationPanel(): RefreshableOnComponent? { override fun getBeforeCheckinConfigurationPanel(): RefreshableOnComponent? {
return if (ModuleManager.getInstance(project).unloadedModuleDescriptions.isNotEmpty()) return if (ModuleManager.getInstance(project).unloadedModuleDescriptions.isNotEmpty())
create(checkinPanel.getProject(), this, false, create(checkinPanel.getProject(), this, false,
@@ -40,61 +40,69 @@ class UnloadedModulesCompilationCheckinHandler(private val project: Project,
} }
} }
override fun beforeCheckin(executor: CommitExecutor?, additionalDataConsumer: PairConsumer<Any, Any>): ReturnResult { override suspend fun runCheck(commitInfo: CommitInfo): CommitProblem? = withContext(Dispatchers.Default) {
if (!settings.COMPILE_AFFECTED_UNLOADED_MODULES_BEFORE_COMMIT || val unloadedModulesCompileScope = readAction {
ModuleManager.getInstance(project).unloadedModuleDescriptions.isEmpty()) { if (ModuleManager.getInstance(project).unloadedModuleDescriptions.isEmpty()) {
return ReturnResult.COMMIT return@readAction null
}
val fileIndex = ProjectFileIndex.getInstance(project)
val compilerManager = CompilerManager.getInstance(project)
val affectedModules = checkinPanel.getVirtualFiles()
.filter { compilerManager.isCompilableFileType(it.fileType) }
.mapNotNullTo(LinkedHashSet()) { fileIndex.getModuleForFile(it) }
val affectedUnloadedModules = affectedModules.flatMapTo(LinkedHashSet()) {
DirectoryIndex.getInstance(project).getDependentUnloadedModules(it)
}
if (affectedUnloadedModules.isEmpty()) {
return@readAction null
}
ModuleCompileScope(project, affectedModules, affectedUnloadedModules, true, false)
} }
val fileIndex = ProjectFileIndex.getInstance(project) if (unloadedModulesCompileScope == null) {
val compilerManager = CompilerManager.getInstance(project) return@withContext null
val affectedModules = checkinPanel.getVirtualFiles()
.filter { compilerManager.isCompilableFileType(it.fileType) }
.mapNotNullTo(LinkedHashSet()) { fileIndex.getModuleForFile(it) }
val affectedUnloadedModules = affectedModules.flatMapTo(LinkedHashSet()) {
DirectoryIndex.getInstance(project).getDependentUnloadedModules(it)
} }
if (affectedUnloadedModules.isEmpty()) {
return ReturnResult.COMMIT val compiledSuccessfully = CompletableFuture<Boolean>()
} withContext(Dispatchers.EDT) {
val result = AtomicReference<BuildResult>() CompilerManager.getInstance(project).make(unloadedModulesCompileScope) { aborted, errors, _, _ ->
compilerManager.makeWithModalProgress(ModuleCompileScope(project, affectedModules, affectedUnloadedModules, true, false)) { aborted, errors, _, _ -> if (aborted) {
result.set(when { compiledSuccessfully.cancel(true)
aborted -> BuildResult.CANCELED }
errors > 0 -> BuildResult.FAILED else {
else -> BuildResult.SUCCESSFUL compiledSuccessfully.complete(errors == 0)
}) }
}
if (result.get() == BuildResult.SUCCESSFUL) {
return ReturnResult.COMMIT
}
val message = JavaCompilerBundle.message("dialog.message.compilation.of.unloaded.modules.failed")
val answer = Messages.showYesNoCancelDialog(project, XmlStringUtil.wrapInHtml(message), JavaCompilerBundle
.message("dialog.title.compilation.failed"),
JavaCompilerBundle.message("button.text.checkin.handler.commit"),
JavaCompilerBundle.message("button.text.checkin.handler.show.errors"),
CommonBundle.getCancelButtonText(), null)
return when (answer) {
Messages.CANCEL -> ReturnResult.CANCEL
Messages.YES -> ReturnResult.COMMIT
else -> {
ApplicationManager.getApplication().invokeLater({
val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.MESSAGES_WINDOW)
toolWindow?.activate(null, false)
}, ModalityState.nonModal())
ReturnResult.CLOSE_WINDOW
} }
} }
if (compiledSuccessfully.await()) {
return@withContext null
}
return@withContext CompilationFailedProblem()
} }
override fun getExecutionOrder(): CommitCheck.ExecutionOrder = CommitCheck.ExecutionOrder.LATE
override fun isEnabled(): Boolean = settings.COMPILE_AFFECTED_UNLOADED_MODULES_BEFORE_COMMIT
private val settings: CompilerWorkspaceConfiguration private val settings: CompilerWorkspaceConfiguration
get() = CompilerWorkspaceConfiguration.getInstance(project) get() = CompilerWorkspaceConfiguration.getInstance(project)
private enum class BuildResult { class CompilationFailedProblem : CommitProblemWithDetails {
SUCCESSFUL, override val text: String
FAILED, get() = JavaCompilerBundle.message("dialog.message.compilation.of.unloaded.modules.failed")
CANCELED override val showDetailsAction: String
} get() = JavaCompilerBundle.message("button.text.checkin.handler.show.errors")
override val showDetailsLink: String
get() = JavaCompilerBundle.message("link.label.checkin.handler.show.errors")
override fun showDetails(project: Project) {
ApplicationManager.getApplication().invokeLater({
val toolWindow = ToolWindowManager.getInstance(project).getToolWindow(BuildContentManager.TOOL_WINDOW_ID)
toolWindow?.activate(null, false)
}, ModalityState.nonModal())
}
}
class Factory : CheckinHandlerFactory() { class Factory : CheckinHandlerFactory() {
override fun createHandler(panel: CheckinProjectPanel, commitContext: CommitContext): CheckinHandler { override fun createHandler(panel: CheckinProjectPanel, commitContext: CommitContext): CheckinHandler {
return UnloadedModulesCompilationCheckinHandler(panel.getProject(), panel) return UnloadedModulesCompilationCheckinHandler(panel.getProject(), panel)

View File

@@ -97,11 +97,10 @@ mesage.text.deployment.descriptor.file.not.exist=Deployment descriptor file ''{0
message.text.deployment.description.invalid.file=Invalid file message.text.deployment.description.invalid.file=Invalid file
warning.text.file.has.been.changed=File has been changed during compilation, inspection validation skipped warning.text.file.has.been.changed=File has been changed during compilation, inspection validation skipped
dialog.message.compilation.of.unloaded.modules.failed=There are unloaded modules in the project which depend on changed files.<br>\ dialog.message.compilation.of.unloaded.modules.failed=There are unloaded modules in the project which depend on changed files.\n\
Compilation of these modules finished with errors. Compilation of these modules finished with errors.
dialog.title.compilation.failed=Compilation Failed
button.text.checkin.handler.commit=&Commit
button.text.checkin.handler.show.errors=&Show Errors button.text.checkin.handler.show.errors=&Show Errors
link.label.checkin.handler.show.errors=Show errors
checkbox.text.compile.affected.unloaded.modules=Compile affected &unloaded modules checkbox.text.compile.affected.unloaded.modules=Compile affected &unloaded modules
#artifacts #artifacts