1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-11-22 04:42:37 +03:00
Files
lazygit/pkg/gui/popup/popup_handler.go
Stefan Haller b3435bd59c Add AllowEmptyInput flag to PromptOpts
Most of our prompts don't (shouldn't) allow empty input, but most callers didn't
check, and would run into cryptic errors when the user pressed enter at an empty
prompt (e.g. when creating a new branch). Now we simply don't allow hitting
enter in this case, and show an error toast instead.

This behavior is opt-out, because there are a few cases where empty input is
supported (e.g. creating a stash).
2025-11-15 15:36:43 +01:00

152 lines
4.7 KiB
Go

package popup
import (
"context"
"errors"
"strings"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/common"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/gui/types"
)
type PopupHandler struct {
*common.Common
createPopupPanelFn func(context.Context, types.CreatePopupPanelOpts)
onErrorFn func() error
popContextFn func()
currentContextFn func() types.Context
createMenuFn func(types.CreateMenuOptions) error
withWaitingStatusFn func(message string, f func(gocui.Task) error)
withWaitingStatusSyncFn func(message string, f func() error) error
toastFn func(message string, kind types.ToastKind)
getPromptInputFn func() string
inDemo func() bool
}
var _ types.IPopupHandler = &PopupHandler{}
func NewPopupHandler(
common *common.Common,
createPopupPanelFn func(context.Context, types.CreatePopupPanelOpts),
onErrorFn func() error,
popContextFn func(),
currentContextFn func() types.Context,
createMenuFn func(types.CreateMenuOptions) error,
withWaitingStatusFn func(message string, f func(gocui.Task) error),
withWaitingStatusSyncFn func(message string, f func() error) error,
toastFn func(message string, kind types.ToastKind),
getPromptInputFn func() string,
inDemo func() bool,
) *PopupHandler {
return &PopupHandler{
Common: common,
createPopupPanelFn: createPopupPanelFn,
onErrorFn: onErrorFn,
popContextFn: popContextFn,
currentContextFn: currentContextFn,
createMenuFn: createMenuFn,
withWaitingStatusFn: withWaitingStatusFn,
withWaitingStatusSyncFn: withWaitingStatusSyncFn,
toastFn: toastFn,
getPromptInputFn: getPromptInputFn,
inDemo: inDemo,
}
}
func (self *PopupHandler) Menu(opts types.CreateMenuOptions) error {
return self.createMenuFn(opts)
}
func (self *PopupHandler) Toast(message string) {
self.toastFn(message, types.ToastKindStatus)
}
func (self *PopupHandler) ErrorToast(message string) {
self.toastFn(message, types.ToastKindError)
}
func (self *PopupHandler) SetToastFunc(f func(string, types.ToastKind)) {
self.toastFn = f
}
func (self *PopupHandler) WithWaitingStatus(message string, f func(gocui.Task) error) error {
self.withWaitingStatusFn(message, f)
return nil
}
func (self *PopupHandler) WithWaitingStatusSync(message string, f func() error) error {
return self.withWaitingStatusSyncFn(message, f)
}
func (self *PopupHandler) ErrorHandler(err error) error {
var notHandledError *types.ErrKeybindingNotHandled
if errors.As(err, &notHandledError) {
if !notHandledError.DisabledReason.ShowErrorInPanel {
if msg := notHandledError.DisabledReason.Text; len(msg) > 0 {
self.ErrorToast(self.Tr.DisabledMenuItemPrefix + msg)
}
return nil
}
}
// Need to set bold here explicitly; otherwise it gets cancelled by the red colouring.
coloredMessage := style.FgRed.SetBold().Sprint(strings.TrimSpace(err.Error()))
if err := self.onErrorFn(); err != nil {
return err
}
self.Alert(self.Tr.Error, coloredMessage)
return nil
}
func (self *PopupHandler) Alert(title string, message string) {
self.Confirm(types.ConfirmOpts{Title: title, Prompt: message})
}
func (self *PopupHandler) Confirm(opts types.ConfirmOpts) {
self.createPopupPanelFn(context.Background(), types.CreatePopupPanelOpts{
Title: opts.Title,
Prompt: opts.Prompt,
HandleConfirm: opts.HandleConfirm,
HandleClose: opts.HandleClose,
})
}
func (self *PopupHandler) ConfirmIf(condition bool, opts types.ConfirmOpts) error {
if condition {
self.createPopupPanelFn(context.Background(), types.CreatePopupPanelOpts{
Title: opts.Title,
Prompt: opts.Prompt,
HandleConfirm: opts.HandleConfirm,
HandleClose: opts.HandleClose,
})
return nil
}
return opts.HandleConfirm()
}
func (self *PopupHandler) Prompt(opts types.PromptOpts) {
self.createPopupPanelFn(context.Background(), types.CreatePopupPanelOpts{
Title: opts.Title,
Prompt: opts.InitialContent,
Editable: true,
HandleConfirmPrompt: opts.HandleConfirm,
HandleClose: opts.HandleClose,
HandleDeleteSuggestion: opts.HandleDeleteSuggestion,
FindSuggestionsFunc: opts.FindSuggestionsFunc,
AllowEditSuggestion: opts.AllowEditSuggestion,
AllowEmptyInput: opts.AllowEmptyInput,
Mask: opts.Mask,
})
}
// returns the content that has currently been typed into the prompt. Useful for
// asynchronously updating the suggestions list under the prompt.
func (self *PopupHandler) GetPromptInput() string {
return self.getPromptInputFn()
}