2018-08-14 11:05:26 +02:00
|
|
|
package gui
|
2018-05-26 05:23:39 +02:00
|
|
|
|
|
|
|
import (
|
2021-10-23 02:25:37 +02:00
|
|
|
"fmt"
|
2018-08-05 14:00:02 +02:00
|
|
|
"strings"
|
2018-05-26 05:23:39 +02:00
|
|
|
|
2018-08-05 14:00:02 +02:00
|
|
|
"github.com/jesseduffield/gocui"
|
2022-01-29 10:15:46 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
2020-11-28 10:53:39 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
2019-10-18 09:48:37 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
2020-10-04 02:00:48 +02:00
|
|
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
2018-05-26 05:23:39 +02:00
|
|
|
)
|
|
|
|
|
2020-11-28 11:30:16 +02:00
|
|
|
func (gui *Gui) wrappedConfirmationFunction(handlersManageFocus bool, function func() error) func() error {
|
|
|
|
return func() error {
|
2022-01-15 03:04:00 +02:00
|
|
|
if err := gui.closeConfirmationPrompt(handlersManageFocus); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-08-05 14:00:02 +02:00
|
|
|
if function != nil {
|
2020-08-15 08:36:39 +02:00
|
|
|
if err := function(); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return gui.c.Error(err)
|
2020-08-15 08:36:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-23 05:09:36 +02:00
|
|
|
return nil
|
2020-08-15 08:36:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-28 11:30:16 +02:00
|
|
|
func (gui *Gui) wrappedPromptConfirmationFunction(handlersManageFocus bool, function func(string) error, getResponse func() string) func() error {
|
|
|
|
return func() error {
|
2022-01-15 03:04:00 +02:00
|
|
|
if err := gui.closeConfirmationPrompt(handlersManageFocus); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-08-15 08:36:39 +02:00
|
|
|
if function != nil {
|
2020-11-28 04:14:48 +02:00
|
|
|
if err := function(getResponse()); err != nil {
|
2022-01-16 05:46:53 +02:00
|
|
|
return gui.c.Error(err)
|
2018-08-05 14:00:02 +02:00
|
|
|
}
|
|
|
|
}
|
2019-11-17 03:02:39 +02:00
|
|
|
|
2020-08-23 05:09:36 +02:00
|
|
|
return nil
|
2020-11-28 11:30:16 +02:00
|
|
|
}
|
2018-05-27 08:32:09 +02:00
|
|
|
}
|
|
|
|
|
2020-08-23 05:09:36 +02:00
|
|
|
func (gui *Gui) closeConfirmationPrompt(handlersManageFocus bool) error {
|
2021-04-04 15:51:59 +02:00
|
|
|
// we've already closed it so we can just return
|
|
|
|
if !gui.Views.Confirmation.Visible {
|
|
|
|
return nil
|
2018-08-05 14:00:02 +02:00
|
|
|
}
|
2020-08-17 09:53:50 +02:00
|
|
|
|
2020-08-23 05:09:36 +02:00
|
|
|
if !handlersManageFocus {
|
|
|
|
if err := gui.returnFromContext(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-08-17 09:53:50 +02:00
|
|
|
|
2021-04-04 15:51:59 +02:00
|
|
|
gui.clearConfirmationViewKeyBindings()
|
|
|
|
gui.Views.Confirmation.Visible = false
|
|
|
|
gui.Views.Suggestions.Visible = false
|
2020-11-28 04:14:48 +02:00
|
|
|
|
2020-08-23 05:09:36 +02:00
|
|
|
return nil
|
2018-05-27 08:32:09 +02:00
|
|
|
}
|
|
|
|
|
2019-01-15 10:33:42 +02:00
|
|
|
func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int {
|
2018-08-05 14:00:02 +02:00
|
|
|
lines := strings.Split(message, "\n")
|
|
|
|
lineCount := 0
|
2018-12-26 13:39:16 +02:00
|
|
|
// if we need to wrap, calculate height to fit content within view's width
|
2019-01-15 10:33:42 +02:00
|
|
|
if wrap {
|
2018-12-26 13:39:16 +02:00
|
|
|
for _, line := range lines {
|
|
|
|
lineCount += len(line)/width + 1
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lineCount = len(lines)
|
2018-08-05 14:00:02 +02:00
|
|
|
}
|
|
|
|
return lineCount
|
2018-05-26 05:23:39 +02:00
|
|
|
}
|
|
|
|
|
2020-08-15 09:23:16 +02:00
|
|
|
func (gui *Gui) getConfirmationPanelDimensions(wrap bool, prompt string) (int, int, int, int) {
|
|
|
|
width, height := gui.g.Size()
|
2020-08-12 13:48:29 +02:00
|
|
|
// we want a minimum width up to a point, then we do it based on ratio.
|
2019-11-10 13:07:45 +02:00
|
|
|
panelWidth := 4 * width / 7
|
2020-08-12 13:48:29 +02:00
|
|
|
minWidth := 80
|
|
|
|
if panelWidth < minWidth {
|
|
|
|
if width-2 < minWidth {
|
|
|
|
panelWidth = width - 2
|
|
|
|
} else {
|
|
|
|
panelWidth = minWidth
|
|
|
|
}
|
|
|
|
}
|
2019-01-15 10:33:42 +02:00
|
|
|
panelHeight := gui.getMessageHeight(wrap, prompt, panelWidth)
|
2020-03-26 12:39:59 +02:00
|
|
|
if panelHeight > height*3/4 {
|
|
|
|
panelHeight = height * 3 / 4
|
|
|
|
}
|
2018-08-05 14:00:02 +02:00
|
|
|
return width/2 - panelWidth/2,
|
|
|
|
height/2 - panelHeight/2 - panelHeight%2 - 1,
|
|
|
|
width/2 + panelWidth/2,
|
|
|
|
height/2 + panelHeight/2
|
2018-05-26 05:23:39 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 02:25:37 +02:00
|
|
|
func (gui *Gui) prepareConfirmationPanel(
|
|
|
|
title,
|
|
|
|
prompt string,
|
|
|
|
hasLoader bool,
|
|
|
|
findSuggestionsFunc func(string) []*types.Suggestion,
|
|
|
|
editable bool,
|
|
|
|
) error {
|
2020-08-15 09:23:16 +02:00
|
|
|
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
|
2021-04-04 16:06:20 +02:00
|
|
|
// calling SetView on an existing view returns the same view, so I'm not bothering
|
|
|
|
// to reassign to gui.Views.Confirmation
|
|
|
|
_, err := gui.g.SetView("confirmation", x0, y0, x1, y1, 0)
|
2018-08-25 07:55:49 +02:00
|
|
|
if err != nil {
|
2021-04-04 16:06:20 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
gui.Views.Confirmation.HasLoader = hasLoader
|
|
|
|
if hasLoader {
|
|
|
|
gui.g.StartTicking()
|
2018-08-05 14:00:02 +02:00
|
|
|
}
|
2021-04-04 16:06:20 +02:00
|
|
|
gui.Views.Confirmation.Title = title
|
2021-10-17 04:00:44 +02:00
|
|
|
// for now we do not support wrapping in our editor
|
|
|
|
gui.Views.Confirmation.Wrap = !editable
|
2021-04-04 16:06:20 +02:00
|
|
|
gui.Views.Confirmation.FgColor = theme.GocuiDefaultTextColor
|
2020-11-28 04:14:48 +02:00
|
|
|
|
2020-11-28 11:01:45 +02:00
|
|
|
gui.findSuggestions = findSuggestionsFunc
|
|
|
|
if findSuggestionsFunc != nil {
|
2020-11-28 04:14:48 +02:00
|
|
|
suggestionsViewHeight := 11
|
2021-10-23 02:25:37 +02:00
|
|
|
suggestionsView, err := gui.g.SetView("suggestions", x0, y1+1, x1, y1+suggestionsViewHeight, 0)
|
2020-11-28 04:14:48 +02:00
|
|
|
if err != nil {
|
2021-04-04 16:06:20 +02:00
|
|
|
return err
|
2020-11-28 04:14:48 +02:00
|
|
|
}
|
2021-10-18 10:00:03 +02:00
|
|
|
suggestionsView.Wrap = false
|
2021-04-04 16:06:20 +02:00
|
|
|
suggestionsView.FgColor = theme.GocuiDefaultTextColor
|
2021-10-23 02:25:37 +02:00
|
|
|
gui.setSuggestions(findSuggestionsFunc(""))
|
2021-04-04 15:51:59 +02:00
|
|
|
suggestionsView.Visible = true
|
2022-01-16 05:46:53 +02:00
|
|
|
suggestionsView.Title = fmt.Sprintf(gui.c.Tr.SuggestionsTitle, gui.c.UserConfig.Keybinding.Universal.TogglePanel)
|
2020-11-28 04:14:48 +02:00
|
|
|
}
|
|
|
|
|
2021-04-04 16:06:20 +02:00
|
|
|
return nil
|
2018-05-27 08:32:09 +02:00
|
|
|
}
|
|
|
|
|
2022-01-29 10:15:46 +02:00
|
|
|
func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
|
2022-01-15 03:04:00 +02:00
|
|
|
// remove any previous keybindings
|
|
|
|
gui.clearConfirmationViewKeyBindings()
|
|
|
|
|
|
|
|
err := gui.prepareConfirmationPanel(
|
2022-01-28 11:44:36 +02:00
|
|
|
opts.Title,
|
|
|
|
opts.Prompt,
|
|
|
|
opts.HasLoader,
|
|
|
|
opts.FindSuggestionsFunc,
|
|
|
|
opts.Editable,
|
2022-01-15 03:04:00 +02:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
confirmationView := gui.Views.Confirmation
|
2022-01-28 11:44:36 +02:00
|
|
|
confirmationView.Editable = opts.Editable
|
2022-01-15 03:04:00 +02:00
|
|
|
confirmationView.Editor = gocui.EditorFunc(gui.defaultEditor)
|
|
|
|
|
2022-01-28 11:44:36 +02:00
|
|
|
if opts.Editable {
|
2022-01-15 03:04:00 +02:00
|
|
|
textArea := confirmationView.TextArea
|
|
|
|
textArea.Clear()
|
2022-01-28 11:44:36 +02:00
|
|
|
textArea.TypeString(opts.Prompt)
|
2022-01-15 03:04:00 +02:00
|
|
|
confirmationView.RenderTextArea()
|
|
|
|
} else {
|
2022-01-28 11:44:36 +02:00
|
|
|
if err := gui.renderString(confirmationView, opts.Prompt); err != nil {
|
2018-08-25 07:55:49 +02:00
|
|
|
return err
|
2018-08-05 14:00:02 +02:00
|
|
|
}
|
2022-01-15 03:04:00 +02:00
|
|
|
}
|
2019-11-17 08:58:24 +02:00
|
|
|
|
2022-01-15 03:04:00 +02:00
|
|
|
if err := gui.setKeyBindings(opts); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
return gui.c.PushContext(gui.State.Contexts.Confirmation)
|
2018-06-05 10:47:57 +02:00
|
|
|
}
|
|
|
|
|
2022-01-29 10:15:46 +02:00
|
|
|
func (gui *Gui) setKeyBindings(opts types.CreatePopupPanelOpts) error {
|
2020-10-04 02:00:48 +02:00
|
|
|
actions := utils.ResolvePlaceholderString(
|
2022-01-16 05:46:53 +02:00
|
|
|
gui.c.Tr.CloseConfirm,
|
2020-10-04 02:00:48 +02:00
|
|
|
map[string]string{
|
2018-08-15 10:53:05 +02:00
|
|
|
"keyBindClose": "esc",
|
|
|
|
"keyBindConfirm": "enter",
|
|
|
|
},
|
|
|
|
)
|
2020-03-26 12:39:59 +02:00
|
|
|
|
2022-01-15 03:04:00 +02:00
|
|
|
_ = gui.renderString(gui.Views.Options, actions)
|
2020-11-28 11:30:16 +02:00
|
|
|
var onConfirm func() error
|
2022-01-28 11:44:36 +02:00
|
|
|
if opts.HandleConfirmPrompt != nil {
|
|
|
|
onConfirm = gui.wrappedPromptConfirmationFunction(opts.HandlersManageFocus, opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
2020-08-15 08:36:39 +02:00
|
|
|
} else {
|
2022-01-28 11:44:36 +02:00
|
|
|
onConfirm = gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleConfirm)
|
2020-08-17 09:53:50 +02:00
|
|
|
}
|
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
keybindingConfig := gui.c.UserConfig.Keybinding
|
2021-10-23 02:25:37 +02:00
|
|
|
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
|
2022-01-28 11:44:36 +02:00
|
|
|
opts.HandlersManageFocus,
|
|
|
|
opts.HandleConfirmPrompt,
|
2021-10-23 02:25:37 +02:00
|
|
|
gui.getSelectedSuggestionValue,
|
|
|
|
)
|
2020-11-28 04:14:48 +02:00
|
|
|
|
2022-01-29 10:09:20 +02:00
|
|
|
bindings := []*types.Binding{
|
2020-11-28 11:30:16 +02:00
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "confirmation",
|
2022-01-29 10:15:46 +02:00
|
|
|
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.Confirm),
|
|
|
|
Handler: onConfirm,
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "confirmation",
|
2022-01-29 10:15:46 +02:00
|
|
|
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.ConfirmAlt1),
|
|
|
|
Handler: onConfirm,
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "confirmation",
|
2022-01-29 10:15:46 +02:00
|
|
|
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.Return),
|
|
|
|
Handler: gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleClose),
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "confirmation",
|
2022-01-29 10:15:46 +02:00
|
|
|
Contexts: []string{string(context.CONFIRMATION_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.TogglePanel),
|
|
|
|
Handler: func() error {
|
2021-10-17 08:49:32 +02:00
|
|
|
if len(gui.State.Suggestions) > 0 {
|
|
|
|
return gui.replaceContext(gui.State.Contexts.Suggestions)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "suggestions",
|
2022-01-30 11:34:59 +02:00
|
|
|
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.Confirm),
|
|
|
|
Handler: onSuggestionConfirm,
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "suggestions",
|
2022-01-30 11:34:59 +02:00
|
|
|
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.ConfirmAlt1),
|
|
|
|
Handler: onSuggestionConfirm,
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "suggestions",
|
2022-01-30 11:34:59 +02:00
|
|
|
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.Return),
|
|
|
|
Handler: gui.wrappedConfirmationFunction(opts.HandlersManageFocus, opts.HandleClose),
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
|
|
|
{
|
2022-01-29 10:09:20 +02:00
|
|
|
ViewName: "suggestions",
|
2022-01-30 11:34:59 +02:00
|
|
|
Contexts: []string{string(context.SUGGESTIONS_CONTEXT_KEY)},
|
2022-01-29 10:09:20 +02:00
|
|
|
Key: gui.getKey(keybindingConfig.Universal.TogglePanel),
|
|
|
|
Handler: func() error { return gui.replaceContext(gui.State.Contexts.Confirmation) },
|
2020-11-28 11:30:16 +02:00
|
|
|
},
|
2020-11-28 04:14:48 +02:00
|
|
|
}
|
|
|
|
|
2022-01-29 10:09:20 +02:00
|
|
|
for _, binding := range bindings {
|
|
|
|
if err := gui.SetKeybinding(binding); err != nil {
|
2020-11-28 11:30:16 +02:00
|
|
|
return err
|
|
|
|
}
|
2020-11-28 04:14:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-08-15 08:36:39 +02:00
|
|
|
}
|
|
|
|
|
2021-10-23 02:25:37 +02:00
|
|
|
func (gui *Gui) clearConfirmationViewKeyBindings() {
|
2022-01-16 05:46:53 +02:00
|
|
|
keybindingConfig := gui.c.UserConfig.Keybinding
|
2021-10-23 02:25:37 +02:00
|
|
|
_ = gui.g.DeleteKeybinding("confirmation", gui.getKey(keybindingConfig.Universal.Confirm), gocui.ModNone)
|
|
|
|
_ = gui.g.DeleteKeybinding("confirmation", gui.getKey(keybindingConfig.Universal.ConfirmAlt1), gocui.ModNone)
|
|
|
|
_ = gui.g.DeleteKeybinding("confirmation", gui.getKey(keybindingConfig.Universal.Return), gocui.ModNone)
|
|
|
|
_ = gui.g.DeleteKeybinding("suggestions", gui.getKey(keybindingConfig.Universal.Confirm), gocui.ModNone)
|
|
|
|
_ = gui.g.DeleteKeybinding("suggestions", gui.getKey(keybindingConfig.Universal.ConfirmAlt1), gocui.ModNone)
|
|
|
|
_ = gui.g.DeleteKeybinding("suggestions", gui.getKey(keybindingConfig.Universal.Return), gocui.ModNone)
|
|
|
|
}
|
|
|
|
|
2022-01-16 05:46:53 +02:00
|
|
|
func (gui *Gui) refreshSuggestions() {
|
|
|
|
gui.suggestionsAsyncHandler.Do(func() func() {
|
|
|
|
suggestions := gui.findSuggestions(gui.c.GetPromptInput())
|
|
|
|
return func() { gui.setSuggestions(suggestions) }
|
|
|
|
})
|
|
|
|
}
|