1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-03-17 21:18:31 +02:00

refactor custom commands panel

This commit is contained in:
Jesse Duffield 2022-02-13 19:15:22 +11:00
parent c685a413c9
commit 85f2319897
2 changed files with 101 additions and 80 deletions

View File

@ -256,6 +256,13 @@ func (gui *Gui) handleToggleCommitFileDirCollapsed() error {
return nil
}
// NOTE: this is very similar to handleToggleFileTreeView, could be DRY'd with generics
func (gui *Gui) handleToggleCommitFileTreeView() error {
gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.ToggleShowTree()
return gui.c.PostRefreshUpdate(gui.State.Contexts.CommitFiles)
}
func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesContextOpts) error {
// sometimes the commitFiles view is already shown in another window, so we need to ensure that window
// no longer considers the commitFiles view as its main view.
@ -273,10 +280,3 @@ func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesC
return gui.c.PushContext(gui.State.Contexts.CommitFiles)
}
// NOTE: this is very similar to handleToggleFileTreeView, could be DRY'd with generics
func (gui *Gui) handleToggleCommitFileTreeView() error {
gui.State.Contexts.CommitFiles.CommitFileTreeViewModel.ToggleShowTree()
return gui.c.PostRefreshUpdate(gui.State.Contexts.CommitFiles)
}

View File

