diff --git a/docs/Custom_Command_Keybindings.md b/docs/Custom_Command_Keybindings.md index 62a034ecf..af9431d07 100644 --- a/docs/Custom_Command_Keybindings.md +++ b/docs/Custom_Command_Keybindings.md @@ -107,6 +107,7 @@ The permitted prompt fields are: | type | one of 'input', 'menu', or 'confirm' | yes | | title | the title to display in the popup panel | no | | initialValue | (only applicable to 'input' prompts) the initial value to appear in the text box | no | +| suggestionsPreset | (only applicable to 'input prompts'. Shows suggestions as the value is typed. One of 'files', 'branches', 'remotes', 'remoteBranches', refs'. | no | | body | (only applicable to 'confirm' prompts) the immutable body text to appear in the text box | no | | options | (only applicable to 'menu' prompts) the options to display in the menu | no | | command | (only applicable to 'menuFromCommand' prompts) the command to run to generate | yes | diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 3b311c0de..27ae17953 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -366,7 +366,8 @@ type CustomCommandPrompt struct { Title string `yaml:"title"` // this only apply to input prompts - InitialValue string `yaml:"initialValue"` + InitialValue string `yaml:"initialValue"` + SuggestionsPreset string `yaml:"suggestionsPreset"` // this only applies to confirm prompts Body string `yaml:"body"` diff --git a/pkg/gui/services/custom_commands/client.go b/pkg/gui/services/custom_commands/client.go index 4cacba385..5e456ff6e 100644 --- a/pkg/gui/services/custom_commands/client.go +++ b/pkg/gui/services/custom_commands/client.go @@ -19,7 +19,7 @@ func NewClient( helpers *helpers.Helpers, ) *Client { sessionStateLoader := NewSessionStateLoader(c, helpers.Refs) - handlerCreator := NewHandlerCreator(c, sessionStateLoader) + handlerCreator := NewHandlerCreator(c, sessionStateLoader, helpers.Suggestions) keybindingCreator := NewKeybindingCreator(c) customCommands := c.UserConfig.CustomCommands diff --git a/pkg/gui/services/custom_commands/handler_creator.go b/pkg/gui/services/custom_commands/handler_creator.go index da1c449fd..2d13a6f55 100644 --- a/pkg/gui/services/custom_commands/handler_creator.go +++ b/pkg/gui/services/custom_commands/handler_creator.go @@ -1,6 +1,7 @@ package custom_commands import ( + "fmt" "strings" "text/template" @@ -18,11 +19,13 @@ type HandlerCreator struct { sessionStateLoader *SessionStateLoader resolver *Resolver menuGenerator *MenuGenerator + suggestionsHelper *helpers.SuggestionsHelper } func NewHandlerCreator( c *helpers.HelperCommon, sessionStateLoader *SessionStateLoader, + suggestionsHelper *helpers.SuggestionsHelper, ) *HandlerCreator { resolver := NewResolver(c.Common) menuGenerator := NewMenuGenerator(c.Common) @@ -32,6 +35,7 @@ func NewHandlerCreator( sessionStateLoader: sessionStateLoader, resolver: resolver, menuGenerator: menuGenerator, + suggestionsHelper: suggestionsHelper, } } @@ -104,15 +108,42 @@ func (self *HandlerCreator) call(customCommand config.CustomCommand) func() erro } func (self *HandlerCreator) inputPrompt(prompt *config.CustomCommandPrompt, wrappedF func(string) error) error { + var findSuggestionsFn func(string) []*types.Suggestion + if prompt.SuggestionsPreset != "" { + var err error + findSuggestionsFn, err = self.getPresetSuggestionsFn(prompt.SuggestionsPreset) + if err != nil { + return err + } + } + return self.c.Prompt(types.PromptOpts{ - Title: prompt.Title, - InitialContent: prompt.InitialValue, + Title: prompt.Title, + InitialContent: prompt.InitialValue, + FindSuggestionsFunc: findSuggestionsFn, HandleConfirm: func(str string) error { return wrappedF(str) }, }) } +func (self *HandlerCreator) getPresetSuggestionsFn(preset string) (func(string) []*types.Suggestion, error) { + switch preset { + case "files": + return self.suggestionsHelper.GetFilePathSuggestionsFunc(), nil + case "branches": + return self.suggestionsHelper.GetBranchNameSuggestionsFunc(), nil + case "remotes": + return self.suggestionsHelper.GetRemoteSuggestionsFunc(), nil + case "remoteBranches": + return self.suggestionsHelper.GetRemoteBranchesSuggestionsFunc("/"), nil + case "refs": + return self.suggestionsHelper.GetRefsSuggestionsFunc(), nil + default: + return nil, fmt.Errorf("Unknown value for suggestionsPreset in custom command: %s. Valid values: files, branches, remotes, remoteBranches, refs", preset) + } +} + func (self *HandlerCreator) menuPrompt(prompt *config.CustomCommandPrompt, wrappedF func(string) error) error { menuItems := slices.Map(prompt.Options, func(option config.CustomCommandMenuOption) *types.MenuItem { return &types.MenuItem{ diff --git a/pkg/gui/services/custom_commands/resolver.go b/pkg/gui/services/custom_commands/resolver.go index 4c32a51ca..119581bfc 100644 --- a/pkg/gui/services/custom_commands/resolver.go +++ b/pkg/gui/services/custom_commands/resolver.go @@ -34,6 +34,11 @@ func (self *Resolver) resolvePrompt( return nil, err } + result.SuggestionsPreset, err = resolveTemplate(prompt.SuggestionsPreset) + if err != nil { + return nil, err + } + result.Body, err = resolveTemplate(prompt.Body) if err != nil { return nil, err diff --git a/pkg/integration/tests/custom_commands/suggestions_preset.go b/pkg/integration/tests/custom_commands/suggestions_preset.go new file mode 100644 index 000000000..894e3b1fe --- /dev/null +++ b/pkg/integration/tests/custom_commands/suggestions_preset.go @@ -0,0 +1,64 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var SuggestionsPreset = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Using a custom command that uses a suggestions preset in a prompt step", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) { + shell.NewBranch("branch-one") + shell.EmptyCommit("blah") + shell.NewBranch("branch-two") + shell.EmptyCommit("blah") + shell.NewBranch("branch-three") + shell.EmptyCommit("blah") + shell.NewBranch("branch-four") + shell.EmptyCommit("blah") + }, + SetupConfig: func(cfg *config.AppConfig) { + cfg.UserConfig.CustomCommands = []config.CustomCommand{ + { + Key: "a", + Context: "localBranches", + Command: `git checkout {{.Form.Branch}}`, + Prompts: []config.CustomCommandPrompt{ + { + Key: "Branch", + Type: "input", + Title: "Enter a branch name", + SuggestionsPreset: "branches", + }, + }, + }, + } + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains("branch-four").IsSelected(), + Contains("branch-three"), + Contains("branch-two"), + Contains("branch-one"), + ). + Press("a") + + t.ExpectPopup().Prompt(). + Title(Equals("Enter a branch name")). + Type("three"). + SuggestionLines(Contains("branch-three")). + ConfirmFirstSuggestion() + + t.Views().Branches(). + Lines( + Contains("branch-three").IsSelected(), + Contains("branch-four"), + Contains("branch-two"), + Contains("branch-one"), + ) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 1dd1a48e7..e0800f7bd 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -77,6 +77,7 @@ var tests = []*components.IntegrationTest{ custom_commands.MenuFromCommandsOutput, custom_commands.MultiplePrompts, custom_commands.OmitFromHistory, + custom_commands.SuggestionsPreset, diff.Diff, diff.DiffAndApplyPatch, diff.DiffCommits,