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
  • Loading branch information
yam-liu committed Jul 27, 2024
1 parent f598da0 commit fb80753
Show file tree
Hide file tree
Showing 18 changed files with 75 additions and 67 deletions.
4 changes: 2 additions & 2 deletions pkg/config/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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
46 changes: 27 additions & 19 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) {
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
}
Expand All @@ -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) {
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/custom_commands/check_for_conflicts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/custom_commands/form_prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/custom_commands/menu_from_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/custom_commands/multiple_prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions pkg/integration/tests/custom_commands/show_output_in_panel.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}",
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/custom_commands/suggestions_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/custom_commands/suggestions_preset.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/demo/custom_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/interactive_rebase/show_exec_todos.go
Original file line number Diff line number Diff line change
Expand Up @@ -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^^",
},
}
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/submodule/enter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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\"",
},
}
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/submodule/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
}
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/integration/tests/worktree/custom_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}",
},
}
},
Expand Down

0 comments on commit fb80753

Please sign in to comment.