diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 7e6a21656..f3ff1befb 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -314,6 +314,8 @@ type CustomCommand struct { } type CustomCommandPrompt struct { + Key string `yaml:"key"` + // one of 'input', 'menu', 'confirm', or 'menuFromCommand' Type string `yaml:"type"` diff --git a/pkg/gui/services/custom_commands/handler_creator.go b/pkg/gui/services/custom_commands/handler_creator.go index 6ac9fb733..839f70adf 100644 --- a/pkg/gui/services/custom_commands/handler_creator.go +++ b/pkg/gui/services/custom_commands/handler_creator.go @@ -45,8 +45,9 @@ func (self *HandlerCreator) call(customCommand config.CustomCommand) func() erro return func() error { sessionState := self.sessionStateLoader.call() promptResponses := make([]string, len(customCommand.Prompts)) + form := make(map[string]string) - f := func() error { return self.finalHandler(customCommand, sessionState, promptResponses) } + f := func() error { return self.finalHandler(customCommand, sessionState, promptResponses, form) } // if we have prompts we'll recursively wrap our confirm handlers with more prompts // until we reach the actual command @@ -60,10 +61,11 @@ func (self *HandlerCreator) call(customCommand config.CustomCommand) func() erro wrappedF := func(response string) error { promptResponses[idx] = response + form[prompt.Key] = response return g() } - resolveTemplate := self.getResolveTemplateFn(promptResponses, sessionState) + resolveTemplate := self.getResolveTemplateFn(form, promptResponses, sessionState) resolvedPrompt, err := self.resolver.resolvePrompt(&prompt, resolveTemplate) if err != nil { return self.c.Error(err) @@ -154,19 +156,21 @@ func (self *HandlerCreator) menuPromptFromCommand(prompt *config.CustomCommandPr type CustomCommandObjects struct { *SessionState PromptResponses []string + Form map[string]string } -func (self *HandlerCreator) getResolveTemplateFn(promptResponses []string, sessionState *SessionState) func(string) (string, error) { +func (self *HandlerCreator) getResolveTemplateFn(form map[string]string, promptResponses []string, sessionState *SessionState) func(string) (string, error) { objects := CustomCommandObjects{ SessionState: sessionState, PromptResponses: promptResponses, + Form: form, } return func(templateStr string) (string, error) { return utils.ResolveTemplate(templateStr, objects) } } -func (self *HandlerCreator) finalHandler(customCommand config.CustomCommand, sessionState *SessionState, promptResponses []string) error { - resolveTemplate := self.getResolveTemplateFn(promptResponses, sessionState) +func (self *HandlerCreator) finalHandler(customCommand config.CustomCommand, sessionState *SessionState, promptResponses []string, form map[string]string) error { + resolveTemplate := self.getResolveTemplateFn(form, promptResponses, sessionState) cmdStr, err := resolveTemplate(customCommand.Command) if err != nil { return self.c.Error(err) diff --git a/pkg/integration/tests/custom_commands/form_prompts.go b/pkg/integration/tests/custom_commands/form_prompts.go new file mode 100644 index 000000000..e4712dbcc --- /dev/null +++ b/pkg/integration/tests/custom_commands/form_prompts.go @@ -0,0 +1,88 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var FormPrompts = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Using a custom command reffering prompt responses by name", + ExtraCmdArgs: "", + Skip: false, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("blah") + }, + SetupConfig: func(cfg *config.AppConfig) { + cfg.UserConfig.CustomCommands = []config.CustomCommand{ + { + Key: "a", + Context: "files", + Command: `echo "{{.Form.FileContent}}" > {{.Form.FileName}}`, + Prompts: []config.CustomCommandPrompt{ + { + Key: "FileName", + Type: "input", + Title: "Enter a file name", + }, + { + Key: "FileContent", + Type: "menu", + Title: "Choose file content", + Options: []config.CustomCommandMenuOption{ + { + Name: "foo", + Description: "Foo", + Value: "FOO", + }, + { + Name: "bar", + Description: "Bar", + Value: "BAR", + }, + { + Name: "baz", + Description: "Baz", + Value: "BAZ", + }, + }, + }, + { + Type: "confirm", + Title: "Are you sure?", + Body: "Are you REALLY sure you want to make this file? Up to you buddy.", + }, + }, + }, + } + }, + Run: func( + shell *Shell, + input *Input, + assert *Assert, + keys config.KeybindingConfig, + ) { + assert.WorkingTreeFileCount(0) + + input.PressKeys("a") + + assert.InPrompt() + assert.MatchCurrentViewTitle(Equals("Enter a file name")) + input.Type("myfile") + input.Confirm() + + assert.InMenu() + assert.MatchCurrentViewTitle(Equals("Choose file content")) + assert.MatchSelectedLine(Contains("foo")) + input.NextItem() + assert.MatchSelectedLine(Contains("bar")) + input.Confirm() + + assert.InConfirm() + assert.MatchCurrentViewTitle(Equals("Are you sure?")) + input.Confirm() + + assert.WorkingTreeFileCount(1) + assert.MatchSelectedLine(Contains("myfile")) + assert.MatchMainViewContent(Contains("BAR")) + }, +}) diff --git a/pkg/integration/tests/tests.go b/pkg/integration/tests/tests.go index 9280347be..eb2fd83fa 100644 --- a/pkg/integration/tests/tests.go +++ b/pkg/integration/tests/tests.go @@ -36,6 +36,7 @@ var tests = []*components.IntegrationTest{ bisect.FromOtherBranch, cherry_pick.CherryPick, cherry_pick.CherryPickConflicts, + custom_commands.FormPrompts, } func GetTests() []*components.IntegrationTest { diff --git a/pkg/utils/template.go b/pkg/utils/template.go index 6c147cd38..41388ae3a 100644 --- a/pkg/utils/template.go +++ b/pkg/utils/template.go @@ -7,7 +7,7 @@ import ( ) func ResolveTemplate(templateStr string, object interface{}) (string, error) { - tmpl, err := template.New("template").Parse(templateStr) + tmpl, err := template.New("template").Option("missingkey=error").Parse(templateStr) if err != nil { return "", err } diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/COMMIT_EDITMSG new file mode 100644 index 000000000..907b30816 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/COMMIT_EDITMSG @@ -0,0 +1 @@ +blah diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/FETCH_HEAD b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/FETCH_HEAD new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/HEAD b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/config b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/config new file mode 100644 index 000000000..596ebaeb3 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/config @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true +[user] + email = CI@example.com + name = CI diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/description b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/index b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/index new file mode 100644 index 000000000..65d675154 Binary files /dev/null and b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/index differ diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/info/exclude b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/logs/HEAD b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/logs/HEAD new file mode 100644 index 000000000..153f2ddc4 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 6cd61dc75eb17cf3e01d4d5f8f2b38a73ed9be90 CI 1660591942 +0000 commit (initial): blah diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/logs/refs/heads/master b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/logs/refs/heads/master new file mode 100644 index 000000000..153f2ddc4 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 6cd61dc75eb17cf3e01d4d5f8f2b38a73ed9be90 CI 1660591942 +0000 commit (initial): blah diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 new file mode 100644 index 000000000..adf64119a Binary files /dev/null and b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 differ diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/objects/6c/d61dc75eb17cf3e01d4d5f8f2b38a73ed9be90 b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/objects/6c/d61dc75eb17cf3e01d4d5f8f2b38a73ed9be90 new file mode 100644 index 000000000..8d0c394f8 Binary files /dev/null and b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/objects/6c/d61dc75eb17cf3e01d4d5f8f2b38a73ed9be90 differ diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/refs/heads/master b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/refs/heads/master new file mode 100644 index 000000000..4c2ab0871 --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/.git_keep/refs/heads/master @@ -0,0 +1 @@ +6cd61dc75eb17cf3e01d4d5f8f2b38a73ed9be90 diff --git a/test/integration_new/custom_commands/form_prompts/expected/repo/myfile b/test/integration_new/custom_commands/form_prompts/expected/repo/myfile new file mode 100644 index 000000000..ba578e48b --- /dev/null +++ b/test/integration_new/custom_commands/form_prompts/expected/repo/myfile @@ -0,0 +1 @@ +BAR