Skip to content

Commit

Permalink
feat(custom command): support multiple contexts within one command (#…
Browse files Browse the repository at this point in the history
…3784)

- **PR Description**

For some custom commands, they can be used in multiple contexts. But for
now, if we want to do this, we should copy and paste the same config
times and times with just a different **context**.

Related issue: #3759 

This PR makes it possible to use multiple contexts in the `context` field of 
`customCommand`, separated by comma.
  • Loading branch information
stefanhaller authored Aug 2, 2024
2 parents dc87592 + 542030f commit 74fe069
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 18 deletions.
5 changes: 5 additions & 0 deletions docs/Custom_Command_Keybindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/gui/services/custom_commands/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 27 additions & 16 deletions pkg/gui/services/custom_commands/keybinding_creator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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) {
Expand Down
61 changes: 61 additions & 0 deletions pkg/integration/tests/custom_commands/global_context.go
Original file line number Diff line number Diff line change
@@ -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")
},
})
58 changes: 58 additions & 0 deletions pkg/integration/tests/custom_commands/multiple_contexts.go
Original file line number Diff line number Diff line change
@@ -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")
},
})
2 changes: 2 additions & 0 deletions pkg/integration/tests/test_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 74fe069

Please sign in to comment.