[vcs] IJPL-198982 Implement subtle dumb mode indication in Commit

GitOrigin-RevId: ecde9ef71784803d5473971fd59a54a6998924e8
This commit is contained in:
Stanislau Palyn
2025-09-24 13:45:47 +00:00
committed by intellij-monorepo-bot
parent e1f9a37e9b
commit df77d67210
6 changed files with 89 additions and 19 deletions
+3
View File
@@ -2129,6 +2129,9 @@ f:com.intellij.vcs.commit.CommitChecksResult$OnlyChecks
f:com.intellij.vcs.commit.CommitChecksResult$Passed
- com.intellij.vcs.commit.CommitChecksResult
- sf:INSTANCE:com.intellij.vcs.commit.CommitChecksResult$Passed
f:com.intellij.vcs.commit.CommitChecksResult$SmartModeRequired
- com.intellij.vcs.commit.CommitChecksResult
- sf:INSTANCE:com.intellij.vcs.commit.CommitChecksResult$SmartModeRequired
com.intellij.vcs.commit.CommitExecutorListener
- java.util.EventListener
- a:executorCalled(com.intellij.openapi.vcs.changes.CommitExecutor):V
@@ -25,6 +25,7 @@ sealed class CommitChecksResult {
class Failed(val toCloseWindow: Boolean = false) : CommitChecksResult()
object Cancelled : CommitChecksResult()
object ExecutionError : CommitChecksResult()
object SmartModeRequired : CommitChecksResult()
val shouldCommit: Boolean get() = this is Passed
val shouldCloseWindow: Boolean get() = this is Failed && toCloseWindow
@@ -16,7 +16,7 @@ vcs.revision.name.current=Current
error.no.changes.to.commit=Select files to commit
error.no.commit.message=Specify commit message
error.no.changes.no.commit.message=Select files to commit and specify commit message
label.commit.checks.not.available.during.indexing=Commit checks are not available during indexing
label.commit.checks.not.available.during.indexing=Some commit checks are not available during indexing
commit.checks.on.commit.progress.text=Committing\u2026
commit.checks.on.commit.progress.text.with.context=Committing: {0}
commit.checks.only.progress.text=Running commit checks\u2026
@@ -49,6 +49,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.jetbrains.annotations.ApiStatus
import java.awt.Dimension
import java.awt.Font
import java.awt.event.ComponentAdapter
@@ -97,6 +98,7 @@ open class CommitProgressPanel(project: Project) : CommitProgressUi, InclusionLi
private val dumbModeFlow: MutableStateFlow<Boolean> = MutableStateFlow(false)
override var isDumbMode: Boolean by dumbModeFlow::value
private var shouldWarnAboutDumbMode: Boolean = false
init {
scope.launch {
@@ -238,11 +240,26 @@ open class CommitProgressPanel(project: Project) : CommitProgressUi, InclusionLi
return failuresPanel.getFailures()
}
@ApiStatus.Internal
override fun showWarningAboutDumbMode() {
shouldWarnAboutDumbMode = true
update()
}
@ApiStatus.Internal
override fun hideWarningAboutDumbMode() {
shouldWarnAboutDumbMode = false
update()
}
override fun documentChanged(event: DocumentEvent) = clearError()
override fun inclusionChanged() = clearError()
protected fun update() {
if (!isDumbMode) {
shouldWarnAboutDumbMode = false
}
val error = buildErrorText()
when {
@@ -263,7 +280,7 @@ open class CommitProgressPanel(project: Project) : CommitProgressUi, InclusionLi
announceCommitErrorAlarm?.cancelAndRequest()
}
}
isDumbMode -> label.setWarning(message("label.commit.checks.not.available.during.indexing"))
isDumbMode && shouldWarnAboutDumbMode -> label.setWarning(message("label.commit.checks.not.available.during.indexing"))
else -> label.isVisible = false
}
revalidatePanel()
@@ -68,6 +68,7 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
if (oldValue == newValue) return@observable
updateDefaultCommitActionName()
}
private var smartChecksWereBlocked = false
private val checkinErrorNotifications = SingletonNotificationManager(VcsNotifier.importantNotification().displayId,
NotificationType.ERROR)
@@ -108,6 +109,8 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
override fun exitDumbMode() {
ui.commitProgressUi.isDumbMode = false
smartChecksWereBlocked = false
updateDefaultCommitActionName()
}
})
}
@@ -125,7 +128,7 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
private fun getCommitActionTextForNotification(
executor: CommitExecutor?,
isSkipCommitChecks: Boolean
isSkipCommitChecks: Boolean,
): @Nls(capitalization = Nls.Capitalization.Sentence) String {
val isAmend = amendCommitHandler.isAmendCommitMode
val actionText: @Nls String = getActionTextWithoutEllipsis(workflow.vcses, executor, isAmend, isSkipCommitChecks,
@@ -209,7 +212,8 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
private fun willSkipCommitChecks() = isCommitChecksResultUpToDate == RecentCommitChecks.EARLY_FAILED ||
isCommitChecksResultUpToDate == RecentCommitChecks.MODIFICATIONS_FAILED ||
isCommitChecksResultUpToDate == RecentCommitChecks.POST_FAILED
isCommitChecksResultUpToDate == RecentCommitChecks.POST_FAILED ||
smartChecksWereBlocked
private fun willSkipEarlyCommitChecks() = isCommitChecksResultUpToDate == RecentCommitChecks.EARLY_FAILED ||
isCommitChecksResultUpToDate == RecentCommitChecks.MODIFICATIONS_FAILED ||
@@ -222,6 +226,7 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
private fun willSkipPostCommitChecks() = isCommitChecksResultUpToDate == RecentCommitChecks.POST_FAILED
private fun willSkipSmartCommitChecks() = smartChecksWereBlocked
protected fun resetCommitChecksResult() {
isCommitChecksResultUpToDate = RecentCommitChecks.UNKNOWN
@@ -316,11 +321,12 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
val skipModificationCommitChecks = !isOnlyRunCommitChecks && willSkipModificationCommitChecks()
val skipLateCommitChecks = !isOnlyRunCommitChecks && willSkipLateCommitChecks()
val skipPostCommitChecks = !isOnlyRunCommitChecks && willSkipPostCommitChecks()
val allowSkippingSmartCommitChecks = willSkipSmartCommitChecks()
resetCommitChecksResult()
ui.commitProgressUi.runWithProgress(isOnlyRunCommitChecks) {
val failure = runNonModalBeforeCommitChecks(commitInfo, skipEarlyCommitChecks, skipModificationCommitChecks,
skipLateCommitChecks, skipPostCommitChecks)
skipLateCommitChecks, skipPostCommitChecks, allowSkippingSmartCommitChecks)
handleCommitProblem(failure, isOnlyRunCommitChecks)
}
}
@@ -334,6 +340,7 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
skipModificationCommitChecks: Boolean,
skipLateCommitChecks: Boolean,
skipPostCommitChecks: Boolean,
allowSkippingSmartCommitChecks: Boolean,
): NonModalCommitChecksFailure? = reportSequentialProgress { reporter ->
try {
val handlers = workflow.commitHandlers
@@ -341,12 +348,15 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
.filter { it.acceptExecutor(commitInfo.executor) }
.map { it.asCommitCheck(commitInfo) }
.filter { it.isEnabled() }
.groupBy { it.getExecutionOrder() }
val earlyChecks = commitChecks[CommitCheck.ExecutionOrder.EARLY].orEmpty()
val modificationChecks = commitChecks[CommitCheck.ExecutionOrder.MODIFICATION].orEmpty()
val lateChecks = commitChecks[CommitCheck.ExecutionOrder.LATE].orEmpty()
val postCommitChecks = commitChecks[CommitCheck.ExecutionOrder.POST_COMMIT].orEmpty()
val dumbModeFailure = handleDumbModeCompatibility(commitChecks)
val commitChecksByOrder = commitChecks.groupBy { it.getExecutionOrder() }
val earlyChecks = commitChecksByOrder[CommitCheck.ExecutionOrder.EARLY].orEmpty()
val modificationChecks = commitChecksByOrder[CommitCheck.ExecutionOrder.MODIFICATION].orEmpty()
val lateChecks = commitChecksByOrder[CommitCheck.ExecutionOrder.LATE].orEmpty()
val postCommitChecks = commitChecksByOrder[CommitCheck.ExecutionOrder.POST_COMMIT].orEmpty()
@Suppress("DEPRECATION") val metaHandlers = handlers.filterIsInstance<CheckinMetaHandler>()
if (!skipEarlyCommitChecks) {
@@ -355,6 +365,10 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
}?.let { return it }
}
if (!allowSkippingSmartCommitChecks && dumbModeFailure != null) {
return dumbModeFailure
}
if (!skipModificationCommitChecks) {
reporter.nextStep(PROGRESS_FRACTION_MODIFICATIONS) {
runModificationCommitChecks(commitInfo, modificationChecks, metaHandlers)
@@ -397,6 +411,22 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
}
}
private fun handleDumbModeCompatibility(commitChecks: List<CommitCheck>): NonModalCommitChecksFailure? {
val dumbService = DumbService.getInstance(project)
val hasBlockedSmartChecks = commitChecks.any { commitCheck ->
!dumbService.isUsableInCurrentContext(commitCheck)
}
if (hasBlockedSmartChecks) {
smartChecksWereBlocked = true
ui.commitProgressUi.showWarningAboutDumbMode()
return NonModalCommitChecksFailure.SMART_MODE_REQUIRED
}
smartChecksWereBlocked = false
ui.commitProgressUi.hideWarningAboutDumbMode()
return null
}
private suspend fun runEarlyCommitChecks(commitInfo: DynamicCommitInfo, commitChecks: List<CommitCheck>): NonModalCommitChecksFailure? {
val problems = commitChecks.mapWithProgress { commitCheck ->
AbstractCommitWorkflow.runCommitCheck(project, commitCheck, commitInfo)
@@ -407,10 +437,12 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
return NonModalCommitChecksFailure.EARLY_FAILED
}
private suspend fun runModificationCommitChecks(commitInfo: DynamicCommitInfo,
commitChecks: List<CommitCheck>,
@Suppress("DEPRECATION")
metaHandlers: List<CheckinMetaHandler>): NonModalCommitChecksFailure? {
private suspend fun runModificationCommitChecks(
commitInfo: DynamicCommitInfo,
commitChecks: List<CommitCheck>,
@Suppress("DEPRECATION")
metaHandlers: List<CheckinMetaHandler>,
): NonModalCommitChecksFailure? {
if (metaHandlers.isEmpty() && commitChecks.isEmpty()) return null
return workflow.runModificationCommitChecks underChangelist@{
@@ -451,8 +483,10 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
}
}
private suspend fun runSyncPostCommitChecks(commitInfo: DynamicCommitInfo,
commitChecks: List<CommitCheck>): NonModalCommitChecksFailure? {
private suspend fun runSyncPostCommitChecks(
commitInfo: DynamicCommitInfo,
commitChecks: List<CommitCheck>,
): NonModalCommitChecksFailure? {
val problems = commitChecks.mapWithProgress { commitCheck ->
AbstractCommitWorkflow.runCommitCheck(project, commitCheck, commitInfo)
}.filterNotNull()
@@ -479,6 +513,7 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
private fun handleCommitProblem(failure: NonModalCommitChecksFailure?, isOnlyRunCommitChecks: Boolean): CommitChecksResult {
val checksPassed = failure == null
val aborted = failure == NonModalCommitChecksFailure.ABORTED
val smartModeRequired = failure == NonModalCommitChecksFailure.SMART_MODE_REQUIRED
when (failure) {
null -> {
@@ -486,6 +521,8 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
isCommitChecksResultUpToDate = RecentCommitChecks.PASSED
}
else {
smartChecksWereBlocked = false
ui.commitProgressUi.hideWarningAboutDumbMode()
isCommitChecksResultUpToDate = RecentCommitChecks.UNKNOWN // We are going to commit, remembering the result is not needed.
}
}
@@ -498,8 +535,12 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
NonModalCommitChecksFailure.POST_FAILED -> {
isCommitChecksResultUpToDate = RecentCommitChecks.POST_FAILED
}
NonModalCommitChecksFailure.SMART_MODE_REQUIRED -> {
isCommitChecksResultUpToDate = RecentCommitChecks.SMART_MODE_REQUIRED
}
NonModalCommitChecksFailure.ABORTED,
NonModalCommitChecksFailure.ERROR -> {
NonModalCommitChecksFailure.ERROR,
-> {
isCommitChecksResultUpToDate = RecentCommitChecks.FAILED
}
}
@@ -513,6 +554,9 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
else if (checksPassed) {
return CommitChecksResult.Passed
}
else if (smartModeRequired) {
return CommitChecksResult.SmartModeRequired
}
else {
return CommitChecksResult.Failed()
}
@@ -601,6 +645,6 @@ abstract class NonModalCommitWorkflowHandler<W : NonModalCommitWorkflow, U : Non
private class PendingPostCommitChecks(val commitInfo: StaticCommitInfo, val commitChecks: List<CommitCheck>)
private enum class NonModalCommitChecksFailure { EARLY_FAILED, MODIFICATIONS_FAILED, POST_FAILED, ABORTED, ERROR }
private enum class NonModalCommitChecksFailure { EARLY_FAILED, MODIFICATIONS_FAILED, POST_FAILED, ABORTED, ERROR, SMART_MODE_REQUIRED }
private enum class RecentCommitChecks { UNKNOWN, PASSED, EARLY_FAILED, MODIFICATIONS_FAILED, POST_FAILED, FAILED }
private enum class RecentCommitChecks { UNKNOWN, PASSED, EARLY_FAILED, MODIFICATIONS_FAILED, POST_FAILED, FAILED, SMART_MODE_REQUIRED }
@@ -42,4 +42,9 @@ interface CommitProgressUi {
fun clearCommitCheckFailures()
fun getCommitCheckFailures(): List<CommitCheckFailure>
@ApiStatus.Internal
fun showWarningAboutDumbMode()
@ApiStatus.Internal
fun hideWarningAboutDumbMode()
}