diff --git a/pkg/gui/context/suggestions_context.go b/pkg/gui/context/suggestions_context.go index 59908fe5e..7b657121a 100644 --- a/pkg/gui/context/suggestions_context.go +++ b/pkg/gui/context/suggestions_context.go @@ -14,10 +14,11 @@ type SuggestionsContext struct { } type SuggestionsContextState struct { - Suggestions []*types.Suggestion - OnConfirm func() error - OnClose func() error - AsyncHandler *tasks.AsyncHandler + Suggestions []*types.Suggestion + OnConfirm func() error + OnClose func() error + OnDeleteSuggestion func() error + AsyncHandler *tasks.AsyncHandler // FindSuggestions will take a string that the user has typed into a prompt // and return a slice of suggestions which match that string. diff --git a/pkg/gui/controllers/custom_command_action.go b/pkg/gui/controllers/custom_command_action.go index f4de3218e..3225867d9 100644 --- a/pkg/gui/controllers/custom_command_action.go +++ b/pkg/gui/controllers/custom_command_action.go @@ -1,6 +1,7 @@ package controllers import ( + "slices" "strings" "github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers" @@ -32,13 +33,34 @@ func (self *CustomCommandAction) Call() error { self.c.OS().Cmd.NewShell(command), ) }, + HandleDeleteSuggestion: func(index int) error { + // index is the index in the _filtered_ list of suggestions, so we + // need to map it back to the full list. There's no really good way + // to do this, but fortunately we keep the items in the + // CustomCommandsHistory unique, which allows us to simply search + // for it by string. + item := self.c.Contexts().Suggestions.GetItems()[index].Value + fullIndex := lo.IndexOf(self.c.GetAppState().CustomCommandsHistory, item) + if fullIndex == -1 { + // Should never happen, but better be safe + return nil + } + + self.c.GetAppState().CustomCommandsHistory = slices.Delete( + self.c.GetAppState().CustomCommandsHistory, fullIndex, fullIndex+1) + self.c.SaveAppStateAndLogError() + self.c.Contexts().Suggestions.RefreshSuggestions() + return nil + }, }) } func (self *CustomCommandAction) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion { - history := self.c.GetAppState().CustomCommandsHistory + return func(input string) []*types.Suggestion { + history := self.c.GetAppState().CustomCommandsHistory - return helpers.FilterFunc(history, self.c.UserConfig.Gui.UseFuzzySearch()) + return helpers.FilterFunc(history, self.c.UserConfig.Gui.UseFuzzySearch())(input) + } } // this mimics the shell functionality `ignorespace` diff --git a/pkg/gui/controllers/helpers/confirmation_helper.go b/pkg/gui/controllers/helpers/confirmation_helper.go index 8c1265c15..0e5e54109 100644 --- a/pkg/gui/controllers/helpers/confirmation_helper.go +++ b/pkg/gui/controllers/helpers/confirmation_helper.go @@ -270,10 +270,20 @@ func (self *ConfirmationHelper) setKeyBindings(cancel goContext.CancelFunc, opts onClose := self.wrappedConfirmationFunction(cancel, opts.HandleClose) + onDeleteSuggestion := func() error { + if opts.HandleDeleteSuggestion == nil { + return nil + } + + idx := self.c.Contexts().Suggestions.GetSelectedLineIdx() + return opts.HandleDeleteSuggestion(idx) + } + self.c.Contexts().Confirmation.State.OnConfirm = onConfirm self.c.Contexts().Confirmation.State.OnClose = onClose self.c.Contexts().Suggestions.State.OnConfirm = onSuggestionConfirm self.c.Contexts().Suggestions.State.OnClose = onClose + self.c.Contexts().Suggestions.State.OnDeleteSuggestion = onDeleteSuggestion return nil } @@ -284,6 +294,7 @@ func (self *ConfirmationHelper) clearConfirmationViewKeyBindings() { self.c.Contexts().Confirmation.State.OnClose = noop self.c.Contexts().Suggestions.State.OnConfirm = noop self.c.Contexts().Suggestions.State.OnClose = noop + self.c.Contexts().Suggestions.State.OnDeleteSuggestion = noop } func (self *ConfirmationHelper) getSelectedSuggestionValue() string { diff --git a/pkg/gui/controllers/suggestions_controller.go b/pkg/gui/controllers/suggestions_controller.go index d6e6151ff..4491cfe35 100644 --- a/pkg/gui/controllers/suggestions_controller.go +++ b/pkg/gui/controllers/suggestions_controller.go @@ -43,6 +43,12 @@ func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) [] Key: opts.GetKey(opts.Config.Universal.TogglePanel), Handler: func() error { return self.c.ReplaceContext(self.c.Contexts().Confirmation) }, }, + { + Key: opts.GetKey(opts.Config.Universal.Remove), + Handler: func() error { + return self.context().State.OnDeleteSuggestion() + }, + }, } return bindings diff --git a/pkg/gui/popup/popup_handler.go b/pkg/gui/popup/popup_handler.go index 1ca9cc3ee..ac003e943 100644 --- a/pkg/gui/popup/popup_handler.go +++ b/pkg/gui/popup/popup_handler.go @@ -104,13 +104,14 @@ func (self *PopupHandler) Confirm(opts types.ConfirmOpts) error { func (self *PopupHandler) Prompt(opts types.PromptOpts) error { return self.createPopupPanelFn(context.Background(), types.CreatePopupPanelOpts{ - Title: opts.Title, - Prompt: opts.InitialContent, - Editable: true, - HandleConfirmPrompt: opts.HandleConfirm, - HandleClose: opts.HandleClose, - FindSuggestionsFunc: opts.FindSuggestionsFunc, - Mask: opts.Mask, + Title: opts.Title, + Prompt: opts.InitialContent, + Editable: true, + HandleConfirmPrompt: opts.HandleConfirm, + HandleClose: opts.HandleClose, + HandleDeleteSuggestion: opts.HandleDeleteSuggestion, + FindSuggestionsFunc: opts.FindSuggestionsFunc, + Mask: opts.Mask, }) } diff --git a/pkg/gui/types/common.go b/pkg/gui/types/common.go index 44f07c1e3..39378484b 100644 --- a/pkg/gui/types/common.go +++ b/pkg/gui/types/common.go @@ -165,13 +165,14 @@ type CreateMenuOptions struct { } type CreatePopupPanelOpts struct { - HasLoader bool - Editable bool - Title string - Prompt string - HandleConfirm func() error - HandleConfirmPrompt func(string) error - HandleClose func() error + HasLoader bool + Editable bool + Title string + Prompt string + HandleConfirm func() error + HandleConfirmPrompt func(string) error + HandleClose func() error + HandleDeleteSuggestion func(int) error FindSuggestionsFunc func(string) []*Suggestion Mask bool @@ -193,8 +194,9 @@ type PromptOpts struct { FindSuggestionsFunc func(string) []*Suggestion HandleConfirm func(string) error // CAPTURE THIS - HandleClose func() error - Mask bool + HandleClose func() error + HandleDeleteSuggestion func(int) error + Mask bool } type MenuSection struct { diff --git a/pkg/integration/components/prompt_driver.go b/pkg/integration/components/prompt_driver.go index 023c2f438..d1cce878c 100644 --- a/pkg/integration/components/prompt_driver.go +++ b/pkg/integration/components/prompt_driver.go @@ -82,3 +82,12 @@ func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) { NavigateToLine(matcher). PressEnter() } + +func (self *PromptDriver) DeleteSuggestion(matcher *TextMatcher) *PromptDriver { + self.t.press(self.t.keys.Universal.TogglePanel) + self.t.Views().Suggestions(). + IsFocused(). + NavigateToLine(matcher) + self.t.press(self.t.keys.Universal.Remove) + return self +} diff --git a/pkg/integration/tests/custom_commands/delete_from_history.go b/pkg/integration/tests/custom_commands/delete_from_history.go new file mode 100644 index 000000000..e90e6c3c6 --- /dev/null +++ b/pkg/integration/tests/custom_commands/delete_from_history.go @@ -0,0 +1,41 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var DeleteFromHistory = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Delete an entry from the custom commands history", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) {}, + SetupConfig: func(cfg *config.AppConfig) {}, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + createCustomCommand := func(command string) { + t.GlobalPress(keys.Universal.ExecuteCustomCommand) + t.ExpectPopup().Prompt(). + Title(Equals("Custom command:")). + Type(command). + Confirm() + } + + createCustomCommand("echo 1") + createCustomCommand("echo 2") + createCustomCommand("echo 3") + + t.GlobalPress(keys.Universal.ExecuteCustomCommand) + t.ExpectPopup().Prompt(). + Title(Equals("Custom command:")). + SuggestionLines( + Contains("3"), + Contains("2"), + Contains("1"), + ). + DeleteSuggestion(Contains("2")). + SuggestionLines( + Contains("3"), + Contains("1"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 2548702e9..eae6a5f7b 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -105,6 +105,7 @@ var tests = []*components.IntegrationTest{ custom_commands.BasicCmdFromConfig, custom_commands.CheckForConflicts, custom_commands.ComplexCmdAtRuntime, + custom_commands.DeleteFromHistory, custom_commands.FormPrompts, custom_commands.History, custom_commands.MenuFromCommand,