From fb80753af943e32d57a18e5a172660a8af4d7d41 Mon Sep 17 00:00:00 2001 From: Yam Liu <1056803+yam-liu@users.noreply.github.com> Date: Sat, 27 Jul 2024 16:40:41 +0000 Subject: [PATCH] feat(custom command)!: support multiple contexts within one command --- pkg/config/user_config.go | 4 +- pkg/gui/services/custom_commands/client.go | 4 +- .../custom_commands/keybinding_creator.go | 46 +++++++++++-------- .../access_commit_properties.go | 6 +-- .../custom_commands/basic_cmd_from_config.go | 6 +-- .../custom_commands/check_for_conflicts.go | 6 +-- .../tests/custom_commands/form_prompts.go | 6 +-- .../custom_commands/menu_from_command.go | 6 +-- .../menu_from_commands_output.go | 6 +-- .../tests/custom_commands/multiple_prompts.go | 6 +-- .../custom_commands/show_output_in_panel.go | 4 +- .../custom_commands/suggestions_command.go | 6 +-- .../custom_commands/suggestions_preset.go | 6 +-- pkg/integration/tests/demo/custom_command.go | 6 +-- .../interactive_rebase/show_exec_todos.go | 6 +-- pkg/integration/tests/submodule/enter.go | 6 +-- pkg/integration/tests/submodule/reset.go | 6 +-- .../tests/worktree/custom_command.go | 6 +-- 18 files changed, 75 insertions(+), 67 deletions(-) diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index d08e4fda476..87a028a7323 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -589,8 +589,8 @@ type CustomCommandAfterHook struct { type CustomCommand struct { // The key to trigger the command. Use a single letter or one of the values from https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md Key string `yaml:"key"` - // The context in which to listen for the key - Context string `yaml:"context" jsonschema:"enum=status,enum=files,enum=worktrees,enum=localBranches,enum=remotes,enum=remoteBranches,enum=tags,enum=commits,enum=reflogCommits,enum=subCommits,enum=commitFiles,enum=stash,enum=global"` + // The contexts in which to listen for the key + Contexts []string `yaml:"contexts" jsonschema:"enum=status,enum=files,enum=worktrees,enum=localBranches,enum=remotes,enum=remoteBranches,enum=tags,enum=commits,enum=reflogCommits,enum=subCommits,enum=commitFiles,enum=stash,enum=global"` // The command to run (using Go template syntax for placeholder values) Command string `yaml:"command" jsonschema:"example=git fetch {{.Form.Remote}} {{.Form.Branch}} && git checkout FETCH_HEAD"` // If true, run the command in a subprocess (e.g. if the command requires user input) diff --git a/pkg/gui/services/custom_commands/client.go b/pkg/gui/services/custom_commands/client.go index c746f0579ae..57144542486 100644 --- a/pkg/gui/services/custom_commands/client.go +++ b/pkg/gui/services/custom_commands/client.go @@ -39,11 +39,11 @@ func (self *Client) GetCustomCommandKeybindings() ([]*types.Binding, error) { bindings := []*types.Binding{} for _, customCommand := range self.customCommands { handler := self.handlerCreator.call(customCommand) - binding, err := self.keybindingCreator.call(customCommand, handler) + compoundBindings, err := self.keybindingCreator.call(customCommand, handler) if err != nil { return nil, err } - bindings = append(bindings, binding) + bindings = append(bindings, compoundBindings...) } return bindings, nil diff --git a/pkg/gui/services/custom_commands/keybinding_creator.go b/pkg/gui/services/custom_commands/keybinding_creator.go index 2a65c1324af..cba0ead67d0 100644 --- a/pkg/gui/services/custom_commands/keybinding_creator.go +++ b/pkg/gui/services/custom_commands/keybinding_creator.go @@ -24,12 +24,12 @@ func NewKeybindingCreator(c *helpers.HelperCommon) *KeybindingCreator { } } -func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler func() error) (*types.Binding, error) { - if customCommand.Context == "" { +func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler func() error) ([]*types.Binding, error) { + if customCommand.Contexts == nil || len(customCommand.Contexts) == 0 { return nil, formatContextNotProvidedError(customCommand) } - viewName, err := self.getViewNameAndContexts(customCommand) + viewNames, err := self.getViewNamesAndContexts(customCommand) if err != nil { return nil, err } @@ -39,27 +39,35 @@ func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler description = customCommand.Command } - return &types.Binding{ - ViewName: viewName, - Key: keybindings.GetKey(customCommand.Key), - Modifier: gocui.ModNone, - Handler: handler, - Description: description, - }, nil + bindings := []*types.Binding{} + for _, viewName := range viewNames { + bindings = append(bindings, &types.Binding{ + ViewName: viewName, + Key: keybindings.GetKey(customCommand.Key), + Modifier: gocui.ModNone, + Handler: handler, + Description: description, + }) + } + return bindings, nil } -func (self *KeybindingCreator) getViewNameAndContexts(customCommand config.CustomCommand) (string, error) { - if customCommand.Context == "global" { - return "", nil +func (self *KeybindingCreator) getViewNamesAndContexts(customCommand config.CustomCommand) ([]string, error) { + if lo.Contains(customCommand.Contexts, "global") { + return []string{}, nil } - ctx, ok := self.contextForContextKey(types.ContextKey(customCommand.Context)) - if !ok { - return "", formatUnknownContextError(customCommand) + viewNames := []string{} + for _, context := range customCommand.Contexts { + ctx, ok := self.contextForContextKey(types.ContextKey(context)) + if !ok { + return []string{}, formatUnknownContextError(customCommand) + } + + viewNames = append(viewNames, ctx.GetViewName()) } - viewName := ctx.GetViewName() - return viewName, nil + return viewNames, nil } func (self *KeybindingCreator) contextForContextKey(contextKey types.ContextKey) (types.Context, bool) { @@ -77,7 +85,7 @@ func formatUnknownContextError(customCommand config.CustomCommand) error { return string(key) }) - return fmt.Errorf("Error when setting custom command keybindings: unknown context: %s. Key: %s, Command: %s.\nPermitted contexts: %s", customCommand.Context, customCommand.Key, customCommand.Command, strings.Join(allContextKeyStrings, ", ")) + return fmt.Errorf("Error when setting custom command keybindings: unknown context: %s. Key: %s, Command: %s.\nPermitted contexts: %s", strings.Join(customCommand.Contexts, ", "), customCommand.Key, customCommand.Command, strings.Join(allContextKeyStrings, ", ")) } func formatContextNotProvidedError(customCommand config.CustomCommand) error { diff --git a/pkg/integration/tests/custom_commands/access_commit_properties.go b/pkg/integration/tests/custom_commands/access_commit_properties.go index 6ac77faf85a..5ca0485f594 100644 --- a/pkg/integration/tests/custom_commands/access_commit_properties.go +++ b/pkg/integration/tests/custom_commands/access_commit_properties.go @@ -17,9 +17,9 @@ var AccessCommitProperties = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "X", - Context: "commits", - Command: "printf '%s\n%s\n%s' '{{ .SelectedLocalCommit.Name }}' '{{ .SelectedLocalCommit.Hash }}' '{{ .SelectedLocalCommit.Sha }}' > file.txt", + Key: "X", + Contexts: []string{"commits"}, + Command: "printf '%s\n%s\n%s' '{{ .SelectedLocalCommit.Name }}' '{{ .SelectedLocalCommit.Hash }}' '{{ .SelectedLocalCommit.Sha }}' > file.txt", }, } }, diff --git a/pkg/integration/tests/custom_commands/basic_cmd_from_config.go b/pkg/integration/tests/custom_commands/basic_cmd_from_config.go index 71b99c2de7b..0f103bec88c 100644 --- a/pkg/integration/tests/custom_commands/basic_cmd_from_config.go +++ b/pkg/integration/tests/custom_commands/basic_cmd_from_config.go @@ -15,9 +15,9 @@ var BasicCmdFromConfig = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "files", - Command: "touch myfile", + Key: "a", + Contexts: []string{"files"}, + Command: "touch myfile", }, } }, diff --git a/pkg/integration/tests/custom_commands/check_for_conflicts.go b/pkg/integration/tests/custom_commands/check_for_conflicts.go index cb8ac7c77c7..b389dba3741 100644 --- a/pkg/integration/tests/custom_commands/check_for_conflicts.go +++ b/pkg/integration/tests/custom_commands/check_for_conflicts.go @@ -16,9 +16,9 @@ var CheckForConflicts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "m", - Context: "localBranches", - Command: "git merge {{ .SelectedLocalBranch.Name | quote }}", + Key: "m", + Contexts: []string{"localBranches"}, + Command: "git merge {{ .SelectedLocalBranch.Name | quote }}", After: config.CustomCommandAfterHook{ CheckForConflicts: true, }, diff --git a/pkg/integration/tests/custom_commands/form_prompts.go b/pkg/integration/tests/custom_commands/form_prompts.go index a64c1aa4c27..7e615c5de31 100644 --- a/pkg/integration/tests/custom_commands/form_prompts.go +++ b/pkg/integration/tests/custom_commands/form_prompts.go @@ -15,9 +15,9 @@ var FormPrompts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "files", - Command: `echo {{.Form.FileContent | quote}} > {{.Form.FileName | quote}}`, + Key: "a", + Contexts: []string{"files"}, + Command: `echo {{.Form.FileContent | quote}} > {{.Form.FileName | quote}}`, Prompts: []config.CustomCommandPrompt{ { Key: "FileName", diff --git a/pkg/integration/tests/custom_commands/menu_from_command.go b/pkg/integration/tests/custom_commands/menu_from_command.go index 9e5f39615e1..9030ddcfae4 100644 --- a/pkg/integration/tests/custom_commands/menu_from_command.go +++ b/pkg/integration/tests/custom_commands/menu_from_command.go @@ -21,9 +21,9 @@ var MenuFromCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "localBranches", - Command: `echo "{{index .PromptResponses 0}} {{index .PromptResponses 1}} {{ .SelectedLocalBranch.Name }}" > output.txt`, + Key: "a", + Contexts: []string{"localBranches"}, + Command: `echo "{{index .PromptResponses 0}} {{index .PromptResponses 1}} {{ .SelectedLocalBranch.Name }}" > output.txt`, Prompts: []config.CustomCommandPrompt{ { Type: "menuFromCommand", diff --git a/pkg/integration/tests/custom_commands/menu_from_commands_output.go b/pkg/integration/tests/custom_commands/menu_from_commands_output.go index 7cbd16506e4..d2994a7ab56 100644 --- a/pkg/integration/tests/custom_commands/menu_from_commands_output.go +++ b/pkg/integration/tests/custom_commands/menu_from_commands_output.go @@ -20,9 +20,9 @@ var MenuFromCommandsOutput = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "localBranches", - Command: "git checkout {{ index .PromptResponses 1 }}", + Key: "a", + Contexts: []string{"localBranches"}, + Command: "git checkout {{ index .PromptResponses 1 }}", Prompts: []config.CustomCommandPrompt{ { Type: "input", diff --git a/pkg/integration/tests/custom_commands/multiple_prompts.go b/pkg/integration/tests/custom_commands/multiple_prompts.go index d36c40d6552..51fc24602b0 100644 --- a/pkg/integration/tests/custom_commands/multiple_prompts.go +++ b/pkg/integration/tests/custom_commands/multiple_prompts.go @@ -15,9 +15,9 @@ var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "files", - Command: `echo "{{index .PromptResponses 1}}" > {{index .PromptResponses 0}}`, + Key: "a", + Contexts: []string{"files"}, + Command: `echo "{{index .PromptResponses 1}}" > {{index .PromptResponses 0}}`, Prompts: []config.CustomCommandPrompt{ { Type: "input", diff --git a/pkg/integration/tests/custom_commands/show_output_in_panel.go b/pkg/integration/tests/custom_commands/show_output_in_panel.go index e98f372e673..1769b4d0849 100644 --- a/pkg/integration/tests/custom_commands/show_output_in_panel.go +++ b/pkg/integration/tests/custom_commands/show_output_in_panel.go @@ -18,13 +18,13 @@ var ShowOutputInPanel = NewIntegrationTest(NewIntegrationTestArgs{ cfg.UserConfig.CustomCommands = []config.CustomCommand{ { Key: "X", - Context: "commits", + Contexts: []string{"commits"}, Command: "printf '%s' '{{ .SelectedLocalCommit.Name }}'", ShowOutput: true, }, { Key: "Y", - Context: "commits", + Contexts: []string{"commits"}, Command: "printf '%s' '{{ .SelectedLocalCommit.Name }}'", ShowOutput: true, OutputTitle: "Subject of commit {{ .SelectedLocalCommit.Hash }}", diff --git a/pkg/integration/tests/custom_commands/suggestions_command.go b/pkg/integration/tests/custom_commands/suggestions_command.go index 592c472cf1a..cef33eb4674 100644 --- a/pkg/integration/tests/custom_commands/suggestions_command.go +++ b/pkg/integration/tests/custom_commands/suggestions_command.go @@ -22,9 +22,9 @@ var SuggestionsCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "localBranches", - Command: `git checkout {{.Form.Branch}}`, + Key: "a", + Contexts: []string{"localBranches"}, + Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ { Key: "Branch", diff --git a/pkg/integration/tests/custom_commands/suggestions_preset.go b/pkg/integration/tests/custom_commands/suggestions_preset.go index d4ae422adc2..5f9e856aeda 100644 --- a/pkg/integration/tests/custom_commands/suggestions_preset.go +++ b/pkg/integration/tests/custom_commands/suggestions_preset.go @@ -22,9 +22,9 @@ var SuggestionsPreset = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "localBranches", - Command: `git checkout {{.Form.Branch}}`, + Key: "a", + Contexts: []string{"localBranches"}, + Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ { Key: "Branch", diff --git a/pkg/integration/tests/demo/custom_command.go b/pkg/integration/tests/demo/custom_command.go index 147a63ba473..410a6762fad 100644 --- a/pkg/integration/tests/demo/custom_command.go +++ b/pkg/integration/tests/demo/custom_command.go @@ -28,9 +28,9 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{ cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "a", - Context: "localBranches", - Command: `git checkout {{.Form.Branch}}`, + Key: "a", + Contexts: []string{"localBranches"}, + Command: `git checkout {{.Form.Branch}}`, Prompts: []config.CustomCommandPrompt{ { Key: "Branch", diff --git a/pkg/integration/tests/interactive_rebase/show_exec_todos.go b/pkg/integration/tests/interactive_rebase/show_exec_todos.go index b66036d0217..8ecb2b6a087 100644 --- a/pkg/integration/tests/interactive_rebase/show_exec_todos.go +++ b/pkg/integration/tests/interactive_rebase/show_exec_todos.go @@ -12,9 +12,9 @@ var ShowExecTodos = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "X", - Context: "commits", - Command: "git -c core.editor=: rebase -i -x false HEAD^^", + Key: "X", + Contexts: []string{"commits"}, + Command: "git -c core.editor=: rebase -i -x false HEAD^^", }, } }, diff --git a/pkg/integration/tests/submodule/enter.go b/pkg/integration/tests/submodule/enter.go index 29e983b7f38..285acd4fd46 100644 --- a/pkg/integration/tests/submodule/enter.go +++ b/pkg/integration/tests/submodule/enter.go @@ -12,9 +12,9 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "e", - Context: "files", - Command: "git commit --allow-empty -m \"empty commit\"", + Key: "e", + Contexts: []string{"files"}, + Command: "git commit --allow-empty -m \"empty commit\"", }, } }, diff --git a/pkg/integration/tests/submodule/reset.go b/pkg/integration/tests/submodule/reset.go index a723561fc9f..d00f4b60250 100644 --- a/pkg/integration/tests/submodule/reset.go +++ b/pkg/integration/tests/submodule/reset.go @@ -12,9 +12,9 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "e", - Context: "files", - Command: "git commit --allow-empty -m \"empty commit\" && echo \"my_file content\" > my_file", + Key: "e", + Contexts: []string{"files"}, + Command: "git commit --allow-empty -m \"empty commit\" && echo \"my_file content\" > my_file", }, } }, diff --git a/pkg/integration/tests/worktree/custom_command.go b/pkg/integration/tests/worktree/custom_command.go index 2276e59be07..27c82ebb5f9 100644 --- a/pkg/integration/tests/worktree/custom_command.go +++ b/pkg/integration/tests/worktree/custom_command.go @@ -12,9 +12,9 @@ var CustomCommand = NewIntegrationTest(NewIntegrationTestArgs{ SetupConfig: func(cfg *config.AppConfig) { cfg.UserConfig.CustomCommands = []config.CustomCommand{ { - Key: "d", - Context: "worktrees", - Command: "git worktree remove {{ .SelectedWorktree.Path | quote }}", + Key: "d", + Contexts: []string{"worktrees"}, + Command: "git worktree remove {{ .SelectedWorktree.Path | quote }}", }, } },