1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2026-01-26 01:41:35 +03:00

feat: support custom keybinds in custom command prompt menus

This commit is contained in:
Karl Wikström
2025-12-21 08:19:18 +01:00
parent 262e7f4e79
commit 62913ee25d
9 changed files with 146 additions and 0 deletions

View File

@@ -192,6 +192,7 @@ The permitted option fields are:
| name | The first part of the label | no |
| description | The second part of the label | no |
| value | the value that will be used in the command | yes |
| key | Keybinding to invoke this menu option without needing to navigate to it. Can be a single letter or one of the values from [here](https://github.com/jesseduffield/lazygit/blob/master/docs/keybindings/Custom_Keybindings.md) | no |
If an option has no name the value will be displayed to the user in place of the name, so you're allowed to only include the value like so:
@@ -233,6 +234,34 @@ customCommands:
description: 'branch for a release'
```
Here's an example of supplying keybindings for menu options:
```yml
customCommands:
- key: 'a'
command: 'echo {{.Form.BranchType | quote}}'
context: 'commits'
prompts:
- type: 'menu'
title: 'What kind of branch is it?'
key: 'BranchType'
options:
- value: 'feature'
name: 'feature branch'
description: 'branch based off develop'
key: 'f'
- value: 'hotfix'
name: 'hotfix branch'
description: 'branch based off main for fast bug fixes'
key: 'h'
- value: 'release'
name: 'release branch'
description: 'branch for a release'
key: 'r'
```
In this example, pressing 'f', 'h', or 'r' will directly select the corresponding option without needing to navigate to it first.
### Menu-from-command
| _field_ | _description_ | _required_ |

View File

@@ -733,6 +733,8 @@ type CustomCommandMenuOption struct {
Description string `yaml:"description"`
// The value that will be used in the command
Value string `yaml:"value" jsonschema:"example=feature,minLength=1"`
// Keybinding to invoke this menu option without needing to navigate to it
Key string `yaml:"key"`
}
type CustomIconsConfig struct {

View File

@@ -136,6 +136,12 @@ func validateCustomCommands(customCommands []CustomCommand) error {
return err
}
} else {
for _, prompt := range customCommand.Prompts {
if err := validateCustomCommandPrompt(prompt); err != nil {
return err
}
}
if err := validateEnum("customCommand.output", customCommand.Output,
[]string{"", "none", "terminal", "log", "logWithPty", "popup"}); err != nil {
return err
@@ -144,3 +150,14 @@ func validateCustomCommands(customCommands []CustomCommand) error {
}
return nil
}
func validateCustomCommandPrompt(prompt CustomCommandPrompt) error {
for _, option := range prompt.Options {
if !isValidKeybindingKey(option.Key) {
return fmt.Errorf("Unrecognized key '%s' for custom command prompt option. For permitted values see %s",
option.Key, constants.Links.Docs.CustomKeybindings)
}
}
return nil
}

View File

@@ -176,6 +176,31 @@ func TestUserConfigValidate_enums(t *testing.T) {
{value: "invalid_value", valid: false},
},
},
{
name: "Custom command keybinding in prompt menu",
setup: func(config *UserConfig, value string) {
config.CustomCommands = []CustomCommand{
{
Key: "X",
Description: "My Custom Commands",
Prompts: []CustomCommandPrompt{
{
Options: []CustomCommandMenuOption{
{Key: value},
},
},
},
},
}
},
testCases: []testCase{
{value: "", valid: true},
{value: "<disabled>", valid: true},
{value: "q", valid: true},
{value: "<c-c>", valid: true},
{value: "invalid_value", valid: false},
},
},
{
name: "Custom command output",
setup: func(config *UserConfig, value string) {

View File

@@ -9,6 +9,7 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/utils"
@@ -203,6 +204,7 @@ func (self *HandlerCreator) menuPrompt(prompt *config.CustomCommandPrompt, wrapp
OnPress: func() error {
return wrappedF(option.Value)
},
Key: keybindings.GetKey(option.Key),
}
})

View File

@@ -108,6 +108,7 @@ func (self *Resolver) resolveMenuOption(option *config.CustomCommandMenuOption,
Name: name,
Description: description,
Value: value,
Key: option.Key,
}, nil
}

View File

@@ -0,0 +1,65 @@
package custom_commands
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var MenuPromptWithKeys = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Using a custom command with menu options that have keybindings",
ExtraCmdArgs: []string{},
Skip: false,
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("initial commit")
},
SetupConfig: func(cfg *config.AppConfig) {
cfg.GetUserConfig().CustomCommands = []config.CustomCommand{
{
Key: "a",
Context: "files",
Command: `echo {{.Form.Choice | quote}} > result.txt`,
Prompts: []config.CustomCommandPrompt{
{
Key: "Choice",
Type: "menu",
Title: "Choose an option",
Options: []config.CustomCommandMenuOption{
{
Name: "first",
Description: "First option",
Value: "FIRST",
Key: "1",
},
{
Name: "second",
Description: "Second option",
Value: "SECOND",
Key: "H",
},
{
Name: "third",
Description: "Third option",
Value: "THIRD",
Key: "3",
},
},
},
},
},
}
},
Run: func(t *TestDriver, keys config.KeybindingConfig) {
t.Views().Files().
IsFocused().
Press("a")
t.ExpectPopup().Menu().
Title(Equals("Choose an option"))
// 'H' is normally a navigation key (ScrollLeft), so this tests that menu item
// keybindings have proper precedence over non-essential navigation keys
t.Views().Menu().Press("H")
t.FileSystem().FileContent("result.txt", Equals("SECOND\n"))
},
})

View File

@@ -175,6 +175,7 @@ var tests = []*components.IntegrationTest{
custom_commands.GlobalContext,
custom_commands.MenuFromCommand,
custom_commands.MenuFromCommandsOutput,
custom_commands.MenuPromptWithKeys,
custom_commands.MultipleContexts,
custom_commands.MultiplePrompts,
custom_commands.RunCommand,

View File

@@ -163,6 +163,10 @@
"examples": [
"feature"
]
},
"key": {
"type": "string",
"description": "Keybinding to invoke this menu option without needing to navigate to it"
}
},
"additionalProperties": false,