summaryrefslogtreecommitdiffstats
path: root/pkg/gui/controllers/bisect_controller.go
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-16 14:46:53 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-03-17 19:13:40 +1100
commit1dd7307fde033dae5fececac15810a99e26c3d91 (patch)
tree4e851c9e3229a6fe3b4191f6311d05d7a9142960 /pkg/gui/controllers/bisect_controller.go
parenta90b6efded49abcfa2516db794d7875b0396f558 (diff)
start moving commit panel handlers into controller
more and more move rebase commit refreshing into existing abstraction and more and more WIP and more handling clicks properly fix merge conflicts update cheatsheet lots more preparation to start moving things into controllers WIP better typing expand on remotes controller moving more code into controllers
Diffstat (limited to 'pkg/gui/controllers/bisect_controller.go')
-rw-r--r--pkg/gui/controllers/bisect_controller.go273
1 files changed, 273 insertions, 0 deletions
diff --git a/pkg/gui/controllers/bisect_controller.go b/pkg/gui/controllers/bisect_controller.go
new file mode 100644
index 000000000..674e79f76
--- /dev/null
+++ b/pkg/gui/controllers/bisect_controller.go
@@ -0,0 +1,273 @@
+package controllers
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/jesseduffield/lazygit/pkg/commands"
+ "github.com/jesseduffield/lazygit/pkg/commands/git_commands"
+ "github.com/jesseduffield/lazygit/pkg/commands/models"
+ "github.com/jesseduffield/lazygit/pkg/config"
+ "github.com/jesseduffield/lazygit/pkg/gui/popup"
+ "github.com/jesseduffield/lazygit/pkg/gui/types"
+)
+
+type BisectController struct {
+ c *ControllerCommon
+ context types.IListContext
+ git *commands.GitCommand
+
+ getSelectedLocalCommit func() *models.Commit
+ getCommits func() []*models.Commit
+}
+
+var _ types.IController = &BisectController{}
+
+func NewBisectController(
+ c *ControllerCommon,
+ context types.IListContext,
+ git *commands.GitCommand,
+
+ getSelectedLocalCommit func() *models.Commit,
+ getCommits func() []*models.Commit,
+) *BisectController {
+ return &BisectController{
+ c: c,
+ context: context,
+ git: git,
+
+ getSelectedLocalCommit: getSelectedLocalCommit,
+ getCommits: getCommits,
+ }
+}
+
+func (self *BisectController) Keybindings(getKey func(key string) interface{}, config config.KeybindingConfig, guards types.KeybindingGuards) []*types.Binding {
+ bindings := []*types.Binding{
+ {
+ Key: getKey(config.Commits.ViewBisectOptions),
+ Handler: guards.OutsideFilterMode(self.checkSelected(self.openMenu)),
+ Description: self.c.Tr.LcViewBisectOptions,
+ OpensMenu: true,
+ },
+ }
+
+ return bindings
+}
+
+func (self *BisectController) openMenu(commit *models.Commit) error {
+ // no shame in getting this directly rather than using the cached value
+ // given how cheap it is to obtain
+ info := self.git.Bisect.GetInfo()
+ if info.Started() {
+ return self.openMidBisectMenu(info, commit)
+ } else {
+ return self.openStartBisectMenu(info, commit)
+ }
+}
+
+func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
+ // if there is not yet a 'current' bisect commit, or if we have
+ // selected the current commit, we need to jump to the next 'current' commit
+ // after we perform a bisect action. The reason we don't unconditionally jump
+ // is that sometimes the user will want to go and mark a few commits as skipped
+ // in a row and they wouldn't want to be jumped back to the current bisect
+ // commit each time.
+ // Originally we were allowing the user to, from the bisect menu, select whether
+ // they were talking about the selected commit or the current bisect commit,
+ // and that was a bit confusing (and required extra keypresses).
+ selectCurrentAfter := info.GetCurrentSha() == "" || info.GetCurrentSha() == commit.Sha
+ // we need to wait to reselect if our bisect commits aren't ancestors of our 'start'
+ // ref, because we'll be reloading our commits in that case.
+ waitToReselect := selectCurrentAfter && !self.git.Bisect.ReachableFromStart(info)
+
+ menuItems := []*popup.MenuItem{
+ {
+ DisplayString: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
+ OnPress: func() error {
+ self.c.LogAction(self.c.Tr.Actions.BisectMark)
+ if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.afterMark(selectCurrentAfter, waitToReselect)
+ },
+ },
+ {
+ DisplayString: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
+ OnPress: func() error {
+ self.c.LogAction(self.c.Tr.Actions.BisectMark)
+ if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.afterMark(selectCurrentAfter, waitToReselect)
+ },
+ },
+ {
+ DisplayString: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
+ OnPress: func() error {
+ self.c.LogAction(self.c.Tr.Actions.BisectSkip)
+ if err := self.git.Bisect.Skip(commit.Sha); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.afterMark(selectCurrentAfter, waitToReselect)
+ },
+ },
+ {
+ DisplayString: self.c.Tr.Bisect.ResetOption,
+ OnPress: func() error {
+ return self.Reset()
+ },
+ },
+ }
+
+ return self.c.Menu(popup.CreateMenuOptions{
+ Title: self.c.Tr.Bisect.BisectMenuTitle,
+ Items: menuItems,
+ })
+}
+
+func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo, commit *models.Commit) error {
+ return self.c.Menu(popup.CreateMenuOptions{
+ Title: self.c.Tr.Bisect.BisectMenuTitle,
+ Items: []*popup.MenuItem{
+ {
+ DisplayString: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
+ OnPress: func() error {
+ self.c.LogAction(self.c.Tr.Actions.StartBisect)
+ if err := self.git.Bisect.Start(); err != nil {
+ return self.c.Error(err)
+ }
+
+ if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.postBisectCommandRefresh()
+ },
+ },
+ {
+ DisplayString: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
+ OnPress: func() error {
+ self.c.LogAction(self.c.Tr.Actions.StartBisect)
+ if err := self.git.Bisect.Start(); err != nil {
+ return self.c.Error(err)
+ }
+
+ if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.postBisectCommandRefresh()
+ },
+ },
+ },
+ })
+}
+
+func (self *BisectController) Reset() error {
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.Bisect.ResetTitle,
+ Prompt: self.c.Tr.Bisect.ResetPrompt,
+ HandleConfirm: func() error {
+ self.c.LogAction(self.c.Tr.Actions.ResetBisect)
+ if err := self.git.Bisect.Reset(); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.postBisectCommandRefresh()
+ },
+ })
+}
+
+func (self *BisectController) showBisectCompleteMessage(candidateShas []string) error {
+ prompt := self.c.Tr.Bisect.CompletePrompt
+ if len(candidateShas) > 1 {
+ prompt = self.c.Tr.Bisect.CompletePromptIndeterminate
+ }
+
+ formattedCommits, err := self.git.Commit.GetCommitsOneline(candidateShas)
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.c.Ask(popup.AskOpts{
+ Title: self.c.Tr.Bisect.CompleteTitle,
+ Prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
+ HandleConfirm: func() error {
+ self.c.LogAction(self.c.Tr.Actions.ResetBisect)
+ if err := self.git.Bisect.Reset(); err != nil {
+ return self.c.Error(err)
+ }
+
+ return self.postBisectCommandRefresh()
+ },
+ })
+}
+
+func (self *BisectController) afterMark(selectCurrent bool, waitToReselect bool) error {
+ done, candidateShas, err := self.git.Bisect.IsDone()
+ if err != nil {
+ return self.c.Error(err)
+ }
+
+ if err := self.afterBisectMarkRefresh(selectCurrent, waitToReselect); err != nil {
+ return self.c.Error(err)
+ }
+
+ if done {
+ return self.showBisectCompleteMessage(candidateShas)
+ }
+
+ return nil
+}
+
+func (self *BisectController) postBisectCommandRefresh() error {
+ return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{}})
+}
+
+func (self *BisectController) afterBisectMarkRefresh(selectCurrent bool, waitToReselect bool) error {
+ selectFn := func() {
+ if selectCurrent {
+ self.selectCurrentBisectCommit()
+ }
+ }
+
+ if waitToReselect {
+ return self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{}, Then: selectFn})
+ } else {
+ selectFn()
+
+ return self.postBisectCommandRefresh()
+ }
+}
+
+func (self *BisectController) selectCurrentBisectCommit() {
+ info := self.git.Bisect.GetInfo()
+ if info.GetCurrentSha() != "" {
+ // find index of commit with that sha, move cursor to that.
+ for i, commit := range self.getCommits() {
+ if commit.Sha == info.GetCurrentSha() {
+ self.context.GetPanelState().SetSelectedLineIdx(i)
+ _ = self.context.HandleFocus()
+ break
+ }
+ }
+ }
+}
+
+func (self *BisectController) checkSelected(callback func(*models.Commit) error) func() error {
+ return func() error {
+ commit := self.getSelectedLocalCommit()
+ if commit == nil {
+ return nil
+ }
+
+ return callback(commit)
+ }
+}
+
+func (self *BisectController) Context() types.Context {
+ return self.context
+}