diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index 6c8f49e1f..748355f67 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -339,7 +339,10 @@ func (self *CommitFilesController) discard(selectedNodes []*filetree.CommitFileN HandleConfirm: func() error { commits := self.c.Model().Commits selectedLineIdx := self.c.Contexts().LocalCommits.GetSelectedLineIdx() - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.RebasingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.RebasingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { var filePaths []string selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes) diff --git a/pkg/gui/controllers/helpers/app_status_helper.go b/pkg/gui/controllers/helpers/app_status_helper.go index 99664445c..44de70546 100644 --- a/pkg/gui/controllers/helpers/app_status_helper.go +++ b/pkg/gui/controllers/helpers/app_status_helper.go @@ -85,30 +85,32 @@ func (self *AppStatusHelper) WithWaitingStatusImpl(message string, f func(gocui. // WithWaitingStatusBlockingInput is like WithWaitingStatus, but it also blocks // keyboard input for the whole duration of the operation: keys the user presses // while it runs are buffered and replayed against the post-operation state (see -// gocui.BeginBlockingEvents). Use it for operations that manipulate an -// in-progress rebase or otherwise rewrite commits, where a racing keypress -// would target the wrong commit or todo. +// gocui.BeginBlockingEvents). Use it for operations whose following keypress +// depends on the state they produce, e.g. ones that manipulate an in-progress +// rebase or otherwise rewrite commits, where a racing keypress would target the +// wrong commit or todo. // // Must be called on the UI thread: the block is begun synchronously here, before // the operation is dispatched to a worker, so no keypress can slip through in // between. -func (self *AppStatusHelper) WithWaitingStatusBlockingInput(message string, f func(gocui.Task) error) { +func (self *AppStatusHelper) WithWaitingStatusBlockingInput(opts types.WaitingStatusOpts, f func(gocui.Task) error) { self.c.GocuiGui().BeginBlockingEvents() - // Hide the rebasing-mode indicator (and its reset button) while we drive the - // rebase ourselves; it reflects the transient on-disk state and would - // otherwise flash on for the duration of the operation. - self.modeHelper.SetSuppressWorkingTreeStateMode(true) + if opts.HideWorkingTreeState { + self.modeHelper.SetSuppressWorkingTreeStateMode(true) + } self.c.OnWorker(func(task gocui.Task) error { // End the block and restore the mode indicator once the operation and its // refresh have applied their UI updates: OnUIThread queues this after the // refresh's model bounces and Then (which RefreshFromWorker has already // enqueued by the time f returns), so the replayed keys act on the - // refreshed state and any resulting rebase state shows correctly. + // refreshed state and any resulting working tree state shows correctly. defer self.c.OnUIThread(func() error { - self.modeHelper.SetSuppressWorkingTreeStateMode(false) + if opts.HideWorkingTreeState { + self.modeHelper.SetSuppressWorkingTreeStateMode(false) + } return self.c.GocuiGui().EndBlockingEvents() }) - return self.WithWaitingStatusImpl(message, f, task) + return self.WithWaitingStatusImpl(opts.Message, f, task) }) } diff --git a/pkg/gui/controllers/helpers/cherry_pick_helper.go b/pkg/gui/controllers/helpers/cherry_pick_helper.go index fc96b9d1b..f82f8843a 100644 --- a/pkg/gui/controllers/helpers/cherry_pick_helper.go +++ b/pkg/gui/controllers/helpers/cherry_pick_helper.go @@ -85,7 +85,10 @@ func (self *CherryPickHelper) Paste() error { HandleConfirm: func() error { mustStash := IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules) cherryPickedCommits := self.getData().CherryPickedCommits - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.CherryPickingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.CherryPickingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.CherryPick) if mustStash { diff --git a/pkg/gui/controllers/local_commits_controller.go b/pkg/gui/controllers/local_commits_controller.go index 17c1dcf30..642263d12 100644 --- a/pkg/gui/controllers/local_commits_controller.go +++ b/pkg/gui/controllers/local_commits_controller.go @@ -743,7 +743,10 @@ func (self *LocalCommitsController) squashDown(selectedCommits []*models.Commit, HandleConfirm: func() error { commits := self.c.Model().Commits self.selectRebaseResultCommit(startIdx) - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.SquashingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.SquashingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.SquashCommitDown) return self.interactiveRebase(commits, todo.Squash, startIdx, endIdx) }) @@ -767,7 +770,10 @@ func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, star OnPress: func() error { commits := self.c.Model().Commits self.selectRebaseResultCommit(startIdx) - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.FixingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.FixingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.FixupCommit) return self.interactiveRebase(commits, todo.Fixup, startIdx, endIdx) }) @@ -780,7 +786,10 @@ func (self *LocalCommitsController) fixup(selectedCommits []*models.Commit, star OnPress: func() error { commits := self.c.Model().Commits self.selectRebaseResultCommit(startIdx) - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.FixingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.FixingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.FixupCommitKeepMessage) return self.interactiveRebaseWithFlag(commits, todo.Fixup, startIdx, endIdx, "-C") }) @@ -891,7 +900,10 @@ func (self *LocalCommitsController) handleReword(summary string, description str self.c.Tr.RewordingStatus, nil, nil) } - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.RewordingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.RewordingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { err := self.c.Git().Rebase.RewordCommit(commits, selectedIdx, summary, description) if err != nil { return err @@ -977,7 +989,10 @@ func (self *LocalCommitsController) drop(selectedCommits []*models.Commit, start if !isMerge { self.selectRebaseResultCommit(startIdx) } - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.DroppingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.DroppingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.DropCommit) if isMerge { return self.dropMergeCommit(commits, startIdx) @@ -1002,7 +1017,10 @@ func (self *LocalCommitsController) edit(selectedCommits []*models.Commit, start commits := self.c.Model().Commits if !commits[endIdx].IsMerge() { - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.RebasingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.RebasingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { err := self.c.Git().Rebase.InteractiveRebase(commits, startIdx, endIdx, todo.Edit, "") return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions( err, types.RefreshOptions{BatchUIUpdates: true}) @@ -1024,7 +1042,10 @@ func (self *LocalCommitsController) quickStartInteractiveRebase() error { func (self *LocalCommitsController) startInteractiveRebaseWithEdit( commitsToEdit []*models.Commit, ) error { - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.RebasingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.RebasingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.EditCommit) err := self.c.Git().Rebase.EditRebase(commitsToEdit[len(commitsToEdit)-1].Hash()) return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions( @@ -1164,7 +1185,10 @@ func (self *LocalCommitsController) move( } commits := self.c.Model().Commits - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.MovingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.MovingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { if offset > 0 { self.c.LogAction(self.c.Tr.Actions.MoveCommitDown) } else { @@ -1209,7 +1233,10 @@ func (self *LocalCommitsController) amendTo(commit *models.Commit) error { selectedIdx := self.context().GetView().SelectedLineIdx() handleCommit = func() error { return self.c.Helpers().WorkingTree.WithEnsureCommittableFiles(func() error { - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.AmendingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.AmendingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.AmendCommit) err := self.c.Git().Rebase.AmendTo(commits, selectedIdx) return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err) @@ -1271,7 +1298,10 @@ func (self *LocalCommitsController) amendAttribute(_ []*models.Commit, start, en } func (self *LocalCommitsController) resetAuthor(commits []*models.Commit, start, end int) error { - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.AmendingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.AmendingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.ResetCommitAuthor) if err := self.c.Git().Rebase.ResetCommitAuthor(commits, start, end); err != nil { return err @@ -1287,7 +1317,10 @@ func (self *LocalCommitsController) setAuthor(commits []*models.Commit, start, e Title: self.c.Tr.SetAuthorPromptTitle, FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(), HandleConfirm: func(value string) error { - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.AmendingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.AmendingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.SetCommitAuthor) if err := self.c.Git().Rebase.SetCommitAuthor(commits, start, end, value); err != nil { return err @@ -1307,7 +1340,10 @@ func (self *LocalCommitsController) addCoAuthor(commits []*models.Commit, start, Title: self.c.Tr.AddCoAuthorPromptTitle, FindSuggestionsFunc: self.c.Helpers().Suggestions.GetAuthorsSuggestionsFunc(), HandleConfirm: func(value string) error { - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.AmendingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.AmendingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.AddCommitCoAuthor) if err := self.c.Git().Rebase.AddCommitCoAuthor(commits, start, end, value); err != nil { return err @@ -1341,7 +1377,10 @@ func (self *LocalCommitsController) revert(commits []*models.Commit, start, end HandleConfirm: func() error { self.c.LogAction(self.c.Tr.Actions.RevertCommit) mustStash := helpers.IsWorkingTreeDirtyExceptSubmodules(self.c.Model().Files, self.c.Model().Submodules) - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.RevertingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.RevertingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { if mustStash { if err := self.c.Git().Stash.Push(self.c.Tr.AutoStashForReverting); err != nil { return err @@ -1392,7 +1431,10 @@ func (self *LocalCommitsController) createFixupCommit(commit *models.Commit) err selectedIdx := self.context().GetSelectedLineIdx() commits := self.c.Model().Commits branches := self.c.Model().Branches - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.CreatingFixupCommitStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.CreatingFixupCommitStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { if err := self.c.Git().Commit.CreateFixupCommit(commit.Hash()); err != nil { return err } @@ -1500,7 +1542,10 @@ func (self *LocalCommitsController) createAmendCommit(commit *models.Commit, inc selectedIdx := self.context().GetSelectedLineIdx() commits := self.c.Model().Commits branches := self.c.Model().Branches - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.CreatingFixupCommitStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.CreatingFixupCommitStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { if err := self.c.Git().Commit.CreateAmendCommit(originalSubject, summary, description, includeFileChanges); err != nil { return err } @@ -1561,7 +1606,10 @@ func (self *LocalCommitsController) squashFixupsImpl(commit *models.Commit, reba // up by that many rows to stay on the same commit. Compute the target as an // absolute index now, on the current list. targetIdx := self.context().GetSelectedLineIdx() - selectionOffset - return self.c.WithWaitingStatusBlockingInput(self.c.Tr.SquashingStatus, func(gocui.Task) error { + return self.c.WithWaitingStatusBlockingInput(types.WaitingStatusOpts{ + Message: self.c.Tr.SquashingStatus, + HideWorkingTreeState: true, + }, func(gocui.Task) error { self.c.LogAction(self.c.Tr.Actions.SquashAllAboveFixupCommits) err := self.c.Git().Rebase.SquashAllAboveFixupCommits(commit) return self.c.Helpers().MergeAndRebase.CheckMergeOrRebaseWithRefreshOptions( diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 4dcd3c209..a7466d584 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -832,8 +832,8 @@ func NewGui( return nil }, func(message string, f func(gocui.Task) error) { gui.helpers.AppStatus.WithWaitingStatus(message, f) }, - func(message string, f func(gocui.Task) error) { - gui.helpers.AppStatus.WithWaitingStatusBlockingInput(message, f) + func(opts types.WaitingStatusOpts, f func(gocui.Task) error) { + gui.helpers.AppStatus.WithWaitingStatusBlockingInput(opts, f) }, func(message string, kind types.ToastKind) { gui.helpers.AppStatus.Toast(message, kind) }, func() string { return gui.Views.Prompt.TextArea.GetContent() }, diff --git a/pkg/gui/popup/popup_handler.go b/pkg/gui/popup/popup_handler.go index 7c15c56ea..3b305a25d 100644 --- a/pkg/gui/popup/popup_handler.go +++ b/pkg/gui/popup/popup_handler.go @@ -19,7 +19,7 @@ type PopupHandler struct { currentContextFn func() types.Context createMenuFn func(types.CreateMenuOptions) error withWaitingStatusFn func(message string, f func(gocui.Task) error) - withWaitingStatusBlockingInputFn func(message string, f func(gocui.Task) error) + withWaitingStatusBlockingInputFn func(opts types.WaitingStatusOpts, f func(gocui.Task) error) toastFn func(message string, kind types.ToastKind) getPromptInputFn func() string inDemo func() bool @@ -35,7 +35,7 @@ func NewPopupHandler( currentContextFn func() types.Context, createMenuFn func(types.CreateMenuOptions) error, withWaitingStatusFn func(message string, f func(gocui.Task) error), - withWaitingStatusBlockingInputFn func(message string, f func(gocui.Task) error), + withWaitingStatusBlockingInputFn func(opts types.WaitingStatusOpts, f func(gocui.Task) error), toastFn func(message string, kind types.ToastKind), getPromptInputFn func() string, inDemo func() bool, @@ -76,8 +76,8 @@ func (self *PopupHandler) WithWaitingStatus(message string, f func(gocui.Task) e return nil } -func (self *PopupHandler) WithWaitingStatusBlockingInput(message string, f func(gocui.Task) error) error { - self.withWaitingStatusBlockingInputFn(message, f) +func (self *PopupHandler) WithWaitingStatusBlockingInput(opts types.WaitingStatusOpts, f func(gocui.Task) error) error { + self.withWaitingStatusBlockingInputFn(opts, f) return nil } diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 2b5fb0ac7..b2b924bc0 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -171,7 +171,7 @@ type IPopupHandler interface { // Shows a popup prompting the user for input. Prompt(opts PromptOpts) WithWaitingStatus(message string, f func(gocui.Task) error) error - WithWaitingStatusBlockingInput(message string, f func(gocui.Task) error) error + WithWaitingStatusBlockingInput(opts WaitingStatusOpts, f func(gocui.Task) error) error Menu(opts CreateMenuOptions) error Toast(message string) ErrorToast(message string) @@ -179,6 +179,20 @@ type IPopupHandler interface { GetPromptInput() string } +type WaitingStatusOpts struct { + // The message shown alongside the spinner while the operation runs. + Message string + + // When set, the working tree state mode (the yellow + // "Rebasing"/"Merging"/"Cherry-picking"/"Reverting" indicator, along with + // its abort button) stays hidden until the operation is done. Set it for + // operations that drive such a state themselves: the state they leave on + // disk while they run is transient, so surfacing it would flash the + // indicator on and offer to abort a sequence that lazygit is in the middle + // of running. + HideWorkingTreeState bool +} + type ToastKind int const (