From 63e89b63766514c61aed0297009973f8ce475a31 Mon Sep 17 00:00:00 2001 From: William Weishuhn Date: Tue, 17 Mar 2026 10:57:46 -0700 Subject: [PATCH] Show waiting status during discard and reset actions --- pkg/gui/controllers/files_controller.go | 44 +++--- .../controllers/workspace_reset_controller.go | 144 ++++++++++-------- 2 files changed, 103 insertions(+), 85 deletions(-) diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index 8cc2ca5e2a6..6431cb3d7ec 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -1466,20 +1466,22 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { discardAllChangesItem := types.MenuItem{ Label: self.c.Tr.DiscardAllChanges, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInFile) + return self.c.WithWaitingStatus(self.c.Tr.DiscardAllChanges, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInFile) - if self.context().IsSelectingRange() { - defer self.context().CancelRangeSelect() - } + if self.context().IsSelectingRange() { + defer self.context().CancelRangeSelect() + } - for _, node := range selectedNodes { - if err := self.c.Git().WorkingTree.DiscardAllDirChanges(node); err != nil { - return err + for _, node := range selectedNodes { + if err := self.c.Git().WorkingTree.DiscardAllDirChanges(node); err != nil { + return err + } } - } - self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) - return nil + self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) + return nil + }) }, Key: self.c.KeybindingsOpts().GetKey(self.c.UserConfig().Keybinding.Files.ConfirmDiscard), Tooltip: utils.ResolvePlaceholderString( @@ -1493,20 +1495,22 @@ func (self *FilesController) remove(selectedNodes []*filetree.FileNode) error { discardUnstagedChangesItem := types.MenuItem{ Label: self.c.Tr.DiscardUnstagedChanges, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.DiscardAllUnstagedChangesInFile) + return self.c.WithWaitingStatus(self.c.Tr.DiscardUnstagedChanges, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.DiscardAllUnstagedChangesInFile) - if self.context().IsSelectingRange() { - defer self.context().CancelRangeSelect() - } + if self.context().IsSelectingRange() { + defer self.context().CancelRangeSelect() + } - for _, node := range selectedNodes { - if err := self.c.Git().WorkingTree.DiscardUnstagedDirChanges(node); err != nil { - return err + for _, node := range selectedNodes { + if err := self.c.Git().WorkingTree.DiscardUnstagedDirChanges(node); err != nil { + return err + } } - } - self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) - return nil + self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES, types.WORKTREES}}) + return nil + }) }, Key: 'u', Tooltip: utils.ResolvePlaceholderString( diff --git a/pkg/gui/controllers/workspace_reset_controller.go b/pkg/gui/controllers/workspace_reset_controller.go index 82357922f4a..5b3abc84403 100644 --- a/pkg/gui/controllers/workspace_reset_controller.go +++ b/pkg/gui/controllers/workspace_reset_controller.go @@ -36,19 +36,21 @@ func (self *FilesController) createResetMenu() error { Title: self.c.Tr.Actions.NukeWorkingTree, Prompt: self.c.Tr.NukeTreeConfirmation, HandleConfirm: func() error { - self.c.LogAction(self.c.Tr.Actions.NukeWorkingTree) - if err := self.c.Git().WorkingTree.ResetAndClean(); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.DiscardAllChangesToAllFiles, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.NukeWorkingTree) + if err := self.c.Git().WorkingTree.ResetAndClean(); err != nil { + return err + } - if self.c.UserConfig().Gui.AnimateExplosion { - self.animateExplosion() - } + if self.c.UserConfig().Gui.AnimateExplosion { + self.animateExplosion() + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, }) return nil @@ -62,15 +64,17 @@ func (self *FilesController) createResetMenu() error { red.Sprint("git checkout -- ."), }, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.DiscardUnstagedFileChanges) - if err := self.c.Git().WorkingTree.DiscardAnyUnstagedFileChanges(); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.DiscardAnyUnstagedChanges, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.DiscardUnstagedFileChanges) + if err := self.c.Git().WorkingTree.DiscardAnyUnstagedFileChanges(); err != nil { + return err + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, Key: 'u', }, @@ -80,15 +84,17 @@ func (self *FilesController) createResetMenu() error { red.Sprint("git clean -fd"), }, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.RemoveUntrackedFiles) - if err := self.c.Git().WorkingTree.RemoveUntrackedFiles(); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.DiscardUntrackedFiles, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.RemoveUntrackedFiles) + if err := self.c.Git().WorkingTree.RemoveUntrackedFiles(); err != nil { + return err + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, Key: 'c', }, @@ -99,21 +105,23 @@ func (self *FilesController) createResetMenu() error { }, Tooltip: self.c.Tr.DiscardStagedChangesDescription, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.RemoveStagedFiles) - if !self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules() { - return errors.New(self.c.Tr.NoTrackedStagedFilesStash) - } - if err := self.c.Git().Stash.SaveStagedChanges("[lazygit] tmp stash"); err != nil { - return err - } - if err := self.c.Git().Stash.DropNewest(); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.DiscardStagedChanges, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.RemoveStagedFiles) + if !self.c.Helpers().WorkingTree.IsWorkingTreeDirtyExceptSubmodules() { + return errors.New(self.c.Tr.NoTrackedStagedFilesStash) + } + if err := self.c.Git().Stash.SaveStagedChanges("[lazygit] tmp stash"); err != nil { + return err + } + if err := self.c.Git().Stash.DropNewest(); err != nil { + return err + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, Key: 'S', }, @@ -123,15 +131,17 @@ func (self *FilesController) createResetMenu() error { red.Sprint("git reset --soft HEAD"), }, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.SoftReset) - if err := self.c.Git().WorkingTree.ResetSoft("HEAD"); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.SoftReset, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.SoftReset) + if err := self.c.Git().WorkingTree.ResetSoft("HEAD"); err != nil { + return err + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, Key: 's', }, @@ -141,15 +151,17 @@ func (self *FilesController) createResetMenu() error { red.Sprint("git reset --mixed HEAD"), }, OnPress: func() error { - self.c.LogAction(self.c.Tr.Actions.MixedReset) - if err := self.c.Git().WorkingTree.ResetMixed("HEAD"); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.Actions.MixedReset, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.MixedReset) + if err := self.c.Git().WorkingTree.ResetMixed("HEAD"); err != nil { + return err + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, Key: 'm', }, @@ -164,15 +176,17 @@ func (self *FilesController) createResetMenu() error { Title: self.c.Tr.Actions.HardReset, Prompt: self.c.Tr.ResetHardConfirmation, HandleConfirm: func() error { - self.c.LogAction(self.c.Tr.Actions.HardReset) - if err := self.c.Git().WorkingTree.ResetHard("HEAD"); err != nil { - return err - } + return self.c.WithWaitingStatus(self.c.Tr.HardReset, func(gocui.Task) error { + self.c.LogAction(self.c.Tr.Actions.HardReset) + if err := self.c.Git().WorkingTree.ResetHard("HEAD"); err != nil { + return err + } - self.c.Refresh( - types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, - ) - return nil + self.c.Refresh( + types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}, + ) + return nil + }) }, }) },