summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2024-01-02 12:19:31 +1100
committerJesse Duffield <jessedduffield@gmail.com>2024-01-28 08:33:13 +1100
commit0f9d9e13d12d37ab419efccc5c217422fb67a765 (patch)
tree35fc5bdc4473da8a6750ee621e270b9e595520c9
parentc07b3fad64ca71bc1873bcc1bb9e2256c05d4df0 (diff)
Show mode-specific keybinding suggestions
As part of making lazygit more discoverable, there are certain keys which you almost certainly need to press when you're in a given mode e.g. 'v' to paste commits when cherry-picking. This commit prominently shows these keybinding suggestions alongside the others in the option view. I'm using the same colours for these keybindings as is associated with the mode elsewhere e.g. yellow for rebasing and cyan for cherry-picking. The cherry-picking one is a bit weird because we also use cyan text to show loaders and app status at the bottom left so it may be confusing, but I haven't personally found it awkward from having tested it out myself. Previously we would render these options whenever a new context was activated, but now that we need to re-render options whenever a mode changes, I'm instead rendering them on each screen re-render (i.e. in the layout function). Given how cheap it is to render this text, I think it's fine performance-wise.
-rw-r--r--pkg/commands/types/enums/enums.go8
-rw-r--r--pkg/gui/context.go2
-rw-r--r--pkg/gui/controllers/confirmation_controller.go16
-rw-r--r--pkg/gui/controllers/global_controller.go10
-rw-r--r--pkg/gui/controllers/menu_controller.go10
-rw-r--r--pkg/gui/controllers/merge_conflicts_controller.go46
-rw-r--r--pkg/gui/layout.go2
-rw-r--r--pkg/gui/options_map.go157
-rw-r--r--pkg/gui/types/keybindings.go28
-rw-r--r--pkg/gui/views.go1
-rw-r--r--pkg/integration/components/common.go26
-rw-r--r--pkg/integration/components/views.go4
-rw-r--r--pkg/integration/tests/test_list.go1
-rw-r--r--pkg/integration/tests/ui/mode_specific_keybinding_suggestions.go118
14 files changed, 325 insertions, 104 deletions
diff --git a/pkg/commands/types/enums/enums.go b/pkg/commands/types/enums/enums.go
index 7e8a81798..68a29d8f0 100644
--- a/pkg/commands/types/enums/enums.go
+++ b/pkg/commands/types/enums/enums.go
@@ -12,3 +12,11 @@ const (
REBASE_MODE_REBASING
REBASE_MODE_MERGING
)
+
+func (self RebaseMode) IsMerging() bool {
+ return self == REBASE_MODE_MERGING
+}
+
+func (self RebaseMode) IsRebasing() bool {
+ return self == REBASE_MODE_INTERACTIVE || self == REBASE_MODE_NORMAL || self == REBASE_MODE_REBASING
+}
diff --git a/pkg/gui/context.go b/pkg/gui/context.go
index d845265a3..be5a720e3 100644
--- a/pkg/gui/context.go
+++ b/pkg/gui/context.go
@@ -245,8 +245,6 @@ func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts)
self.gui.c.GocuiGui().Cursor = v.Editable
- self.gui.renderContextOptionsMap(c)
-
if err := c.HandleFocus(opts); err != nil {
return err
}
diff --git a/pkg/gui/controllers/confirmation_controller.go b/pkg/gui/controllers/confirmation_controller.go
index 164af19ec..aa5617fa8 100644
--- a/pkg/gui/controllers/confirmation_controller.go
+++ b/pkg/gui/controllers/confirmation_controller.go
@@ -24,16 +24,16 @@ func NewConfirmationController(
func (self *ConfirmationController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
bindings := []*types.Binding{
{
- Key: opts.GetKey(opts.Config.Universal.Confirm),
- Handler: func() error { return self.context().State.OnConfirm() },
- Description: self.c.Tr.Confirm,
- Display: true,
+ Key: opts.GetKey(opts.Config.Universal.Confirm),
+ 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,
- Display: 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),
diff --git a/pkg/gui/controllers/global_controller.go b/pkg/gui/controllers/global_controller.go
index ba02ac26a..548c8461e 100644
--- a/pkg/gui/controllers/global_controller.go
+++ b/pkg/gui/controllers/global_controller.go
@@ -72,6 +72,7 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
Description: self.c.Tr.OpenKeybindingsMenu,
Handler: self.createOptionsMenu,
ShortDescription: self.c.Tr.Keybindings,
+ DisplayOnScreen: true,
},
{
ViewName: "",
@@ -112,10 +113,11 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
Handler: self.quitWithoutChangingDirectory,
},
{
- Key: opts.GetKey(opts.Config.Universal.Return),
- Modifier: gocui.ModNone,
- Handler: self.escape,
- Description: self.c.Tr.Cancel,
+ Key: opts.GetKey(opts.Config.Universal.Return),
+ Modifier: gocui.ModNone,
+ Handler: self.escape,
+ Description: self.c.Tr.Cancel,
+ DisplayOnScreen: true,
},
{
Key: opts.GetKey(opts.Config.Universal.ToggleWhitespaceInDiffView),
diff --git a/pkg/gui/controllers/menu_controller.go b/pkg/gui/controllers/menu_controller.go
index a64189138..ddd0b2c18 100644
--- a/pkg/gui/controllers/menu_controller.go
+++ b/pkg/gui/controllers/menu_controller.go
@@ -42,13 +42,13 @@ func (self *MenuController) GetKeybindings(opts types.KeybindingsOpts) []*types.
Handler: self.withItem(self.press),
GetDisabledReason: self.require(self.singleItemSelected()),
Description: self.c.Tr.Execute,
- Display: true,
+ DisplayOnScreen: true,
},
{
- Key: opts.GetKey(opts.Config.Universal.Return),
- Handler: self.close,
- Description: self.c.Tr.Close,
- Display: true,
+ Key: opts.GetKey(opts.Config.Universal.Return),
+ Handler: self.close,
+ Description: self.c.Tr.Close,
+ DisplayOnScreen: true,
},
}
diff --git a/pkg/gui/controllers/merge_conflicts_controller.go b/pkg/gui/controllers/merge_conflicts_controller.go
index e0d4cae06..03ca4a10b 100644
--- a/pkg/gui/controllers/merge_conflicts_controller.go
+++ b/pkg/gui/controllers/merge_conflicts_controller.go
@@ -48,30 +48,30 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts)
Description: self.c.Tr.SelectNextHunk,
},
{
- Key: opts.GetKey(opts.Config.Universal.PrevBlock),
- Handler: self.withRenderAndFocus(self.PrevConflict),
- Description: self.c.Tr.PrevConflict,
- Display: true,
+ Key: opts.GetKey(opts.Config.Universal.PrevBlock),
+ Handler: self.withRenderAndFocus(self.PrevConflict),
+ Description: self.c.Tr.PrevConflict,
+ DisplayOnScreen: true,
},
{
- Key: opts.GetKey(opts.Config.Universal.NextBlock),
- Handler: self.withRenderAndFocus(self.NextConflict),
- Description: self.c.Tr.NextConflict,
- Display: true,
+ Key: opts.GetKey(opts.Config.Universal.NextBlock),
+ Handler: self.withRenderAndFocus(self.NextConflict),
+ Description: self.c.Tr.NextConflict,
+ DisplayOnScreen: true,
},
{
- Key: opts.GetKey(opts.Config.Universal.Undo),
- Handler: self.withRenderAndFocus(self.HandleUndo),
- Description: self.c.Tr.Undo,
- Tooltip: self.c.Tr.UndoMergeResolveTooltip,
- Display: true,
+ Key: opts.GetKey(opts.Config.Universal.Undo),
+ Handler: self.withRenderAndFocus(self.HandleUndo),
+ Description: self.c.Tr.Undo,
+ Tooltip: self.c.Tr.UndoMergeResolveTooltip,
+ DisplayOnScreen: true,
},
{
- Key: opts.GetKey(opts.Config.Universal.Edit),
- Handler: self.HandleEditFile,
- Description: self.c.Tr.EditFile,
- Tooltip: self.c.Tr.EditFileTooltip,
- Display: true,
+ Key: opts.GetKey(opts.Config.Universal.Edit),
+ Handler: self.HandleEditFile,
+ Description: self.c.Tr.EditFile,
+ Tooltip: self.c.Tr.EditFileTooltip,
+ DisplayOnScreen: true,
},
{
Key: opts.GetKey(opts.Config.Universal.OpenFile),
@@ -108,11 +108,11 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts)
Tag: "navigation",
},
{
- Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
- Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
- Description: self.c.Tr.OpenMergeTool,
- Tooltip: self.c.Tr.OpenMergeToolTooltip,
- Display: true,
+ Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
+ Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
+ Description: self.c.Tr.OpenMergeTool,
+ Tooltip: self.c.Tr.OpenMergeToolTooltip,
+ DisplayOnScreen: true,
},
{
Key: opts.GetKey(opts.Config.Universal.Return),
diff --git a/pkg/gui/layout.go b/pkg/gui/layout.go
index 02c74b023..823dfe994 100644
--- a/pkg/gui/layout.go
+++ b/pkg/gui/layout.go
@@ -165,6 +165,8 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return err
}
+ gui.renderContextOptionsMap()
+
outer:
for {
select {
diff --git a/pkg/gui/options_map.go b/pkg/gui/options_map.go
index a2f1496bc..01bb3e212 100644
--- a/pkg/gui/options_map.go
+++ b/pkg/gui/options_map.go
@@ -4,9 +4,13 @@ import (
"fmt"
"strings"
+ "github.com/jesseduffield/lazygit/pkg/gui/context"
"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/theme"
+ "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
)
@@ -14,30 +18,89 @@ type OptionsMapMgr struct {
c *helpers.HelperCommon
}
-func (gui *Gui) renderContextOptionsMap(c types.Context) {
+func (gui *Gui) renderContextOptionsMap() {
// In demos, we render our own content to this view
if gui.integrationTest != nil && gui.integrationTest.IsDemo() {
return
}
mgr := OptionsMapMgr{c: gui.c}
- mgr.renderContextOptionsMap(c)
+ mgr.renderContextOptionsMap()
}
-// render the options available for the current context at the bottom of the screen
-func (self *OptionsMapMgr) renderContextOptionsMap(c types.Context) {
- bindingsToDisplay := lo.Filter(c.GetKeybindings(self.c.KeybindingsOpts()), func(binding *types.Binding, _ int) bool {
- return binding.Display
+// Render the options available for the current context at the bottom of the screen
+// STYLE GUIDE: we use the default options fg color for most keybindings. We can
+// only use a different color if we're in a specific mode where the user is likely
+// to want to press that key. For example, when in cherry-picking mode, we
+// want to prominently show the keybinding for pasting commits.
+func (self *OptionsMapMgr) renderContextOptionsMap() {
+ currentContext := self.c.CurrentContext()
+
+ currentContextBindings := currentContext.GetKeybindings(self.c.KeybindingsOpts())
+ globalBindings := self.c.Contexts().Global.GetKeybindings(self.c.KeybindingsOpts())
+
+ allBindings := append(currentContextBindings, globalBindings...)
+
+ bindingsToDisplay := lo.Filter(allBindings, func(binding *types.Binding, _ int) bool {
+ return binding.DisplayOnScreen && !binding.IsDisabled()
+ })
+
+ optionsMap := lo.Map(bindingsToDisplay, func(binding *types.Binding, _ int) bindingInfo {
+ displayStyle := theme.OptionsFgColor
+ if binding.DisplayStyle != nil {
+ displayStyle = *binding.DisplayStyle
+ }
+
+ description := binding.Description
+ if binding.ShortDescription != "" {
+ description = binding.ShortDescription
+ }
+
+ return bindingInfo{
+ key: keybindings.LabelFromKey(binding.Key),
+ description: description,
+ style: displayStyle,
+ }
})
- var optionsMap []bindingInfo
- if len(bindingsToDisplay) == 0 {
- optionsMap = self.globalOptions()
- } else {
- optionsMap = lo.Map(bindingsToDisplay, func(binding *types.Binding, _ int) bindingInfo {
- return bindingInfo{
- key: keybindings.LabelFromKey(binding.Key),
- description: binding.Description,
- }
+ // Mode-specific local keybindings
+ if currentContext.GetKey() == context.LOCAL_COMMITS_CONTEXT_KEY {
+ if self.c.Modes().CherryPicking.Active() {
+ optionsMap = utils.Prepend(optionsMap, bindingInfo{
+ key: keybindings.Label(self.c.KeybindingsOpts().Config.Commits.PasteCommits),
+ description: self.c.Tr.PasteCommits,
+ style: style.FgCyan,
+ })
+ }
+
+ if self.c.Model().BisectInfo.Started() {
+ optionsMap = utils.Prepend(optionsMap, bindingInfo{
+ key: keybindings.Label(self.c.KeybindingsOpts().Config.Commits.ViewBisectOptions),
+ description: self.c.Tr.ViewBisectOptions,
+ style: style.FgGreen,
+ })
+ }
+ }
+
+ // Mode-specific global keybindings
+ if self.c.Model().WorkingTreeStateAtLastCommitRefresh.IsRebasing() {
+ optionsMap = utils.Prepend(optionsMap, bindingInfo{
+ key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu),
+ description: self.c.Tr.ViewRebaseOptions,
+ style: style.FgYellow,
+ })
+ } else if self.c.Model().WorkingTreeStateAtLastCommitRefresh.IsMerging() {
+ optionsMap = utils.Prepend(optionsMap, bindingInfo{
+ key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreateRebaseOptionsMenu),
+ description: self.c.Tr.ViewMergeOptions,
+ style: style.FgYellow,
+ })
+ }
+
+ if self.c.Git().Patch.PatchBuilder.Active() {
+ optionsMap = utils.Prepend(optionsMap, bindingInfo{
+ key: keybindings.Label(self.c.KeybindingsOpts().Config.Universal.CreatePatchOptionsMenu),
+ description: self.c.Tr.ViewPatchOptions,
+ style: style.FgYellow,
})
}
@@ -45,49 +108,41 @@ func (self *OptionsMapMgr) renderContextOptionsMap(c types.Context) {
}
func (self *OptionsMapMgr) formatBindingInfos(bindingInfos []bindingInfo) string {
- return strings.Join(
- lo.Map(bindingInfos, func(bindingInfo bindingInfo, _ int) string {
- return fmt.Sprintf("%s: %s", bindingInfo.key, bindingInfo.description)
- }),
- ", ")
+ width := self.c.Views().Options.Width() - 4 // -4 for the padding
+ var builder strings.Builder
+ ellipsis := "…"
+ separator := " | "
+
+ length := 0
+
+ for i, info := range bindingInfos {
+ plainText := fmt.Sprintf("%s: %s", info.description, info.key)
+
+ // Check if adding the next formatted string exceeds the available width
+ if i > 0 && length+len(separator)+len(plainText) > width {
+ builder.WriteString(theme.OptionsFgColor.Sprint(separator + ellipsis))
+ break
+ }
+
+ formatted := info.style.Sprintf(plainText)
+
+ if i > 0 {
+ builder.WriteString(theme.OptionsFgColor.Sprint(separator))
+ length += len(separator)
+ }
+ builder.WriteString(formatted)
+ length += len(plainText)
+ }
+
+ return builder.String()
}
func (self *OptionsMapMgr) renderOptions(options string) {
self.c.SetViewContent(self.c.Views().Options, options)
}
-func (self *OptionsMapMgr) globalOptions() []bindingInfo {
- keybindingConfig := self.c.UserConfig.Keybinding
-
- return []bindingInfo{
- {
- key: fmt.Sprintf("%s/%s", keybindings.Label(keybindingConfig.Universal.ScrollUpMain), keybindings.Label(keybindingConfig.Universal.ScrollDownMain)),
- description: self.c.Tr.Scroll,
- },
- {
- key: keybindings.Label(keybindingConfig.Universal.Return),
- description: self.c.Tr.Cancel,
- },
- {
- key: keybindings.Label(keybindingConfig.Universal.Quit),
- description: self.c.Tr.Quit,
- },
- {
- key: keybindings.Label(keybindingConfig.Universal.OptionMenuAlt1),
- description: self.c.Tr.Keybindings,
- },
- {
- key: fmt.Sprintf("%s-%s", keybindings.Label(keybindingConfig.Universal.JumpToBlock[0]), keybindings.Label(keybindingConfig.Universal.JumpToBlock[len(keybindingConfig.Universal.JumpToBlock)-1])),
- description: self.c.Tr.Jump,
- },
- {
- key: fmt.Sprintf("%s/%s", keybindings.Label(keybindingConfig.Universal.ScrollLeft), keybindings.Label(keybindingConfig.Universal.ScrollRight)),
- description: self.c.Tr.ScrollLeftRight,
- },
- }
-}
-
type bindingInfo struct {
key string
description string
+ style style.TextStyle
}
diff --git a/pkg/gui/types/keybindings.go b/pkg/gui/types/keybindings.go
index 7d2c19bd4..db21e7bc9 100644
--- a/pkg/gui/types/keybindings.go
+++ b/pkg/gui/types/keybindings.go
@@ -11,21 +11,25 @@ type Key interface{} // FIXME: find out how to get `gocui.Key | rune`
// is only handled if the given view has focus, or handled globally if the view
// is ""
type Binding struct {
- ViewName string
- Handler func() error
- Key Key
- Modifier gocui.Modifier
- Description string
+ ViewName string
+ Handler func() error
+ Key Key
+ Modifier gocui.Modifier
+ Description string
+ // If defined, this is used in place of Description when showing the keybinding
+ // in the options view at the bottom left of the screen.
ShortDescription string
Alternative string
Tag string // e.g. 'navigation'. Used for grouping things in the cheatsheet
OpensMenu bool
- // If true, the keybinding will appear at the bottom of the screen. If
- // the given view has no bindings with Display: true, the default keybindings
- // will be displayed instead.
- // TODO: implement this
- Display bool
+ // If true, the keybinding will appear at the bottom of the screen.
+ // Even if set to true, the keybinding will not be displayed if it is currently
+ // disabled. We could instead display it with a strikethrough, but there's
+ // limited realestate to show all the keybindings we want, so we're hiding it instead.
+ DisplayOnScreen bool
+ // if unset, the binding will be displayed in the default color. Only applies to the keybinding
+ // on-screen, not in the keybindings menu.
DisplayStyle *style.TextStyle
// to be displayed if the keybinding is highlighted from within a menu
@@ -39,6 +43,10 @@ type Binding struct {
GetDisabledReason func() *DisabledReason
}
+func (Binding *Binding) IsDisabled() bool {
+ return Binding.GetDisabledReason != nil && Binding.GetDisabledReason() != nil
+}
+
// A guard is a decorator which checks something before executing a handler
// and potentially early-exits if some precondition hasn't been met.
type Guard func(func() error) func() error
diff --git a/pkg/gui/views.go b/pkg/gui/views.go
index be279c9d4..13caa9c7f 100644
--- a/pkg/gui/views.go
+++ b/pkg/gui/views.go
@@ -94,7 +94,6 @@ func (gui *Gui) createAllViews() error {
(*mapping.viewPtr).SelBgColor = theme.GocuiSelectedLineBgColor
}
- gui.Views.Options.FgColor = theme.OptionsColor
gui.Views.Options.Frame = false
gui.Views.SearchPrefix.BgColor = gocui.ColorDefault
diff --git a/pkg/integration/components/common.go b/pkg/integration/components/common.go
index 4f7cd9754..2033a9442 100644
--- a/pkg/integration/components/common.go
+++ b/pkg/integration/components/common.go
@@ -62,3 +62,29 @@ func (self *Common) SelectPatchOption(matcher *TextMatcher) {
self.t.ExpectPopup().Menu().Title(Equals("Patch options")).Select(matcher).Confirm()
}
+
+func (self *Common) ResetBisect() {
+ self.t.Views().Commits().
+ Focus().
+ Press(self.t.keys.Commits.ViewBisectOptions).
+ Tap(func() {
+ self.t.ExpectPopup().Menu().
+ Title(Equals("Bisect")).
+ Select(Contains("Reset bisect")).
+ Confirm()
+
+ self.t.ExpectPopup().Confirmation().
+ Title(Equals("Reset 'git bisect'")).
+ Content(Contains("Are you sure you want to reset 'git bisect'?")).
+ Confirm()
+ })
+}
+
+func (self *Common) ResetCustomPatch() {
+ self.t.GlobalPress(self.t.keys.Universal.CreatePatchOptionsMenu)
+
+ self.t.ExpectPopup().Menu().
+ Title(Equals("Patch options")).
+ Select(Contains("Reset patch")).
+ Confirm()
+}
diff --git a/pkg/integration/components/views.go b/pkg/integration/components/views.go
index edb2b85b6..873aca650 100644
--- a/pkg/integration/components/views.go
+++ b/pkg/integration/components/views.go
@@ -147,3 +147,7 @@ func (self *Views) Search() *ViewDriver {
func (self *Views) Tooltip() *ViewDriver {
return self.regularView("tooltip")
}
+
+func (self *Views) Options() *ViewDriver {
+ return self.regularView("options")
+}
diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go
index 2406b2f78..f4693faef 100644
--- a/pkg/integration/tests/test_list.go
+++ b/pkg/integration/tests/test_list.go
@@ -269,6 +269,7 @@ var tests = []*components.IntegrationTest{
ui.Accordion,
ui.DoublePopup,
ui.EmptyMenu,
+ ui.ModeSpecificKeybindingSuggestions,
ui.OpenLinkFailure,
ui.RangeSelect,
ui.SwitchTabFromMenu,
diff --git a/pkg/integration/tests/ui/mode_specific_keybinding_suggestions.go b/pkg/integration/tests/ui/mode_specific_keybinding_suggestions.go
new file mode 100644
index 000000000..f11e5fd27
--- /dev/null
+++ b/pkg/integration/tests/ui/mode_specific_keybinding_suggestions.go
@@ -0,0 +1,118 @@
+package ui
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/config"
+ . "github.com/jesseduffield/lazygit/pkg/integration/components"
+ "github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
+)
+
+var ModeSpecificKeybindingSuggestions = NewIntegrationTest(NewIntegrationTestArgs{
+ Description: "When in various modes, we should corresponding keybinding suggestions onscreen",
+ ExtraCmdArgs: []string{},
+ Skip: false,
+ SetupConfig: func(config *config.AppConfig) {},
+ SetupRepo: func(shell *Shell) {
+ shell.CreateNCommits(2)
+ shell.NewBranch("base-branch")
+ shared.MergeConflictsSetup(shell)
+ shell.Checkout("base-branch")
+ },
+ Run: func(t *TestDriver, keys config.KeybindingConfig) {
+ rebaseSuggestion := "View rebase options: m"
+ cherryPickSuggestion := "Paste (cherry-pick): V"
+ bisectSuggestion := "View bisect options: b"
+ customPatchSuggestion := "View custom patch options: <c-p>"
+ mergeSuggestion := "View merge options: m"
+
+ t.Views().Commits().
+ Focus().
+ Lines(
+ Contains("commit 02").IsSelected(),
+ Contains("commit 01"),
+ ).
+ Tap(func() {
+ // These suggestions are mode-specific so are not shown by default
+ t.Views().Options().Content(
+ DoesNotContain(rebaseSuggestion).
+ DoesNotContain(mergeSuggestion).
+ DoesNotContain(cherryPickSuggestion).
+ DoesNotContain(bisectSuggestion).
+ DoesNotContain(customPatchSuggestion),
+ )
+ }).
+ // Start an interactive rebase
+ Press(keys.Universal.Edit).
+ Tap(func() {
+ // Confirm the rebase suggestion now appears
+ t.Views().Options().Content(Contains(rebaseSuggestion))
+ }).
+ Press(keys.Commits.CherryPickCopy).
+ Tap(func() {
+ // Confirm the cherry pick suggestion now appears
+ t.Views().Options().Content(Contains(cherryPickSuggestion))
+ // Importantly, we show multiple of these suggestions at once
+ t.Views().Options().Content(Contains(rebaseSuggestion))
+ }).
+ // Cancel the cherry pick
+ PressEscape().
+ Tap(func() {
+ t.Views().Options().Content(DoesNotContain(cherryPickSuggestion))
+ }).
+ // Cancel the rebase
+ Tap(func() {
+ t.Common().AbortRebase()
+
+ t.Views().Options().Content(DoesNotContain(rebaseSuggestion))
+ }).
+ Press(keys.Commits.ViewBisectOptions).
+ Tap(func() {
+ t.ExpectPopup().Menu().
+ Title(Equals("Bisect")).
+ Select(MatchesRegexp("Mark.* as bad")).
+ Confirm()
+
+ t.Views().Options().Content(Contains(bisectSuggestion))
+
+ // Cancel bisect
+ t.Common().ResetBisect()
+
+ t.Views().Options().Content(DoesNotContain(bisectSuggestion))
+ }).
+ // Enter commit files view
+ PressEnter()
+
+ t.Views().CommitFiles().
+ IsFocused().
+ // Add a commit file to the patch
+ Press(keys.Universal.Select).
+ Tap(func() {
+ t.Views().Options().Content(Contains(customPatchSuggestion))
+
+ t.Common().ResetCustomPatch()
+
+ t.Views().Options().Content(DoesNotContain(customPatchSuggestion))
+ })
+
+ // Test merge options suggestion
+ t.Views().Branches().
+ Focus().
+ NavigateToLine(Contains("first-change-branch")).
+ Press(keys.Universal.Select).
+ NavigateToLine(Contains("second-change-branch")).
+ Press(keys.Branches.MergeIntoCurrentBranch).
+ Tap(func() {
+ t.ExpectPopup().Confirmation().
+ Title(Equals("Merge")).
+ Content(Contains("Are you sure you want to merge")).
+ Confirm()
+
+ t.Common().AcknowledgeConflicts()
+
+ t.Views().Options().Content(Contains(mergeSuggestion))
+
+ t.Common().AbortMerge()
+
+ t.Views().Options().Content(DoesNotContain(mergeSuggestion))
+ })
+ },
+})