@ -40,7 +40,7 @@ type commandMenuEntry struct {
value string
}
func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (string, error) {
func (gui *Gui) getResolveTemplateFn(promptResponses []string) func(string) (string, error) {
objects := CustomCommandObjects{
SelectedFile: gui.getSelectedFile(),
SelectedPath: gui.getSelectedPath(),
@ -58,71 +58,101 @@ func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (s
PromptResponses: promptResponses,
}
return utils.ResolveTemplate(templateStr, objects)
return func(templateStr string) (string, error) { return utils.ResolveTemplate(templateStr, objects) }
}
func (gui *Gui) inputPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
func resolveCustomCommandPrompt(prompt *config.CustomCommandPrompt, resolveTemplate func(string) (string, error)) (*config.CustomCommandPrompt, error) {
var err error
result := &config.CustomCommandPrompt{}
result.Title, err = resolveTemplate(prompt.Title)
if err != nil {
return gui.c.Error(err)
return nil, err
}
initialValue, err := gui.resolveTemplate(prompt.InitialValue, promptResponses)
result.InitialValue, err = resolveTemplate(prompt.InitialValue)
if err != nil {
return gui.c.Error(err)
return nil, err
}
result.Command, err = resolveTemplate(prompt.Command)
if err != nil {
return nil, err
}
result.Filter, err = resolveTemplate(prompt.Filter)
if err != nil {
return nil, err
}
if len(prompt.Options) > 0 {
newOptions := make([]config.CustomCommandMenuOption, len(prompt.Options))
for _, option := range prompt.Options {
option := option
newOption, err := resolveMenuOption(&option, resolveTemplate)
if err != nil {
return nil, err
}
newOptions = append(newOptions, *newOption)
}
prompt.Options = newOptions
}
return result, nil
}
func resolveMenuOption(option *config.CustomCommandMenuOption, resolveTemplate func(string) (string, error)) (*config.CustomCommandMenuOption, error) {
nameTemplate := option.Name
if nameTemplate == "" {
// this allows you to only pass values rather than bother with names/descriptions
nameTemplate = option.Value
}
name, err := resolveTemplate(nameTemplate)
if err != nil {
return nil, err
}
description, err := resolveTemplate(option.Description)
if err != nil {
return nil, err
}
value, err := resolveTemplate(option.Value)
if err != nil {
return nil, err
}
return &config.CustomCommandMenuOption{
Name: name,
Description: description,
Value: value,
}, nil
}
func (gui *Gui) inputPrompt(prompt *config.CustomCommandPrompt, wrappedF func(string) error) error {
return gui.c.Prompt(types.PromptOpts{
Title: title,
InitialContent: initialValue,
Title: prompt.Title,
InitialContent: prompt.InitialValue,
HandleConfirm: func(str string) error {
promptResponses[responseIdx] = str
return wrappedF()
return wrappedF(str)
},
})
}
func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
// need to make a menu here some how
func (gui *Gui) menuPrompt(prompt *config.CustomCommandPrompt, wrappedF func(string) error) error {
menuItems := make([]*types.MenuItem, len(prompt.Options))
for i, option := range prompt.Options {
option := option
nameTemplate := option.Name
if nameTemplate == "" {
// this allows you to only pass values rather than bother with names/descriptions
nameTemplate = option.Value
}
name, err := gui.resolveTemplate(nameTemplate, promptResponses)
if err != nil {
return gui.c.Error(err)
}
description, err := gui.resolveTemplate(option.Description, promptResponses)
if err != nil {
return gui.c.Error(err)
}
value, err := gui.resolveTemplate(option.Value, promptResponses)
if err != nil {
return gui.c.Error(err)
}
menuItems[i] = &types.MenuItem{
DisplayStrings: []string{name, style.FgYellow.Sprint(description)},
DisplayStrings: []string{option.Name, style.FgYellow.Sprint(option.Description)},
OnPress: func() error {
promptResponses[responseIdx] = value
return wrappedF()
return wrappedF(option.Value)
},
}
}
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
if err != nil {
return gui.c.Error(err)
}
return gui.c.Menu(types.CreateMenuOptions{Title: title, Items: menuItems})
return gui.c.Menu(types.CreateMenuOptions{Title: prompt.Title, Items: menuItems})
}
func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, labelFormat string) ([]commandMenuEntry, error) {
@ -191,27 +221,15 @@ func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, label
return candidates, err
}
func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
// Collect cmd to run from config
cmdStr, err := gui.resolveTemplate(prompt.Command, promptResponses)
if err != nil {
return gui.c.Error(err)
}
// Collect Filter regexp
filter, err := gui.resolveTemplate(prompt.Filter, promptResponses)
if err != nil {
return gui.c.Error(err)
}
func (gui *Gui) menuPromptFromCommand(prompt *config.CustomCommandPrompt, wrappedF func(string) error) error {
// Run and save output
message, err := gui.git.Custom.RunWithOutput(cmdStr)
message, err := gui.git.Custom.RunWithOutput(prompt.Command)
if err != nil {
return gui.c.Error(err)
}
// Need to make a menu out of what the cmd has displayed
candidates, err := gui.GenerateMenuCandidates(message, filter, prompt.ValueFormat, prompt.LabelFormat)
candidates, err := gui.GenerateMenuCandidates(message, prompt.Filter, prompt.ValueFormat, prompt.LabelFormat)
if err != nil {
return gui.c.Error(err)
}
@ -222,18 +240,12 @@ func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptR
menuItems[i] = &types.MenuItem{
DisplayStrings: []string{candidates[i].label},
OnPress: func() error {
promptResponses[responseIdx] = candidates[i].value
return wrappedF()
return wrappedF(candidates[i].value)
},
}
}
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
if err != nil {
return gui.c.Error(err)
}
return gui.c.Menu(types.CreateMenuOptions{Title: title, Items: menuItems})
return gui.c.Menu(types.CreateMenuOptions{Title: prompt.Title, Items: menuItems})
}
func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand) func() error {
@ -241,7 +253,8 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
promptResponses := make([]string, len(customCommand.Prompts))
f := func() error {
cmdStr, err := gui.resolveTemplate(customCommand.Command, promptResponses)
resolveTemplate := gui.getResolveTemplateFn(promptResponses)
cmdStr, err := resolveTemplate(customCommand.Command)
if err != nil {
return gui.c.Error(err)
}
@ -254,6 +267,7 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
if loadingText == "" {
loadingText = gui.c.Tr.LcRunningCustomCommandStatus
}
return gui.c.WithWaitingStatus(loadingText, func() error {
gui.c.LogAction(gui.c.Tr.Actions.CustomCommand)
cmdObj := gui.os.Cmd.NewShell(cmdStr)
@ -276,26 +290,33 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
// going backwards so the outermost prompt is the first one
prompt := customCommand.Prompts[idx]
// need to do this because f's value will change with each iteration
wrappedF := f
wrappedF := func(response string) error {
promptResponses[idx] = response
return f()
}
resolveTemplate := gui.getResolveTemplateFn(promptResponses)
resolvedPrompt, err := resolveCustomCommandPrompt(&prompt, resolveTemplate)
if err != nil {
return gui.c.Error(err)
}
switch prompt.Type {
case "input":
f = func() error {
return gui.inputPrompt(prompt, promptResponses, idx, wrappedF)
return gui.inputPrompt(resolvedPrompt, wrappedF)
}
case "menu":
f = func() error {
return gui.menuPrompt(prompt, promptResponses, idx, wrappedF)
return gui.menuPrompt(resolvedPrompt, wrappedF)
}
case "menuFromCommand":
f = func() error {
return gui.menuPromptFromCommand(prompt, promptResponses, idx, wrappedF)
return gui.menuPromptFromCommand(resolvedPrompt, wrappedF)
}
default:
return gui.c.ErrorMsg("custom command prompt must have a type of 'input', 'menu' or 'menuFromCommand'")
}
}
return f()