mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-10-08 22:52:12 +02:00
Make it possible to rebind the Confirm keybinding (#4860)
### PR Description Remapping `keybinding.universal.confirm` from `<enter>` to something like `y` is currently impossible because the same keybinding is also used to confirm prompts (e.g. "New branch") and the search prompt. Fix this by hard-coding enter for those; it doesn't really make sense to use any other key for prompts. While at it, add separate bindings for `confirmMenu` and `confirmSuggestion` for those who would like to have different keys for these. Of these, `confirmMenu` could be a little tricky because menus are sometimes used purely as a choice (e.g. in "Amend commit attribute" or the global keybindings menu), in which case you might want to use `<enter>`, but other times as a substitute for a confirmation (e.g. for "Delete branch"), in which case you might want to remap to `y`. I don't have a great idea what to do about that, to be honest. Feedback welcome. In this PR we only take care of Confirm, which many people seem to be concerned about. We might consider doing something similar for Esc, but it seems less urgent, and I'm out of time now. 😄 This seemingly simple change required some serious refactoring under the hood, so thorough testing would be good to ensure we didn't break anything. Closes #2611 Closes #2767 Closes #3471 Related: #2768
This commit is contained in:
@@ -557,6 +557,8 @@ keybinding:
|
|||||||
select: <space>
|
select: <space>
|
||||||
goInto: <enter>
|
goInto: <enter>
|
||||||
confirm: <enter>
|
confirm: <enter>
|
||||||
|
confirmMenu: <enter>
|
||||||
|
confirmSuggestion: <enter>
|
||||||
confirmInEditor: <a-enter>
|
confirmInEditor: <a-enter>
|
||||||
confirmInEditor-alt: <c-s>
|
confirmInEditor-alt: <c-s>
|
||||||
remove: d
|
remove: d
|
||||||
|
@@ -160,6 +160,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` 0 `` | Focus main view | |
|
| `` 0 `` | Focus main view | |
|
||||||
| `` / `` | Search the current view by text | |
|
| `` / `` | Search the current view by text | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | Confirm | |
|
||||||
|
| `` <esc> `` | Close/Cancel | |
|
||||||
|
|
||||||
## Local branches
|
## Local branches
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -52,6 +52,13 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味
|
|||||||
| `` ] `` | 次のタブ | |
|
| `` ] `` | 次のタブ | |
|
||||||
| `` [ `` | 前のタブ | |
|
| `` [ `` | 前のタブ | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | 確認 | |
|
||||||
|
| `` <esc> `` | 閉じる/キャンセル | |
|
||||||
|
|
||||||
## コミット
|
## コミット
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -52,6 +52,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` ] `` | 이전 탭 | |
|
| `` ] `` | 이전 탭 | |
|
||||||
| `` [ `` | 다음 탭 | |
|
| `` [ `` | 다음 탭 | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | 확인 | |
|
||||||
|
| `` <esc> `` | 닫기/취소 | |
|
||||||
|
|
||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -190,6 +190,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` w `` | View worktree options | |
|
| `` w `` | View worktree options | |
|
||||||
| `` / `` | Start met zoeken | |
|
| `` / `` | Start met zoeken | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | Bevestig | |
|
||||||
|
| `` <esc> `` | Sluiten | |
|
||||||
|
|
||||||
## Menu
|
## Menu
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -125,6 +125,13 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_
|
|||||||
| `` <esc> `` | Wyjdź z budowniczego niestandardowej łatki | |
|
| `` <esc> `` | Wyjdź z budowniczego niestandardowej łatki | |
|
||||||
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
| `` / `` | Szukaj w bieżącym widoku po tekście | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | Potwierdź | |
|
||||||
|
| `` <esc> `` | Zamknij/Anuluj | |
|
||||||
|
|
||||||
## Lokalne gałęzie
|
## Lokalne gałęzie
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -218,6 +218,13 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
|||||||
| `` w `` | View worktree options | |
|
| `` w `` | View worktree options | |
|
||||||
| `` / `` | Filter the current view by text | |
|
| `` / `` | Filter the current view by text | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | Confirmar | |
|
||||||
|
| `` <esc> `` | Fechar/Cancelar | |
|
||||||
|
|
||||||
## Menu
|
## Menu
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -52,6 +52,13 @@ _Связки клавиш_
|
|||||||
| `` ] `` | Следующая вкладка | |
|
| `` ] `` | Следующая вкладка | |
|
||||||
| `` [ `` | Предыдущая вкладка | |
|
| `` [ `` | Предыдущая вкладка | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | Подтвердить | |
|
||||||
|
| `` <esc> `` | Закрыть/отменить | |
|
||||||
|
|
||||||
## Worktrees
|
## Worktrees
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -52,6 +52,13 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_
|
|||||||
| `` ] `` | 下一个标签 | |
|
| `` ] `` | 下一个标签 | |
|
||||||
| `` [ `` | 上一个标签 | |
|
| `` [ `` | 上一个标签 | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | 确认 | |
|
||||||
|
| `` <esc> `` | 关闭 | |
|
||||||
|
|
||||||
## Reflog
|
## Reflog
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -52,6 +52,13 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B
|
|||||||
| `` ] `` | 下一個索引標籤 | |
|
| `` ] `` | 下一個索引標籤 | |
|
||||||
| `` [ `` | 上一個索引標籤 | |
|
| `` [ `` | 上一個索引標籤 | |
|
||||||
|
|
||||||
|
## Input prompt
|
||||||
|
|
||||||
|
| Key | Action | Info |
|
||||||
|
|-----|--------|-------------|
|
||||||
|
| `` <enter> `` | 確認 | |
|
||||||
|
| `` <esc> `` | 關閉/取消 | |
|
||||||
|
|
||||||
## 主面板 (補丁生成)
|
## 主面板 (補丁生成)
|
||||||
|
|
||||||
| Key | Action | Info |
|
| Key | Action | Info |
|
||||||
|
@@ -116,6 +116,7 @@ func localisedTitle(tr *i18n.TranslationSet, str string) string {
|
|||||||
"commitDescription": tr.CommitDescriptionTitle,
|
"commitDescription": tr.CommitDescriptionTitle,
|
||||||
"commits": tr.CommitsTitle,
|
"commits": tr.CommitsTitle,
|
||||||
"confirmation": tr.ConfirmationTitle,
|
"confirmation": tr.ConfirmationTitle,
|
||||||
|
"prompt": tr.PromptTitle,
|
||||||
"information": tr.InformationTitle,
|
"information": tr.InformationTitle,
|
||||||
"main": tr.NormalTitle,
|
"main": tr.NormalTitle,
|
||||||
"patchBuilding": tr.PatchBuildingTitle,
|
"patchBuilding": tr.PatchBuildingTitle,
|
||||||
|
@@ -425,6 +425,8 @@ type KeybindingUniversalConfig struct {
|
|||||||
Select string `yaml:"select"`
|
Select string `yaml:"select"`
|
||||||
GoInto string `yaml:"goInto"`
|
GoInto string `yaml:"goInto"`
|
||||||
Confirm string `yaml:"confirm"`
|
Confirm string `yaml:"confirm"`
|
||||||
|
ConfirmMenu string `yaml:"confirmMenu"`
|
||||||
|
ConfirmSuggestion string `yaml:"confirmSuggestion"`
|
||||||
ConfirmInEditor string `yaml:"confirmInEditor"`
|
ConfirmInEditor string `yaml:"confirmInEditor"`
|
||||||
ConfirmInEditorAlt string `yaml:"confirmInEditor-alt"`
|
ConfirmInEditorAlt string `yaml:"confirmInEditor-alt"`
|
||||||
Remove string `yaml:"remove"`
|
Remove string `yaml:"remove"`
|
||||||
@@ -889,6 +891,8 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
Select: "<space>",
|
Select: "<space>",
|
||||||
GoInto: "<enter>",
|
GoInto: "<enter>",
|
||||||
Confirm: "<enter>",
|
Confirm: "<enter>",
|
||||||
|
ConfirmMenu: "<enter>",
|
||||||
|
ConfirmSuggestion: "<enter>",
|
||||||
ConfirmInEditor: "<a-enter>",
|
ConfirmInEditor: "<a-enter>",
|
||||||
ConfirmInEditorAlt: "<c-s>",
|
ConfirmInEditorAlt: "<c-s>",
|
||||||
Remove: "d",
|
Remove: "d",
|
||||||
|
@@ -41,6 +41,7 @@ const (
|
|||||||
|
|
||||||
MENU_CONTEXT_KEY types.ContextKey = "menu"
|
MENU_CONTEXT_KEY types.ContextKey = "menu"
|
||||||
CONFIRMATION_CONTEXT_KEY types.ContextKey = "confirmation"
|
CONFIRMATION_CONTEXT_KEY types.ContextKey = "confirmation"
|
||||||
|
PROMPT_CONTEXT_KEY types.ContextKey = "prompt"
|
||||||
SEARCH_CONTEXT_KEY types.ContextKey = "search"
|
SEARCH_CONTEXT_KEY types.ContextKey = "search"
|
||||||
COMMIT_MESSAGE_CONTEXT_KEY types.ContextKey = "commitMessage"
|
COMMIT_MESSAGE_CONTEXT_KEY types.ContextKey = "commitMessage"
|
||||||
COMMIT_DESCRIPTION_CONTEXT_KEY types.ContextKey = "commitDescription"
|
COMMIT_DESCRIPTION_CONTEXT_KEY types.ContextKey = "commitDescription"
|
||||||
@@ -73,6 +74,7 @@ var AllContextKeys = []types.ContextKey{
|
|||||||
|
|
||||||
MENU_CONTEXT_KEY,
|
MENU_CONTEXT_KEY,
|
||||||
CONFIRMATION_CONTEXT_KEY,
|
CONFIRMATION_CONTEXT_KEY,
|
||||||
|
PROMPT_CONTEXT_KEY,
|
||||||
SEARCH_CONTEXT_KEY,
|
SEARCH_CONTEXT_KEY,
|
||||||
COMMIT_MESSAGE_CONTEXT_KEY,
|
COMMIT_MESSAGE_CONTEXT_KEY,
|
||||||
SUBMODULES_CONTEXT_KEY,
|
SUBMODULES_CONTEXT_KEY,
|
||||||
@@ -106,6 +108,7 @@ type ContextTree struct {
|
|||||||
CustomPatchBuilderSecondary types.Context
|
CustomPatchBuilderSecondary types.Context
|
||||||
MergeConflicts *MergeConflictsContext
|
MergeConflicts *MergeConflictsContext
|
||||||
Confirmation *ConfirmationContext
|
Confirmation *ConfirmationContext
|
||||||
|
Prompt *PromptContext
|
||||||
CommitMessage *CommitMessageContext
|
CommitMessage *CommitMessageContext
|
||||||
CommitDescription types.Context
|
CommitDescription types.Context
|
||||||
CommandLog types.Context
|
CommandLog types.Context
|
||||||
@@ -141,6 +144,7 @@ func (self *ContextTree) Flatten() []types.Context {
|
|||||||
self.Stash,
|
self.Stash,
|
||||||
self.Menu,
|
self.Menu,
|
||||||
self.Confirmation,
|
self.Confirmation,
|
||||||
|
self.Prompt,
|
||||||
self.CommitMessage,
|
self.CommitMessage,
|
||||||
self.CommitDescription,
|
self.CommitDescription,
|
||||||
|
|
||||||
|
30
pkg/gui/context/prompt_context.go
Normal file
30
pkg/gui/context/prompt_context.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package context
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PromptContext struct {
|
||||||
|
*SimpleContext
|
||||||
|
c *ContextCommon
|
||||||
|
|
||||||
|
State ConfirmationContextState
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ types.Context = (*PromptContext)(nil)
|
||||||
|
|
||||||
|
func NewPromptContext(
|
||||||
|
c *ContextCommon,
|
||||||
|
) *PromptContext {
|
||||||
|
return &PromptContext{
|
||||||
|
c: c,
|
||||||
|
SimpleContext: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||||
|
View: c.Views().Prompt,
|
||||||
|
WindowName: "prompt",
|
||||||
|
Key: PROMPT_CONTEXT_KEY,
|
||||||
|
Kind: types.TEMPORARY_POPUP,
|
||||||
|
Focusable: true,
|
||||||
|
HasUncontrolledBounds: true,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
@@ -84,6 +84,7 @@ func NewContextTree(c *ContextCommon) *ContextTree {
|
|||||||
c,
|
c,
|
||||||
),
|
),
|
||||||
Confirmation: NewConfirmationContext(c),
|
Confirmation: NewConfirmationContext(c),
|
||||||
|
Prompt: NewPromptContext(c),
|
||||||
CommitMessage: NewCommitMessageContext(c),
|
CommitMessage: NewCommitMessageContext(c),
|
||||||
CommitDescription: NewSimpleContext(
|
CommitDescription: NewSimpleContext(
|
||||||
NewBaseContext(NewBaseContextOpts{
|
NewBaseContext(NewBaseContextOpts{
|
||||||
|
@@ -193,6 +193,7 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
statusController := controllers.NewStatusController(common)
|
statusController := controllers.NewStatusController(common)
|
||||||
commandLogController := controllers.NewCommandLogController(common)
|
commandLogController := controllers.NewCommandLogController(common)
|
||||||
confirmationController := controllers.NewConfirmationController(common)
|
confirmationController := controllers.NewConfirmationController(common)
|
||||||
|
promptController := controllers.NewPromptController(common)
|
||||||
suggestionsController := controllers.NewSuggestionsController(common)
|
suggestionsController := controllers.NewSuggestionsController(common)
|
||||||
jumpToSideWindowController := controllers.NewJumpToSideWindowController(common, gui.handleNextTab)
|
jumpToSideWindowController := controllers.NewJumpToSideWindowController(common, gui.handleNextTab)
|
||||||
|
|
||||||
@@ -399,6 +400,10 @@ func (gui *Gui) resetHelpersAndControllers() {
|
|||||||
confirmationController,
|
confirmationController,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
controllers.AttachControllers(gui.State.Contexts.Prompt,
|
||||||
|
promptController,
|
||||||
|
)
|
||||||
|
|
||||||
controllers.AttachControllers(gui.State.Contexts.Suggestions,
|
controllers.AttachControllers(gui.State.Contexts.Suggestions,
|
||||||
suggestionsController,
|
suggestionsController,
|
||||||
)
|
)
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jesseduffield/gocui"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
)
|
)
|
||||||
@@ -38,46 +35,20 @@ func (self *ConfirmationController) GetKeybindings(opts types.KeybindingsOpts) [
|
|||||||
Description: self.c.Tr.CloseCancel,
|
Description: self.c.Tr.CloseCancel,
|
||||||
DisplayOnScreen: true,
|
DisplayOnScreen: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
|
||||||
Handler: func() error {
|
|
||||||
if len(self.c.Contexts().Suggestions.State.Suggestions) > 0 {
|
|
||||||
self.switchToSuggestions()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
Key: opts.GetKey(opts.Config.Universal.CopyToClipboard),
|
||||||
Handler: self.handleCopyToClipboard,
|
Handler: self.handleCopyToClipboard,
|
||||||
Description: self.c.Tr.CopyToClipboardMenu,
|
Description: self.c.Tr.CopyToClipboardMenu,
|
||||||
DisplayOnScreen: true,
|
DisplayOnScreen: true,
|
||||||
GetDisabledReason: self.copyToClipboardEnabled,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return bindings
|
return bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfirmationController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
|
||||||
return []*gocui.ViewMouseBinding{
|
|
||||||
{
|
|
||||||
ViewName: self.c.Contexts().Suggestions.GetViewName(),
|
|
||||||
FocusedView: self.c.Contexts().Confirmation.GetViewName(),
|
|
||||||
Key: gocui.MouseLeft,
|
|
||||||
Handler: func(gocui.ViewMouseBindingOpts) error {
|
|
||||||
self.switchToSuggestions()
|
|
||||||
// Let it fall through to the ListController's click handler so that
|
|
||||||
// the clicked line gets selected:
|
|
||||||
return gocui.ErrKeybindingNotHandled
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ConfirmationController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
func (self *ConfirmationController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
||||||
return func(types.OnFocusLostOpts) {
|
return func(types.OnFocusLostOpts) {
|
||||||
self.c.Helpers().Confirmation.DeactivateConfirmationPrompt()
|
self.c.Helpers().Confirmation.DeactivateConfirmation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,18 +60,6 @@ func (self *ConfirmationController) context() *context.ConfirmationContext {
|
|||||||
return self.c.Contexts().Confirmation
|
return self.c.Contexts().Confirmation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfirmationController) switchToSuggestions() {
|
|
||||||
subtitle := ""
|
|
||||||
if self.c.State().GetRepoState().GetCurrentPopupOpts().HandleDeleteSuggestion != nil {
|
|
||||||
// We assume that whenever things are deletable, they
|
|
||||||
// are also editable, so we show both keybindings
|
|
||||||
subtitle = fmt.Sprintf(self.c.Tr.SuggestionsSubtitle,
|
|
||||||
self.c.UserConfig().Keybinding.Universal.Remove, self.c.UserConfig().Keybinding.Universal.Edit)
|
|
||||||
}
|
|
||||||
self.c.Views().Suggestions.Subtitle = subtitle
|
|
||||||
self.c.Context().Replace(self.c.Contexts().Suggestions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *ConfirmationController) handleCopyToClipboard() error {
|
func (self *ConfirmationController) handleCopyToClipboard() error {
|
||||||
confirmationView := self.c.Views().Confirmation
|
confirmationView := self.c.Views().Confirmation
|
||||||
text := confirmationView.Buffer()
|
text := confirmationView.Buffer()
|
||||||
@@ -111,12 +70,3 @@ func (self *ConfirmationController) handleCopyToClipboard() error {
|
|||||||
self.c.Toast(self.c.Tr.MessageCopiedToClipboard)
|
self.c.Toast(self.c.Tr.MessageCopiedToClipboard)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfirmationController) copyToClipboardEnabled() *types.DisabledReason {
|
|
||||||
if self.c.Views().Confirmation.Editable {
|
|
||||||
// The empty text is intentional. We don't want to get a toast when invoking this, we only
|
|
||||||
// want to prevent it from showing up in the options bar.
|
|
||||||
return &types.DisabledReason{Text: ""}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@@ -46,17 +46,27 @@ func (self *ConfirmationHelper) wrappedPromptConfirmationFunction(cancel goConte
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfirmationHelper) DeactivateConfirmationPrompt() {
|
func (self *ConfirmationHelper) DeactivateConfirmation() {
|
||||||
self.c.Mutexes().PopupMutex.Lock()
|
self.c.Mutexes().PopupMutex.Lock()
|
||||||
self.c.State().GetRepoState().SetCurrentPopupOpts(nil)
|
self.c.State().GetRepoState().SetCurrentPopupOpts(nil)
|
||||||
self.c.Mutexes().PopupMutex.Unlock()
|
self.c.Mutexes().PopupMutex.Unlock()
|
||||||
|
|
||||||
self.c.Views().Confirmation.Visible = false
|
self.c.Views().Confirmation.Visible = false
|
||||||
self.c.Views().Suggestions.Visible = false
|
|
||||||
|
|
||||||
self.clearConfirmationViewKeyBindings()
|
self.clearConfirmationViewKeyBindings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) DeactivatePrompt() {
|
||||||
|
self.c.Mutexes().PopupMutex.Lock()
|
||||||
|
self.c.State().GetRepoState().SetCurrentPopupOpts(nil)
|
||||||
|
self.c.Mutexes().PopupMutex.Unlock()
|
||||||
|
|
||||||
|
self.c.Views().Prompt.Visible = false
|
||||||
|
self.c.Views().Suggestions.Visible = false
|
||||||
|
|
||||||
|
self.clearPromptViewKeyBindings()
|
||||||
|
}
|
||||||
|
|
||||||
func getMessageHeight(wrap bool, editable bool, message string, width int, tabWidth int) int {
|
func getMessageHeight(wrap bool, editable bool, message string, width int, tabWidth int) int {
|
||||||
wrappedLines, _, _ := utils.WrapViewLinesToWidth(wrap, editable, message, width, tabWidth)
|
wrappedLines, _, _ := utils.WrapViewLinesToWidth(wrap, editable, message, width, tabWidth)
|
||||||
return len(wrappedLines)
|
return len(wrappedLines)
|
||||||
@@ -105,15 +115,28 @@ func (self *ConfirmationHelper) prepareConfirmationPanel(
|
|||||||
opts types.ConfirmOpts,
|
opts types.ConfirmOpts,
|
||||||
) {
|
) {
|
||||||
self.c.Views().Confirmation.Title = opts.Title
|
self.c.Views().Confirmation.Title = opts.Title
|
||||||
// for now we do not support wrapping in our editor
|
|
||||||
self.c.Views().Confirmation.Wrap = !opts.Editable
|
|
||||||
self.c.Views().Confirmation.FgColor = theme.GocuiDefaultTextColor
|
self.c.Views().Confirmation.FgColor = theme.GocuiDefaultTextColor
|
||||||
self.c.Views().Confirmation.Mask = runeForMask(opts.Mask)
|
|
||||||
self.c.Views().Confirmation.SetOrigin(0, 0)
|
|
||||||
|
|
||||||
|
self.c.ResetViewOrigin(self.c.Views().Confirmation)
|
||||||
|
self.c.SetViewContent(self.c.Views().Confirmation, style.AttrBold.Sprint(strings.TrimSpace(opts.Prompt)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) preparePromptPanel(
|
||||||
|
opts types.ConfirmOpts,
|
||||||
|
) {
|
||||||
|
self.c.Views().Prompt.Title = opts.Title
|
||||||
|
self.c.Views().Prompt.FgColor = theme.GocuiDefaultTextColor
|
||||||
|
self.c.Views().Prompt.Mask = runeForMask(opts.Mask)
|
||||||
|
self.c.Views().Prompt.SetOrigin(0, 0)
|
||||||
|
|
||||||
|
textArea := self.c.Views().Prompt.TextArea
|
||||||
|
textArea.Clear()
|
||||||
|
textArea.TypeString(opts.Prompt)
|
||||||
|
self.c.Views().Prompt.RenderTextArea()
|
||||||
|
|
||||||
|
if opts.FindSuggestionsFunc != nil {
|
||||||
suggestionsContext := self.c.Contexts().Suggestions
|
suggestionsContext := self.c.Contexts().Suggestions
|
||||||
suggestionsContext.State.FindSuggestions = opts.FindSuggestionsFunc
|
suggestionsContext.State.FindSuggestions = opts.FindSuggestionsFunc
|
||||||
if opts.FindSuggestionsFunc != nil {
|
|
||||||
suggestionsView := self.c.Views().Suggestions
|
suggestionsView := self.c.Views().Suggestions
|
||||||
suggestionsView.Wrap = false
|
suggestionsView.Wrap = false
|
||||||
suggestionsView.FgColor = theme.GocuiDefaultTextColor
|
suggestionsView.FgColor = theme.GocuiDefaultTextColor
|
||||||
@@ -150,44 +173,59 @@ func (self *ConfirmationHelper) CreatePopupPanel(ctx goContext.Context, opts typ
|
|||||||
|
|
||||||
// remove any previous keybindings
|
// remove any previous keybindings
|
||||||
self.clearConfirmationViewKeyBindings()
|
self.clearConfirmationViewKeyBindings()
|
||||||
|
self.clearPromptViewKeyBindings()
|
||||||
|
|
||||||
|
var context types.Context
|
||||||
|
if opts.Editable {
|
||||||
|
self.c.Contexts().Suggestions.State.FindSuggestions = opts.FindSuggestionsFunc
|
||||||
|
|
||||||
|
self.preparePromptPanel(
|
||||||
|
types.ConfirmOpts{
|
||||||
|
Title: opts.Title,
|
||||||
|
Prompt: opts.Prompt,
|
||||||
|
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
||||||
|
Mask: opts.Mask,
|
||||||
|
})
|
||||||
|
|
||||||
|
context = self.c.Contexts().Prompt
|
||||||
|
|
||||||
|
self.setPromptKeyBindings(cancel, opts)
|
||||||
|
} else {
|
||||||
|
if opts.FindSuggestionsFunc != nil {
|
||||||
|
panic("non-editable confirmation views do not support suggestions")
|
||||||
|
}
|
||||||
|
|
||||||
|
self.c.Contexts().Suggestions.State.FindSuggestions = nil
|
||||||
|
|
||||||
self.prepareConfirmationPanel(
|
self.prepareConfirmationPanel(
|
||||||
types.ConfirmOpts{
|
types.ConfirmOpts{
|
||||||
Title: opts.Title,
|
Title: opts.Title,
|
||||||
Prompt: opts.Prompt,
|
Prompt: opts.Prompt,
|
||||||
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
|
||||||
Editable: opts.Editable,
|
|
||||||
Mask: opts.Mask,
|
|
||||||
})
|
})
|
||||||
confirmationView := self.c.Views().Confirmation
|
|
||||||
confirmationView.Editable = opts.Editable
|
|
||||||
|
|
||||||
if opts.Editable {
|
context = self.c.Contexts().Confirmation
|
||||||
textArea := confirmationView.TextArea
|
|
||||||
textArea.Clear()
|
self.setConfirmationKeyBindings(cancel, opts)
|
||||||
textArea.TypeString(opts.Prompt)
|
|
||||||
confirmationView.RenderTextArea()
|
|
||||||
} else {
|
|
||||||
self.c.ResetViewOrigin(confirmationView)
|
|
||||||
self.c.SetViewContent(confirmationView, style.AttrBold.Sprint(strings.TrimSpace(opts.Prompt)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.setKeyBindings(cancel, opts)
|
|
||||||
|
|
||||||
self.c.Contexts().Suggestions.State.AllowEditSuggestion = opts.AllowEditSuggestion
|
self.c.Contexts().Suggestions.State.AllowEditSuggestion = opts.AllowEditSuggestion
|
||||||
|
|
||||||
self.c.State().GetRepoState().SetCurrentPopupOpts(&opts)
|
self.c.State().GetRepoState().SetCurrentPopupOpts(&opts)
|
||||||
|
|
||||||
self.c.Context().Push(self.c.Contexts().Confirmation, types.OnFocusOpts{})
|
self.c.Context().Push(context, types.OnFocusOpts{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfirmationHelper) setKeyBindings(cancel goContext.CancelFunc, opts types.CreatePopupPanelOpts) {
|
func (self *ConfirmationHelper) setConfirmationKeyBindings(cancel goContext.CancelFunc, opts types.CreatePopupPanelOpts) {
|
||||||
var onConfirm func() error
|
onConfirm := self.wrappedConfirmationFunction(cancel, opts.HandleConfirm)
|
||||||
if opts.HandleConfirmPrompt != nil {
|
onClose := self.wrappedConfirmationFunction(cancel, opts.HandleClose)
|
||||||
onConfirm = self.wrappedPromptConfirmationFunction(cancel, opts.HandleConfirmPrompt, func() string { return self.c.Views().Confirmation.TextArea.GetContent() })
|
|
||||||
} else {
|
self.c.Contexts().Confirmation.State.OnConfirm = onConfirm
|
||||||
onConfirm = self.wrappedConfirmationFunction(cancel, opts.HandleConfirm)
|
self.c.Contexts().Confirmation.State.OnClose = onClose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) setPromptKeyBindings(cancel goContext.CancelFunc, opts types.CreatePopupPanelOpts) {
|
||||||
|
onConfirm := self.wrappedPromptConfirmationFunction(cancel, opts.HandleConfirmPrompt,
|
||||||
|
func() string { return self.c.Views().Prompt.TextArea.GetContent() })
|
||||||
|
|
||||||
onSuggestionConfirm := self.wrappedPromptConfirmationFunction(
|
onSuggestionConfirm := self.wrappedPromptConfirmationFunction(
|
||||||
cancel,
|
cancel,
|
||||||
@@ -206,8 +244,8 @@ func (self *ConfirmationHelper) setKeyBindings(cancel goContext.CancelFunc, opts
|
|||||||
return opts.HandleDeleteSuggestion(idx)
|
return opts.HandleDeleteSuggestion(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.Contexts().Confirmation.State.OnConfirm = onConfirm
|
self.c.Contexts().Prompt.State.OnConfirm = onConfirm
|
||||||
self.c.Contexts().Confirmation.State.OnClose = onClose
|
self.c.Contexts().Prompt.State.OnClose = onClose
|
||||||
self.c.Contexts().Suggestions.State.OnConfirm = onSuggestionConfirm
|
self.c.Contexts().Suggestions.State.OnConfirm = onSuggestionConfirm
|
||||||
self.c.Contexts().Suggestions.State.OnClose = onClose
|
self.c.Contexts().Suggestions.State.OnClose = onClose
|
||||||
self.c.Contexts().Suggestions.State.OnDeleteSuggestion = onDeleteSuggestion
|
self.c.Contexts().Suggestions.State.OnDeleteSuggestion = onDeleteSuggestion
|
||||||
@@ -217,6 +255,12 @@ func (self *ConfirmationHelper) clearConfirmationViewKeyBindings() {
|
|||||||
noop := func() error { return nil }
|
noop := func() error { return nil }
|
||||||
self.c.Contexts().Confirmation.State.OnConfirm = noop
|
self.c.Contexts().Confirmation.State.OnConfirm = noop
|
||||||
self.c.Contexts().Confirmation.State.OnClose = noop
|
self.c.Contexts().Confirmation.State.OnClose = noop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) clearPromptViewKeyBindings() {
|
||||||
|
noop := func() error { return nil }
|
||||||
|
self.c.Contexts().Prompt.State.OnConfirm = noop
|
||||||
|
self.c.Contexts().Prompt.State.OnClose = noop
|
||||||
self.c.Contexts().Suggestions.State.OnConfirm = noop
|
self.c.Contexts().Suggestions.State.OnConfirm = noop
|
||||||
self.c.Contexts().Suggestions.State.OnClose = noop
|
self.c.Contexts().Suggestions.State.OnClose = noop
|
||||||
self.c.Contexts().Suggestions.State.OnDeleteSuggestion = noop
|
self.c.Contexts().Suggestions.State.OnDeleteSuggestion = noop
|
||||||
@@ -238,8 +282,10 @@ func (self *ConfirmationHelper) ResizeCurrentPopupPanels() {
|
|||||||
switch c {
|
switch c {
|
||||||
case self.c.Contexts().Menu:
|
case self.c.Contexts().Menu:
|
||||||
self.resizeMenu(parentPopupContext)
|
self.resizeMenu(parentPopupContext)
|
||||||
case self.c.Contexts().Confirmation, self.c.Contexts().Suggestions:
|
case self.c.Contexts().Confirmation:
|
||||||
self.resizeConfirmationPanel(parentPopupContext)
|
self.resizeConfirmationPanel(parentPopupContext)
|
||||||
|
case self.c.Contexts().Prompt, self.c.Contexts().Suggestions:
|
||||||
|
self.resizePromptPanel(parentPopupContext)
|
||||||
case self.c.Contexts().CommitMessage, self.c.Contexts().CommitDescription:
|
case self.c.Contexts().CommitMessage, self.c.Contexts().CommitDescription:
|
||||||
self.ResizeCommitMessagePanels(parentPopupContext)
|
self.ResizeCommitMessagePanels(parentPopupContext)
|
||||||
}
|
}
|
||||||
@@ -300,26 +346,30 @@ func (self *ConfirmationHelper) layoutMenuPrompt(contentWidth int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *ConfirmationHelper) resizeConfirmationPanel(parentPopupContext types.Context) {
|
func (self *ConfirmationHelper) resizeConfirmationPanel(parentPopupContext types.Context) {
|
||||||
|
panelWidth := self.getPopupPanelWidth()
|
||||||
|
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||||
|
confirmationView := self.c.Views().Confirmation
|
||||||
|
prompt := confirmationView.Buffer()
|
||||||
|
panelHeight := getMessageHeight(true, false, prompt, contentWidth, confirmationView.TabWidth)
|
||||||
|
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
||||||
|
_, _ = self.c.GocuiGui().SetView(confirmationView.Name(), x0, y0, x1, y1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ConfirmationHelper) resizePromptPanel(parentPopupContext types.Context) {
|
||||||
suggestionsViewHeight := 0
|
suggestionsViewHeight := 0
|
||||||
if self.c.Views().Suggestions.Visible {
|
if self.c.Views().Suggestions.Visible {
|
||||||
suggestionsViewHeight = 11
|
suggestionsViewHeight = 11
|
||||||
}
|
}
|
||||||
panelWidth := self.getPopupPanelWidth()
|
panelWidth := self.getPopupPanelWidth()
|
||||||
contentWidth := panelWidth - 2 // minus 2 for the frame
|
contentWidth := panelWidth - 2 // minus 2 for the frame
|
||||||
confirmationView := self.c.Views().Confirmation
|
promptView := self.c.Views().Prompt
|
||||||
prompt := confirmationView.Buffer()
|
prompt := promptView.TextArea.GetContent()
|
||||||
wrap := true
|
panelHeight := getMessageHeight(false, true, prompt, contentWidth, promptView.TabWidth) + suggestionsViewHeight
|
||||||
editable := confirmationView.Editable
|
|
||||||
if editable {
|
|
||||||
prompt = confirmationView.TextArea.GetContent()
|
|
||||||
wrap = false
|
|
||||||
}
|
|
||||||
panelHeight := getMessageHeight(wrap, editable, prompt, contentWidth, confirmationView.TabWidth) + suggestionsViewHeight
|
|
||||||
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
x0, y0, x1, y1 := self.getPopupPanelDimensionsAux(panelWidth, panelHeight, parentPopupContext)
|
||||||
confirmationViewBottom := y1 - suggestionsViewHeight
|
promptViewBottom := y1 - suggestionsViewHeight
|
||||||
_, _ = self.c.GocuiGui().SetView(confirmationView.Name(), x0, y0, x1, confirmationViewBottom, 0)
|
_, _ = self.c.GocuiGui().SetView(promptView.Name(), x0, y0, x1, promptViewBottom, 0)
|
||||||
|
|
||||||
suggestionsViewTop := confirmationViewBottom + 1
|
suggestionsViewTop := promptViewBottom + 1
|
||||||
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
|
_, _ = self.c.GocuiGui().SetView(self.c.Views().Suggestions.Name(), x0, suggestionsViewTop, x1, suggestionsViewTop+suggestionsViewHeight, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,7 +38,7 @@ func (self *MenuController) GetKeybindings(opts types.KeybindingsOpts) []*types.
|
|||||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
Key: opts.GetKey(opts.Config.Universal.ConfirmMenu),
|
||||||
Handler: self.withItem(self.press),
|
Handler: self.withItem(self.press),
|
||||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||||
Description: self.c.Tr.Execute,
|
Description: self.c.Tr.Execute,
|
||||||
|
95
pkg/gui/controllers/prompt_controller.go
Normal file
95
pkg/gui/controllers/prompt_controller.go
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/gocui"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PromptController struct {
|
||||||
|
baseController
|
||||||
|
c *ControllerCommon
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ types.IController = &PromptController{}
|
||||||
|
|
||||||
|
func NewPromptController(
|
||||||
|
c *ControllerCommon,
|
||||||
|
) *PromptController {
|
||||||
|
return &PromptController{
|
||||||
|
baseController: baseController{},
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
|
bindings := []*types.Binding{
|
||||||
|
{
|
||||||
|
Key: gocui.KeyEnter,
|
||||||
|
Handler: func() error { return self.context().State.OnConfirm() },
|
||||||
|
Description: self.c.Tr.Confirm,
|
||||||
|
DisplayOnScreen: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||||
|
Handler: func() error { return self.context().State.OnClose() },
|
||||||
|
Description: self.c.Tr.CloseCancel,
|
||||||
|
DisplayOnScreen: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||||
|
Handler: func() error {
|
||||||
|
if len(self.c.Contexts().Suggestions.State.Suggestions) > 0 {
|
||||||
|
self.switchToSuggestions()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PromptController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||||
|
return []*gocui.ViewMouseBinding{
|
||||||
|
{
|
||||||
|
ViewName: self.c.Contexts().Suggestions.GetViewName(),
|
||||||
|
FocusedView: self.c.Contexts().Prompt.GetViewName(),
|
||||||
|
Key: gocui.MouseLeft,
|
||||||
|
Handler: func(gocui.ViewMouseBindingOpts) error {
|
||||||
|
self.switchToSuggestions()
|
||||||
|
// Let it fall through to the ListController's click handler so that
|
||||||
|
// the clicked line gets selected:
|
||||||
|
return gocui.ErrKeybindingNotHandled
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PromptController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
||||||
|
return func(types.OnFocusLostOpts) {
|
||||||
|
self.c.Helpers().Confirmation.DeactivatePrompt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PromptController) Context() types.Context {
|
||||||
|
return self.context()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PromptController) context() *context.PromptContext {
|
||||||
|
return self.c.Contexts().Prompt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *PromptController) switchToSuggestions() {
|
||||||
|
subtitle := ""
|
||||||
|
if self.c.State().GetRepoState().GetCurrentPopupOpts().HandleDeleteSuggestion != nil {
|
||||||
|
// We assume that whenever things are deletable, they
|
||||||
|
// are also editable, so we show both keybindings
|
||||||
|
subtitle = fmt.Sprintf(self.c.Tr.SuggestionsSubtitle,
|
||||||
|
self.c.UserConfig().Keybinding.Universal.Remove, self.c.UserConfig().Keybinding.Universal.Edit)
|
||||||
|
}
|
||||||
|
self.c.Views().Suggestions.Subtitle = subtitle
|
||||||
|
self.c.Context().Replace(self.c.Contexts().Suggestions)
|
||||||
|
}
|
@@ -24,7 +24,7 @@ func NewSearchPromptController(
|
|||||||
func (self *SearchPromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
func (self *SearchPromptController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
return []*types.Binding{
|
return []*types.Binding{
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
Key: gocui.KeyEnter,
|
||||||
Modifier: gocui.ModNone,
|
Modifier: gocui.ModNone,
|
||||||
Handler: self.confirm,
|
Handler: self.confirm,
|
||||||
},
|
},
|
||||||
|
@@ -32,7 +32,7 @@ func NewSuggestionsController(
|
|||||||
func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||||
bindings := []*types.Binding{
|
bindings := []*types.Binding{
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
Key: opts.GetKey(opts.Config.Universal.ConfirmSuggestion),
|
||||||
Handler: func() error { return self.context().State.OnConfirm() },
|
Handler: func() error { return self.context().State.OnConfirm() },
|
||||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||||
},
|
},
|
||||||
@@ -42,7 +42,7 @@ func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) []
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||||
Handler: self.switchToConfirmation,
|
Handler: self.switchToPrompt,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Remove),
|
Key: opts.GetKey(opts.Config.Universal.Remove),
|
||||||
@@ -55,11 +55,11 @@ func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) []
|
|||||||
Handler: func() error {
|
Handler: func() error {
|
||||||
if self.context().State.AllowEditSuggestion {
|
if self.context().State.AllowEditSuggestion {
|
||||||
if selectedItem := self.c.Contexts().Suggestions.GetSelected(); selectedItem != nil {
|
if selectedItem := self.c.Contexts().Suggestions.GetSelected(); selectedItem != nil {
|
||||||
self.c.Contexts().Confirmation.GetView().TextArea.Clear()
|
self.c.Contexts().Prompt.GetView().TextArea.Clear()
|
||||||
self.c.Contexts().Confirmation.GetView().TextArea.TypeString(selectedItem.Value)
|
self.c.Contexts().Prompt.GetView().TextArea.TypeString(selectedItem.Value)
|
||||||
self.c.Contexts().Confirmation.GetView().RenderTextArea()
|
self.c.Contexts().Prompt.GetView().RenderTextArea()
|
||||||
self.c.Contexts().Suggestions.RefreshSuggestions()
|
self.c.Contexts().Suggestions.RefreshSuggestions()
|
||||||
return self.switchToConfirmation()
|
return self.switchToPrompt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -73,26 +73,26 @@ func (self *SuggestionsController) GetKeybindings(opts types.KeybindingsOpts) []
|
|||||||
func (self *SuggestionsController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
func (self *SuggestionsController) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||||
return []*gocui.ViewMouseBinding{
|
return []*gocui.ViewMouseBinding{
|
||||||
{
|
{
|
||||||
ViewName: self.c.Contexts().Confirmation.GetViewName(),
|
ViewName: self.c.Contexts().Prompt.GetViewName(),
|
||||||
FocusedView: self.c.Contexts().Suggestions.GetViewName(),
|
FocusedView: self.c.Contexts().Suggestions.GetViewName(),
|
||||||
Key: gocui.MouseLeft,
|
Key: gocui.MouseLeft,
|
||||||
Handler: func(gocui.ViewMouseBindingOpts) error {
|
Handler: func(gocui.ViewMouseBindingOpts) error {
|
||||||
return self.switchToConfirmation()
|
return self.switchToPrompt()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsController) switchToConfirmation() error {
|
func (self *SuggestionsController) switchToPrompt() error {
|
||||||
self.c.Views().Suggestions.Subtitle = ""
|
self.c.Views().Suggestions.Subtitle = ""
|
||||||
self.c.Views().Suggestions.Highlight = false
|
self.c.Views().Suggestions.Highlight = false
|
||||||
self.c.Context().Replace(self.c.Contexts().Confirmation)
|
self.c.Context().Replace(self.c.Contexts().Prompt)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *SuggestionsController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
func (self *SuggestionsController) GetOnFocusLost() func(types.OnFocusLostOpts) {
|
||||||
return func(types.OnFocusLostOpts) {
|
return func(types.OnFocusLostOpts) {
|
||||||
self.c.Helpers().Confirmation.DeactivateConfirmationPrompt()
|
self.c.Helpers().Confirmation.DeactivatePrompt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -52,7 +52,7 @@ func (self *WorktreesController) GetKeybindings(opts types.KeybindingsOpts) []*t
|
|||||||
DisplayOnScreen: true,
|
DisplayOnScreen: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
Key: opts.GetKey(opts.Config.Universal.GoInto),
|
||||||
Handler: self.withItem(self.enter),
|
Handler: self.withItem(self.enter),
|
||||||
GetDisabledReason: self.require(self.singleItemSelected()),
|
GetDisabledReason: self.require(self.singleItemSelected()),
|
||||||
},
|
},
|
||||||
|
@@ -90,60 +90,36 @@ func (gui *Gui) scrollDownSecondary() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) scrollUpConfirmationPanel() error {
|
func (gui *Gui) scrollUpConfirmationPanel() error {
|
||||||
if gui.Views.Confirmation.Editable {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.scrollUpView(gui.Views.Confirmation)
|
gui.scrollUpView(gui.Views.Confirmation)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) scrollDownConfirmationPanel() error {
|
func (gui *Gui) scrollDownConfirmationPanel() error {
|
||||||
if gui.Views.Confirmation.Editable {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.scrollDownView(gui.Views.Confirmation)
|
gui.scrollDownView(gui.Views.Confirmation)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pageUpConfirmationPanel() error {
|
func (gui *Gui) pageUpConfirmationPanel() error {
|
||||||
if gui.Views.Confirmation.Editable {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.Views.Confirmation.ScrollUp(gui.Contexts().Confirmation.GetViewTrait().PageDelta())
|
gui.Views.Confirmation.ScrollUp(gui.Contexts().Confirmation.GetViewTrait().PageDelta())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) pageDownConfirmationPanel() error {
|
func (gui *Gui) pageDownConfirmationPanel() error {
|
||||||
if gui.Views.Confirmation.Editable {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.Views.Confirmation.ScrollDown(gui.Contexts().Confirmation.GetViewTrait().PageDelta())
|
gui.Views.Confirmation.ScrollDown(gui.Contexts().Confirmation.GetViewTrait().PageDelta())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) goToConfirmationPanelTop() error {
|
func (gui *Gui) goToConfirmationPanelTop() error {
|
||||||
if gui.Views.Confirmation.Editable {
|
|
||||||
return gocui.ErrKeybindingNotHandled
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.Views.Confirmation.ScrollUp(gui.Views.Confirmation.ViewLinesHeight())
|
gui.Views.Confirmation.ScrollUp(gui.Views.Confirmation.ViewLinesHeight())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) goToConfirmationPanelBottom() error {
|
func (gui *Gui) goToConfirmationPanelBottom() error {
|
||||||
if gui.Views.Confirmation.Editable {
|
|
||||||
return gocui.ErrKeybindingNotHandled
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.Views.Confirmation.ScrollDown(gui.Views.Confirmation.ViewLinesHeight())
|
gui.Views.Confirmation.ScrollDown(gui.Views.Confirmation.ViewLinesHeight())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -697,7 +697,7 @@ func NewGui(
|
|||||||
return gui.helpers.AppStatus.WithWaitingStatusSync(message, f)
|
return gui.helpers.AppStatus.WithWaitingStatusSync(message, f)
|
||||||
},
|
},
|
||||||
func(message string, kind types.ToastKind) { gui.helpers.AppStatus.Toast(message, kind) },
|
func(message string, kind types.ToastKind) { gui.helpers.AppStatus.Toast(message, kind) },
|
||||||
func() string { return gui.Views.Confirmation.TextArea.GetContent() },
|
func() string { return gui.Views.Prompt.TextArea.GetContent() },
|
||||||
func() bool { return gui.c.InDemo() },
|
func() bool { return gui.c.InDemo() },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -507,11 +507,11 @@ func (gui *Gui) SetMouseKeybinding(binding *gocui.ViewMouseBinding) error {
|
|||||||
!gocui.IsMouseScrollKey(opts.Key) {
|
!gocui.IsMouseScrollKey(opts.Key) {
|
||||||
// we ignore click events on views that aren't popup panels, when a popup panel is focused.
|
// we ignore click events on views that aren't popup panels, when a popup panel is focused.
|
||||||
// Unless both the current view and the clicked-on view are either commit message or commit
|
// Unless both the current view and the clicked-on view are either commit message or commit
|
||||||
// description, or a confirmation and the suggestions view, because we want to allow switching
|
// description, or a prompt and the suggestions view, because we want to allow switching
|
||||||
// between those two views by clicking.
|
// between those two views by clicking.
|
||||||
isCommitMessageOrSuggestionsView := func(viewName string) bool {
|
isCommitMessageOrSuggestionsView := func(viewName string) bool {
|
||||||
return viewName == "commitMessage" || viewName == "commitDescription" ||
|
return viewName == "commitMessage" || viewName == "commitDescription" ||
|
||||||
viewName == "confirmation" || viewName == "suggestions"
|
viewName == "prompt" || viewName == "suggestions"
|
||||||
}
|
}
|
||||||
if !isCommitMessageOrSuggestionsView(gui.currentViewName()) || !isCommitMessageOrSuggestionsView(binding.ViewName) {
|
if !isCommitMessageOrSuggestionsView(gui.currentViewName()) || !isCommitMessageOrSuggestionsView(binding.ViewName) {
|
||||||
return nil
|
return nil
|
||||||
|
@@ -3,6 +3,7 @@ package gui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||||
)
|
)
|
||||||
@@ -20,6 +21,7 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
maxColumnSize := 1
|
maxColumnSize := 1
|
||||||
|
confirmKey := keybindings.GetKey(gui.c.UserConfig().Keybinding.Universal.ConfirmMenu)
|
||||||
|
|
||||||
for _, item := range opts.Items {
|
for _, item := range opts.Items {
|
||||||
if item.LabelColumns == nil {
|
if item.LabelColumns == nil {
|
||||||
@@ -31,6 +33,11 @@ func (gui *Gui) createMenu(opts types.CreateMenuOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
maxColumnSize = max(maxColumnSize, len(item.LabelColumns))
|
maxColumnSize = max(maxColumnSize, len(item.LabelColumns))
|
||||||
|
|
||||||
|
// Remove all item keybindings that are the same as the confirm binding
|
||||||
|
if item.Key == confirmKey {
|
||||||
|
item.Key = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range opts.Items {
|
for _, item := range opts.Items {
|
||||||
|
@@ -25,6 +25,7 @@ type Views struct {
|
|||||||
|
|
||||||
Options *gocui.View
|
Options *gocui.View
|
||||||
Confirmation *gocui.View
|
Confirmation *gocui.View
|
||||||
|
Prompt *gocui.View
|
||||||
Menu *gocui.View
|
Menu *gocui.View
|
||||||
CommitMessage *gocui.View
|
CommitMessage *gocui.View
|
||||||
CommitDescription *gocui.View
|
CommitDescription *gocui.View
|
||||||
|
@@ -68,6 +68,7 @@ func (gui *Gui) orderedViewNameMappings() []viewNameMapping {
|
|||||||
{viewPtr: &gui.Views.Menu, name: "menu"},
|
{viewPtr: &gui.Views.Menu, name: "menu"},
|
||||||
{viewPtr: &gui.Views.Suggestions, name: "suggestions"},
|
{viewPtr: &gui.Views.Suggestions, name: "suggestions"},
|
||||||
{viewPtr: &gui.Views.Confirmation, name: "confirmation"},
|
{viewPtr: &gui.Views.Confirmation, name: "confirmation"},
|
||||||
|
{viewPtr: &gui.Views.Prompt, name: "prompt"},
|
||||||
{viewPtr: &gui.Views.Tooltip, name: "tooltip"},
|
{viewPtr: &gui.Views.Tooltip, name: "tooltip"},
|
||||||
|
|
||||||
// this guy will cover everything else when it appears
|
// this guy will cover everything else when it appears
|
||||||
@@ -127,9 +128,14 @@ func (gui *Gui) createAllViews() error {
|
|||||||
gui.Views.CommitDescription.Editor = gocui.EditorFunc(gui.commitDescriptionEditor)
|
gui.Views.CommitDescription.Editor = gocui.EditorFunc(gui.commitDescriptionEditor)
|
||||||
|
|
||||||
gui.Views.Confirmation.Visible = false
|
gui.Views.Confirmation.Visible = false
|
||||||
gui.Views.Confirmation.Editor = gocui.EditorFunc(gui.promptEditor)
|
gui.Views.Confirmation.Wrap = true
|
||||||
gui.Views.Confirmation.AutoRenderHyperLinks = true
|
gui.Views.Confirmation.AutoRenderHyperLinks = true
|
||||||
|
|
||||||
|
gui.Views.Prompt.Visible = false
|
||||||
|
gui.Views.Prompt.Wrap = false // We don't want wrapping in one-line prompts
|
||||||
|
gui.Views.Prompt.Editable = true
|
||||||
|
gui.Views.Prompt.Editor = gocui.EditorFunc(gui.promptEditor)
|
||||||
|
|
||||||
gui.Views.Suggestions.Visible = false
|
gui.Views.Suggestions.Visible = false
|
||||||
|
|
||||||
gui.Views.Menu.Visible = false
|
gui.Views.Menu.Visible = false
|
||||||
|
@@ -611,6 +611,7 @@ type TranslationSet struct {
|
|||||||
MustStashWarning string
|
MustStashWarning string
|
||||||
MustStashTitle string
|
MustStashTitle string
|
||||||
ConfirmationTitle string
|
ConfirmationTitle string
|
||||||
|
PromptTitle string
|
||||||
PrevPage string
|
PrevPage string
|
||||||
NextPage string
|
NextPage string
|
||||||
GotoTop string
|
GotoTop string
|
||||||
@@ -1692,6 +1693,7 @@ func EnglishTranslationSet() *TranslationSet {
|
|||||||
MustStashWarning: "Pulling a patch out into the index requires stashing and unstashing your changes. If something goes wrong, you'll be able to access your files from the stash. Continue?",
|
MustStashWarning: "Pulling a patch out into the index requires stashing and unstashing your changes. If something goes wrong, you'll be able to access your files from the stash. Continue?",
|
||||||
MustStashTitle: "Must stash",
|
MustStashTitle: "Must stash",
|
||||||
ConfirmationTitle: "Confirmation panel",
|
ConfirmationTitle: "Confirmation panel",
|
||||||
|
PromptTitle: "Input prompt",
|
||||||
PrevPage: "Previous page",
|
PrevPage: "Previous page",
|
||||||
NextPage: "Next page",
|
NextPage: "Next page",
|
||||||
GotoTop: "Scroll to top",
|
GotoTop: "Scroll to top",
|
||||||
|
@@ -27,7 +27,7 @@ func (self *CommitDescriptionPanelDriver) SwitchToSummary() *CommitMessagePanelD
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitDescriptionPanelDriver) AddNewline() *CommitDescriptionPanelDriver {
|
func (self *CommitDescriptionPanelDriver) AddNewline() *CommitDescriptionPanelDriver {
|
||||||
self.t.pressFast(self.t.keys.Universal.Confirm)
|
self.t.pressFast("<enter>")
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ func (self *MenuDriver) Title(expected *TextMatcher) *MenuDriver {
|
|||||||
func (self *MenuDriver) Confirm() *MenuDriver {
|
func (self *MenuDriver) Confirm() *MenuDriver {
|
||||||
self.checkNecessaryChecksCompleted()
|
self.checkNecessaryChecksCompleted()
|
||||||
|
|
||||||
self.getViewDriver().PressEnter()
|
self.getViewDriver().Press(self.t.keys.Universal.ConfirmMenu)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ func (self *Popup) Confirmation() *ConfirmationDriver {
|
|||||||
func (self *Popup) inConfirm() {
|
func (self *Popup) inConfirm() {
|
||||||
self.t.assertWithRetries(func() (bool, string) {
|
self.t.assertWithRetries(func() (bool, string) {
|
||||||
currentView := self.t.gui.CurrentContext().GetView()
|
currentView := self.t.gui.CurrentContext().GetView()
|
||||||
return currentView.Name() == "confirmation" && !currentView.Editable, "Expected confirmation popup to be focused"
|
return currentView.Name() == "confirmation", "Expected confirmation popup to be focused"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ func (self *Popup) Prompt() *PromptDriver {
|
|||||||
func (self *Popup) inPrompt() {
|
func (self *Popup) inPrompt() {
|
||||||
self.t.assertWithRetries(func() (bool, string) {
|
self.t.assertWithRetries(func() (bool, string) {
|
||||||
currentView := self.t.gui.CurrentContext().GetView()
|
currentView := self.t.gui.CurrentContext().GetView()
|
||||||
return currentView.Name() == "confirmation" && currentView.Editable, "Expected prompt popup to be focused"
|
return currentView.Name() == "prompt", "Expected prompt popup to be focused"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ func (self *Popup) inAlert() {
|
|||||||
// basically the same thing as a confirmation popup with the current implementation
|
// basically the same thing as a confirmation popup with the current implementation
|
||||||
self.t.assertWithRetries(func() (bool, string) {
|
self.t.assertWithRetries(func() (bool, string) {
|
||||||
currentView := self.t.gui.CurrentContext().GetView()
|
currentView := self.t.gui.CurrentContext().GetView()
|
||||||
return currentView.Name() == "confirmation" && !currentView.Editable, "Expected alert popup to be focused"
|
return currentView.Name() == "confirmation", "Expected alert popup to be focused"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@ type PromptDriver struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *PromptDriver) getViewDriver() *ViewDriver {
|
func (self *PromptDriver) getViewDriver() *ViewDriver {
|
||||||
return self.t.Views().Confirmation()
|
return self.t.Views().Prompt()
|
||||||
}
|
}
|
||||||
|
|
||||||
// asserts that the popup has the expected title
|
// asserts that the popup has the expected title
|
||||||
@@ -72,7 +72,7 @@ func (self *PromptDriver) ConfirmFirstSuggestion() {
|
|||||||
self.t.Views().Suggestions().
|
self.t.Views().Suggestions().
|
||||||
IsFocused().
|
IsFocused().
|
||||||
SelectedLineIdx(0).
|
SelectedLineIdx(0).
|
||||||
PressEnter()
|
Press(self.t.keys.Universal.ConfirmSuggestion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) {
|
func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) {
|
||||||
@@ -80,7 +80,7 @@ func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) {
|
|||||||
self.t.Views().Suggestions().
|
self.t.Views().Suggestions().
|
||||||
IsFocused().
|
IsFocused().
|
||||||
NavigateToLine(matcher).
|
NavigateToLine(matcher).
|
||||||
PressEnter()
|
Press(self.t.keys.Universal.ConfirmSuggestion)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PromptDriver) DeleteSuggestion(matcher *TextMatcher) *PromptDriver {
|
func (self *PromptDriver) DeleteSuggestion(matcher *TextMatcher) *PromptDriver {
|
||||||
|
@@ -128,6 +128,10 @@ func (self *Views) Confirmation() *ViewDriver {
|
|||||||
return self.regularView("confirmation")
|
return self.regularView("confirmation")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Views) Prompt() *ViewDriver {
|
||||||
|
return self.regularView("prompt")
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Views) CommitMessage() *ViewDriver {
|
func (self *Views) CommitMessage() *ViewDriver {
|
||||||
return self.regularView("commitMessage")
|
return self.regularView("commitMessage")
|
||||||
}
|
}
|
||||||
|
@@ -1366,6 +1366,14 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "\u003center\u003e"
|
"default": "\u003center\u003e"
|
||||||
},
|
},
|
||||||
|
"confirmMenu": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "\u003center\u003e"
|
||||||
|
},
|
||||||
|
"confirmSuggestion": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "\u003center\u003e"
|
||||||
|
},
|
||||||
"confirmInEditor": {
|
"confirmInEditor": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "\u003ca-enter\u003e"
|
"default": "\u003ca-enter\u003e"
|
||||||
|
Reference in New Issue
Block a user