diff --git a/docs/Custom_Command_Keybindings.md b/docs/Custom_Command_Keybindings.md index 1053f9e45..dd7c11af7 100644 --- a/docs/Custom_Command_Keybindings.md +++ b/docs/Custom_Command_Keybindings.md @@ -87,6 +87,11 @@ The permitted contexts are: | stash | The 'Stash' tab | | global | This keybinding will take affect everywhere | +> **Bonus** +> +> You can use a comma-separated string, such as `context: 'commits, subCommits'`, to make it effective in multiple contexts. + + ## Prompts ### Common fields diff --git a/pkg/gui/services/custom_commands/client.go b/pkg/gui/services/custom_commands/client.go index c746f0579..571445424 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 2a65c1324..4e6f9d8c7 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) { +func (self *KeybindingCreator) call(customCommand config.CustomCommand, handler func() error) ([]*types.Binding, error) { if customCommand.Context == "" { return nil, formatContextNotProvidedError(customCommand) } - viewName, err := self.getViewNameAndContexts(customCommand) + viewNames, err := self.getViewNamesAndContexts(customCommand) if err != nil { return nil, err } @@ -39,27 +39,38 @@ 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 + return lo.Map(viewNames, func(viewName string, _ int) *types.Binding { + return &types.Binding{ + ViewName: viewName, + Key: keybindings.GetKey(customCommand.Key), + Modifier: gocui.ModNone, + Handler: handler, + Description: description, + } + }), nil } -func (self *KeybindingCreator) getViewNameAndContexts(customCommand config.CustomCommand) (string, error) { +func (self *KeybindingCreator) getViewNamesAndContexts(customCommand config.CustomCommand) ([]string, error) { if customCommand.Context == "global" { - return "", nil + return []string{""}, nil } - ctx, ok := self.contextForContextKey(types.ContextKey(customCommand.Context)) - if !ok { - return "", formatUnknownContextError(customCommand) + contexts := strings.Split(customCommand.Context, ",") + contexts = lo.Map(contexts, func(context string, _ int) string { + return strings.TrimSpace(context) + }) + + viewNames := []string{} + for _, context := range 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) { diff --git a/pkg/integration/tests/custom_commands/global_context.go b/pkg/integration/tests/custom_commands/global_context.go new file mode 100644 index 000000000..8f8518559 --- /dev/null +++ b/pkg/integration/tests/custom_commands/global_context.go @@ -0,0 +1,61 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var GlobalContext = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Ensure global context works", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("my change") + }, + SetupConfig: func(cfg *config.AppConfig) { + cfg.UserConfig.CustomCommands = []config.CustomCommand{ + { + Key: "X", + Context: "global", + Command: "touch myfile", + ShowOutput: false, + }, + } + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + // commits + t.Views().Commits(). + Focus(). + Press("X") + + t.Views().Files(). + Focus(). + Lines(Contains("myfile")) + + t.Shell().DeleteFile("myfile") + t.GlobalPress(keys.Files.RefreshFiles) + + // branches + t.Views().Branches(). + Focus(). + Press("X") + + t.Views().Files(). + Focus(). + Lines(Contains("myfile")) + + t.Shell().DeleteFile("myfile") + t.GlobalPress(keys.Files.RefreshFiles) + + // files + t.Views().Files(). + Focus(). + Press("X") + + t.Views().Files(). + Focus(). + Lines(Contains("myfile")) + + t.Shell().DeleteFile("myfile") + }, +}) diff --git a/pkg/integration/tests/custom_commands/multiple_contexts.go b/pkg/integration/tests/custom_commands/multiple_contexts.go new file mode 100644 index 000000000..3edc6e907 --- /dev/null +++ b/pkg/integration/tests/custom_commands/multiple_contexts.go @@ -0,0 +1,58 @@ +package custom_commands + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var MultipleContexts = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Test that multiple contexts works", + ExtraCmdArgs: []string{}, + Skip: false, + SetupRepo: func(shell *Shell) { + shell.EmptyCommit("my change") + }, + SetupConfig: func(cfg *config.AppConfig) { + cfg.UserConfig.CustomCommands = []config.CustomCommand{ + { + Key: "X", + Context: "commits, reflogCommits", + Command: "touch myfile", + ShowOutput: false, + }, + } + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + // commits + t.Views().Commits(). + Focus(). + Press("X") + + t.Views().Files(). + Focus(). + Lines(Contains("myfile")) + + t.Shell().DeleteFile("myfile") + t.GlobalPress(keys.Files.RefreshFiles) + + // branches + t.Views().Branches(). + Focus(). + Press("X") + + t.Views().Files(). + Focus(). + IsEmpty() + + // files + t.Views().ReflogCommits(). + Focus(). + Press("X") + + t.Views().Files(). + Focus(). + Lines(Contains("myfile")) + + t.Shell().DeleteFile("myfile") + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 477fdee44..7c52d76e9 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -122,9 +122,11 @@ var tests = []*components.IntegrationTest{ custom_commands.DeleteFromHistory, custom_commands.EditHistory, custom_commands.FormPrompts, + custom_commands.GlobalContext, custom_commands.History, custom_commands.MenuFromCommand, custom_commands.MenuFromCommandsOutput, + custom_commands.MultipleContexts, custom_commands.MultiplePrompts, custom_commands.OmitFromHistory, custom_commands.ShowOutputInPanel,