summaryrefslogtreecommitdiffstats
path: root/pkg/gui
diff options
context:
space:
mode:
authorMark Kopenga <mkopenga@gmail.com>2018-12-08 16:41:39 +0100
committerGitHub <noreply@github.com>2018-12-08 16:41:39 +0100
commit19a6a32625d59c61ecfe91ef8626390eb34a922a (patch)
treeb02d1a9c8477d2614f99cc80b40b54accf28c127 /pkg/gui
parent270658fc009dc254a80501fc1c9f5f82acd27239 (diff)
parentff856b763033a241370bfde98d7386b43b7d0893 (diff)
Merge branch 'master' into https-ask-for-username-password
Diffstat (limited to 'pkg/gui')
-rw-r--r--pkg/gui/branches_panel.go187
-rw-r--r--pkg/gui/commit_message_panel.go10
-rw-r--r--pkg/gui/commits_panel.go124
-rw-r--r--pkg/gui/confirmation_panel.go11
-rw-r--r--pkg/gui/files_panel.go137
-rw-r--r--pkg/gui/gui.go95
-rw-r--r--pkg/gui/keybindings.go36
-rw-r--r--pkg/gui/menu_panel.go35
-rw-r--r--pkg/gui/merge_panel.go7
-rw-r--r--pkg/gui/staging_panel.go62
-rw-r--r--pkg/gui/stash_panel.go72
-rw-r--r--pkg/gui/status_panel.go15
-rw-r--r--pkg/gui/view_helpers.go209
13 files changed, 613 insertions, 387 deletions
diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go
index 059f0af50..e54d6e8c1 100644
--- a/pkg/gui/branches_panel.go
+++ b/pkg/gui/branches_panel.go
@@ -7,25 +7,116 @@ import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/git"
- "github.com/jesseduffield/lazygit/pkg/utils"
)
+// list panel functions
+
+func (gui *Gui) getSelectedBranch() *commands.Branch {
+ selectedLine := gui.State.Panels.Branches.SelectedLine
+ if selectedLine == -1 {
+ return nil
+ }
+
+ return gui.State.Branches[selectedLine]
+}
+
+// may want to standardise how these select methods work
+func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
+ // This really shouldn't happen: there should always be a master branch
+ if len(gui.State.Branches) == 0 {
+ return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
+ }
+ branch := gui.getSelectedBranch()
+ if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil {
+ return err
+ }
+ go func() {
+ _ = gui.RenderSelectedBranchUpstreamDifferences()
+ }()
+ go func() {
+ graph, err := gui.GitCommand.GetBranchGraph(branch.Name)
+ if err != nil && strings.HasPrefix(graph, "fatal: ambiguous argument") {
+ graph = gui.Tr.SLocalize("NoTrackingThisBranch")
+ }
+ _ = gui.renderString(g, "main", graph)
+ }()
+ return nil
+}
+
+func (gui *Gui) RenderSelectedBranchUpstreamDifferences() error {
+ // here we tell the selected branch that it is selected.
+ // this is necessary for showing stats on a branch that is selected, because
+ // the displaystring function doesn't have access to gui state to tell if it's selected
+ for i, branch := range gui.State.Branches {
+ branch.Selected = i == gui.State.Panels.Branches.SelectedLine
+ }
+
+ branch := gui.getSelectedBranch()
+ branch.Pushables, branch.Pullables = gui.GitCommand.GetBranchUpstreamDifferenceCount(branch.Name)
+ return gui.renderListPanel(gui.getBranchesView(gui.g), gui.State.Branches)
+}
+
+// gui.refreshStatus is called at the end of this because that's when we can
+// be sure there is a state.Branches array to pick the current branch from
+func (gui *Gui) refreshBranches(g *gocui.Gui) error {
+ g.Update(func(g *gocui.Gui) error {
+ builder, err := git.NewBranchListBuilder(gui.Log, gui.GitCommand)
+ if err != nil {
+ return err
+ }
+ gui.State.Branches = builder.Build()
+
+ gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
+ if err := gui.resetOrigin(gui.getBranchesView(gui.g)); err != nil {
+ return err
+ }
+ if err := gui.RenderSelectedBranchUpstreamDifferences(); err != nil {
+ return err
+ }
+
+ return gui.refreshStatus(g)
+ })
+ return nil
+}
+
+func (gui *Gui) handleBranchesNextLine(g *gocui.Gui, v *gocui.View) error {
+ panelState := gui.State.Panels.Branches
+ gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), false)
+ return gui.handleBranchSelect(gui.g, v)
+}
+
+func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error {
+ panelState := gui.State.Panels.Branches
+ gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Branches), true)
+
+ return gui.handleBranchSelect(gui.g, v)
+}
+
+// specific functions
+
func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error {
- index := gui.getItemPosition(gui.getBranchesView(g))
- if index == 0 {
+ if gui.State.Panels.Branches.SelectedLine == -1 {
+ return nil
+ }
+ if gui.State.Panels.Branches.SelectedLine == 0 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch"))
}
- branch := gui.getSelectedBranch(gui.getBranchesView(g))
+ branch := gui.getSelectedBranch()
if err := gui.GitCommand.Checkout(branch.Name, false); err != nil {
- gui.createErrorPanel(g, err.Error())
+ if err := gui.createErrorPanel(g, err.Error()); err != nil {
+ return err
+ }
+ } else {
+ gui.State.Panels.Branches.SelectedLine = 0
}
+
return gui.refreshSidePanels(g)
}
func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error {
- branch := gui.getSelectedBranch(gui.getBranchesView(g))
pullRequest := commands.NewPullRequest(gui.GitCommand)
+ branch := gui.getSelectedBranch()
if err := pullRequest.Create(branch); err != nil {
return gui.createErrorPanel(g, err.Error())
}
@@ -60,7 +151,7 @@ func (gui *Gui) handleGitFetch(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error {
- branch := gui.getSelectedBranch(v)
+ branch := gui.getSelectedBranch()
message := gui.Tr.SLocalize("SureForceCheckout")
title := gui.Tr.SLocalize("ForceCheckoutBranch")
return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error {
@@ -108,8 +199,11 @@ func (gui *Gui) handleForceDeleteBranch(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error {
+ selectedBranch := gui.getSelectedBranch()
+ if selectedBranch == nil {
+ return nil
+ }
checkedOutBranch := gui.State.Branches[0]
- selectedBranch := gui.getSelectedBranch(v)
if checkedOutBranch.Name == selectedBranch.Name {
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantDeleteCheckOutBranch"))
}
@@ -144,7 +238,7 @@ func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *c
func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
checkedOutBranch := gui.State.Branches[0]
- selectedBranch := gui.getSelectedBranch(v)
+ selectedBranch := gui.getSelectedBranch()
defer gui.refreshSidePanels(g)
if checkedOutBranch.Name == selectedBranch.Name {
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself"))
@@ -155,59 +249,36 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
return nil
}
-func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch {
- lineNumber := gui.getItemPosition(v)
- return gui.State.Branches[lineNumber]
-}
-
-func (gui *Gui) renderBranchesOptions(g *gocui.Gui) error {
- return gui.renderGlobalOptions(g)
-}
-
-// may want to standardise how these select methods work
-func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error {
- if err := gui.renderBranchesOptions(g); err != nil {
- return err
+func (gui *Gui) handleFastForward(g *gocui.Gui, v *gocui.View) error {
+ branch := gui.getSelectedBranch()
+ if branch == nil {
+ return nil
}
- // This really shouldn't happen: there should always be a master branch
- if len(gui.State.Branches) == 0 {
- return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo"))
+ if branch.Pushables == "" {
+ return nil
+ }
+ if branch.Pushables == "?" {
+ return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with no upstream")
+ }
+ if branch.Pushables != "0" {
+ return gui.createErrorPanel(gui.g, "Cannot fast-forward a branch with commits to push")
}
+ upstream := "origin" // hardcoding for now
+ message := gui.Tr.TemplateLocalize(
+ "Fetching",
+ Teml{
+ "from": fmt.Sprintf("%s/%s", upstream, branch.Name),
+ "to": branch.Name,
+ },
+ )
go func() {
- branch := gui.getSelectedBranch(v)
- diff, err := gui.GitCommand.GetBranchGraph(branch.Name)
- if err != nil && strings.HasPrefix(diff, "fatal: ambiguous argument") {
- diff = gui.Tr.SLocalize("NoTrackingThisBranch")
+ _ = gui.createMessagePanel(gui.g, v, "", message)
+ if err := gui.GitCommand.FastForward(branch.Name); err != nil {
+ _ = gui.createErrorPanel(gui.g, err.Error())
+ } else {
+ _ = gui.closeConfirmationPrompt(gui.g)
+ _ = gui.RenderSelectedBranchUpstreamDifferences()
}
- gui.renderString(g, "main", diff)
}()
return nil
}
-
-// gui.refreshStatus is called at the end of this because that's when we can
-// be sure there is a state.Branches array to pick the current branch from
-func (gui *Gui) refreshBranches(g *gocui.Gui) error {
- g.Update(func(g *gocui.Gui) error {
- v, err := g.View("branches")
- if err != nil {
- panic(err)
- }
- builder, err := git.NewBranchListBuilder(gui.Log, gui.GitCommand)
- if err != nil {
- return err
- }
- gui.State.Branches = builder.Build()
-
- v.Clear()
- list, err := utils.RenderList(gui.State.Branches)
- if err != nil {
- return err
- }
-
- fmt.Fprint(v, list)
-
- gui.resetOrigin(v)
- return gui.refreshStatus(g)
- })
- return nil
-}
diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go
index 14e3ab795..85497ef87 100644
--- a/pkg/gui/commit_message_panel.go
+++ b/pkg/gui/commit_message_panel.go
@@ -23,12 +23,12 @@ func (gui *Gui) handleCommitConfirm(g *gocui.Gui, v *gocui.View) error {
gui.SubProcess = sub
return gui.Errors.ErrSubProcess
}
- gui.refreshFiles(g)
v.Clear()
- v.SetCursor(0, 0)
- g.SetViewOnBottom("commitMessage")
- gui.switchFocus(g, v, gui.getFilesView(g))
- return gui.refreshCommits(g)
+ _ = v.SetCursor(0, 0)
+ _ = v.SetOrigin(0, 0)
+ _, _ = g.SetViewOnBottom("commitMessage")
+ _ = gui.switchFocus(g, v, gui.getFilesView(g))
+ return gui.refreshSidePanels(g)
}
func (gui *Gui) handleCommitClose(g *gocui.Gui, v *gocui.View) error {
diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go
index ee7f191a7..4ff79960d 100644
--- a/pkg/gui/commits_panel.go
+++ b/pkg/gui/commits_panel.go
@@ -9,29 +9,54 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
+// list panel functions
+
+func (gui *Gui) getSelectedCommit(g *gocui.Gui) *commands.Commit {
+ selectedLine := gui.State.Panels.Commits.SelectedLine
+ if selectedLine == -1 {
+ return nil
+ }
+
+ return gui.State.Commits[selectedLine]
+}
+
+func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
+ commit := gui.getSelectedCommit(g)
+ if commit == nil {
+ return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
+ }
+
+ if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, v); err != nil {
+ return err
+ }
+ commitText, err := gui.GitCommand.Show(commit.Sha)
+ if err != nil {
+ return err
+ }
+ return gui.renderString(g, "main", commitText)
+}
+
func (gui *Gui) refreshCommits(g *gocui.Gui) error {
g.Update(func(*gocui.Gui) error {
commits, err := gui.GitCommand.GetCommits()
if err != nil {
return err
}
-
gui.State.Commits = commits
- v, err := g.View("commits")
- if err != nil {
- return err
- }
- v.Clear()
+ gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
list, err := utils.RenderList(gui.State.Commits)
if err != nil {
return err
}
+
+ v := gui.getCommitsView(gui.g)
+ v.Clear()
fmt.Fprint(v, list)
gui.refreshStatus(g)
- if g.CurrentView().Name() == "commits" {
+ if v == g.CurrentView() {
gui.handleCommitSelect(g, v)
}
return nil
@@ -39,11 +64,27 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
return nil
}
+func (gui *Gui) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error {
+ panelState := gui.State.Panels.Commits
+ gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), false)
+
+ return gui.handleCommitSelect(gui.g, v)
+}
+
+func (gui *Gui) handleCommitsPrevLine(g *gocui.Gui, v *gocui.View) error {
+ panelState := gui.State.Panels.Commits
+ gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true)
+
+ return gui.handleCommitSelect(gui.g, v)
+}
+
+// specific functions
+
func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error {
return gui.createConfirmationPanel(g, commitView, gui.Tr.SLocalize("ResetToCommit"), gui.Tr.SLocalize("SureResetThisCommit"), func(g *gocui.Gui, v *gocui.View) error {
- commit, err := gui.getSelectedCommit(g)
- if err != nil {
- panic(err)
+ commit := gui.getSelectedCommit(g)
+ if commit == nil {
+ panic(errors.New(gui.Tr.SLocalize("NoCommitsThisBranch")))
}
if err := gui.GitCommand.ResetToCommit(commit.Sha); err != nil {
return gui.createErrorPanel(g, err.Error())
@@ -55,42 +96,21 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
panic(err)
}
gui.resetOrigin(commitView)
- return gui.handleCommitSelect(g, nil)
+ gui.State.Panels.Commits.SelectedLine = 0
+ return gui.handleCommitSelect(g, commitView)
}, nil)
}
-func (gui *Gui) renderCommitsOptions(g *gocui.Gui) error {
- return gui.renderGlobalOptions(g)
-}
-
-func (gui *Gui) handleCommitSelect(g *gocui.Gui, v *gocui.View) error {
- if err := gui.renderCommitsOptions(g); err != nil {
- return err
- }
- commit, err := gui.getSelectedCommit(g)
- if err != nil {
- if err.Error() != gui.Tr.SLocalize("NoCommitsThisBranch") {
- return err
- }
- return gui.renderString(g, "main", gui.Tr.SLocalize("NoCommitsThisBranch"))
- }
- commitText, err := gui.GitCommand.Show(commit.Sha)
- if err != nil {
- return err
- }
- return gui.renderString(g, "main", commitText)
-}
-
func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
- if gui.getItemPosition(v) != 0 {
+ if gui.State.Panels.Commits.SelectedLine != 0 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlySquashTopmostCommit"))
}
- if len(gui.State.Commits) == 1 {
+ if len(gui.State.Commits) <= 1 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
}
- commit, err := gui.getSelectedCommit(g)
- if err != nil {
- return err
+ commit := gui.getSelectedCommit(g)
+ if commit == nil {
+ return errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
}
if err := gui.GitCommand.SquashPreviousTwoCommits(commit.Name); err != nil {
return gui.createErrorPanel(g, err.Error())
@@ -113,16 +133,16 @@ func (gui *Gui) anyUnStagedChanges(files []*commands.File) bool {
}
func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
- if len(gui.State.Commits) == 1 {
+ if len(gui.State.Commits) <= 1 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("YouNoCommitsToSquash"))
}
if gui.anyUnStagedChanges(gui.State.Files) {
return gui.createErrorPanel(g, gui.Tr.SLocalize("CantFixupWhileUnstagedChanges"))
}
branch := gui.State.Branches[0]
- commit, err := gui.getSelectedCommit(g)
- if err != nil {
- return err
+ commit := gui.getSelectedCommit(g)
+ if commit == nil {
+ return gui.createErrorPanel(g, gui.Tr.SLocalize("NoCommitsThisBranch"))
}
message := gui.Tr.SLocalize("SureFixupThisCommit")
gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("Fixup"), message, func(g *gocui.Gui, v *gocui.View) error {
@@ -138,7 +158,7 @@ func (gui *Gui) handleCommitFixup(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
- if gui.getItemPosition(gui.getCommitsView(g)) != 0 {
+ if gui.State.Panels.Commits.SelectedLine != 0 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
}
return gui.createPromptPanel(g, v, gui.Tr.SLocalize("renameCommit"), func(g *gocui.Gui, v *gocui.View) error {
@@ -153,7 +173,7 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error {
}
func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
- if gui.getItemPosition(gui.getCommitsView(g)) != 0 {
+ if gui.State.Panels.Commits.SelectedLine != 0 {
return gui.createErrorPanel(g, gui.Tr.SLocalize("OnlyRenameTopCommit"))
}
@@ -164,19 +184,3 @@ func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error {
return nil
}
-
-func (gui *Gui) getSelectedCommit(g *gocui.Gui) (*commands.Commit, error) {
- v, err := g.View("commits")
- if err != nil {
- panic(err)
- }
- if len(gui.State.Commits) == 0 {
- return &commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch"))
- }
- lineNumber := gui.getItemPosition(v)
- if lineNumber > len(gui.State.Commits)-1 {
- gui.Log.Info(gui.Tr.SLocalize("PotentialErrInGetselectedCommit"), gui.State.Commits, lineNumber)
- return gui.State.Commits[len(gui.State.Commits)-1], nil
- }
- return gui.State.Commits[lineNumber], nil
-}
diff --git a/pkg/gui/confirmation_panel.go b/pkg/gui/confirmation_panel.go
index 75349777c..521bd827d 100644
--- a/pkg/gui/confirmation_panel.go
+++ b/pkg/gui/confirmation_panel.go
@@ -28,7 +28,7 @@ func (gui *Gui) wrappedConfirmationFunction(function func(*gocui.Gui, *gocui.Vie
func (gui *Gui) closeConfirmationPrompt(g *gocui.Gui) error {
view, err := g.View("confirmation")
if err != nil {
- panic(err)
+ return nil // if it's already been closed we can just return
}
if err := gui.returnFocus(g, view); err != nil {
panic(err)
@@ -77,11 +77,10 @@ func (gui *Gui) prepareConfirmationPanel(currentView *gocui.View, title, prompt
confirmationView.Wrap = true
confirmationView.FgColor = gocui.ColorWhite
}
- confirmationView.Clear()
-
- if err := gui.switchFocus(gui.g, currentView, confirmationView); err != nil {
- return nil, err
- }
+ gui.g.Update(func(g *gocui.Gui) error {
+ confirmationView.Clear()
+ return gui.switchFocus(gui.g, currentView, confirmationView)
+ })
return confirmationView, nil
}
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index cd8df1859..7942e421b 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -15,6 +15,79 @@ import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
+// list panel functions
+
+func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
+ selectedLine := gui.State.Panels.Files.SelectedLine
+ if selectedLine == -1 {
+ return &commands.File{}, gui.Errors.ErrNoFiles
+ }
+
+ return gui.State.Files[selectedLine], nil
+}
+
+func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
+ file, err := gui.getSelectedFile(g)
+ if err != nil {
+ if err != gui.Errors.ErrNoFiles {
+ return err
+ }
+ return gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
+ }
+
+ if file.HasMergeConflicts {
+ return gui.refreshMergePanel(g)
+ }
+
+ if err := gui.focusPoint(0, gui.State.Panels.Files.SelectedLine, v); err != nil {
+ return err
+ }
+
+ content := gui.GitCommand.Diff(file, false)
+ return gui.renderString(g, "main", content)
+}
+
+func (gui *Gui) refreshFiles(g *gocui.Gui) error {
+ filesView, err := g.View("files")
+ if err != nil {
+ return err
+ }
+ gui.refreshStateFiles()
+
+ gui.g.Update(func(g *gocui.Gui) error {
+
+ filesView.Clear()
+ list, err := utils.RenderList(gui.State.Files)
+ if err != nil {
+ return err
+ }
+ fmt.Fprint(filesView, list)
+
+ if filesView == g.CurrentView() {
+ return gui.handleFileSelect(g, filesView)
+ }
+ return nil
+ })
+
+ return nil
+}
+
+func (gui *Gui) handleFilesNextLine(g *gocui.Gui, v *gocui.View) error {
+ panelState := gui.State.Panels.Files
+ gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), false)
+
+ return gui.handleFileSelect(gui.g, v)
+}
+
+func (gui *Gui) handleFilesPrevLine(g *gocui.Gui, v *gocui.View) error {
+ panelState := gui.State.Panels.Files
+ gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Files), true)
+
+ return gui.handleFileSelect(gui.g, v)
+}
+
+// specific functions
+
func (gui *Gui) stagedFiles() []*commands.File {
files := gui.State.Files
result := make([]*commands.File, 0)
@@ -139,18 +212,6 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
return gui.Errors.ErrSubProcess
}
-func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
- if len(gui.State.Files) == 0 {
- return &commands.File{}, gui.Errors.ErrNoFiles
- }
- filesView, err := g.View("files")
- if err != nil {
- panic(err)
- }
- lineNumber := gui.getItemPosition(filesView)
- return gui.State.Files[lineNumber], nil
-}
-
func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
file, err := gui.getSelectedFile(g)
if err != nil {
@@ -194,30 +255,6 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error {
return gui.refreshFiles(g)
}
-func (gui *Gui) renderfilesOptions(g *gocui.Gui, file *commands.File) error {
- return gui.renderGlobalOptions(g)
-}
-
-func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error {
- file, err := gui.getSelectedFile(g)
- if err != nil {
- if err != gui.Errors.ErrNoFiles {
- return err
- }
- gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles"))
- return gui.renderfilesOptions(g, nil)
- }
- if err := gui.renderfilesOptions(g, file); err != nil {
- return err
- }
- if file.HasMergeConflicts {
- return gui.refreshMergePanel(g)
- }
-
- content := gui.GitCommand.Diff(file, false)
- return gui.renderString(g, "main", content)
-}
-
func (gui *Gui) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error {
if len(gui.stagedFiles()) == 0 && !gui.State.HasMergeConflicts {
return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit"))
@@ -309,6 +346,7 @@ func (gui *Gui) refreshStateFiles() {
// get files to stage
files := gui.GitCommand.GetStatusFiles()
gui.State.Files = gui.GitCommand.MergeStatusFiles(gui.State.Files, files)
+ gui.refreshSelectedLine(&gui.State.Panels.Files.SelectedLine, len(gui.State.Files))
gui.updateHasMergeConflictStatus()
}
@@ -340,27 +378,6 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) {
return cat, nil
}
-func (gui *Gui) refreshFiles(g *gocui.Gui) error {
- filesView, err := g.View("files")
- if err != nil {
- return err
- }
- gui.refreshStateFiles()
-
- filesView.Clear()
- list, err := utils.RenderList(gui.State.Files)
- if err != nil {
- return err
- }
- fmt.Fprint(filesView, list)
-
- gui.correctCursor(filesView)
- if filesView == g.CurrentView() {
- gui.handleFileSelect(g, filesView)
- }
- return nil
-}
-
func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error {
if err := gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("PullWait")); err != nil {
return err
@@ -424,7 +441,7 @@ func (gui *Gui) pushWithForceFlag(g *gocui.Gui, v *gocui.View, force bool) error
func (gui *Gui) pushFiles(g *gocui.Gui, v *gocui.View) error {
// if we have pullables we'll ask if the user wants to force push
- _, pullables := gui.GitCommand.UpstreamDifferenceCount()
+ _, pullables := gui.GitCommand.GetCurrentBranchUpstreamDifferenceCount()
if pullables == "?" || pullables == "0" {
return gui.pushWithForceFlag(g, v, false)
}
@@ -462,9 +479,9 @@ func (gui *Gui) handleAbortMerge(g *gocui.Gui, v *gocui.View) error {
return gui.refreshFiles(g)
}
-func (gui *Gui) handleResetHard(g *gocui.Gui, v *gocui.View) error {
+func (gui *Gui) handleResetAndClean(g *gocui.Gui, v *gocui.View) error {
return gui.createConfirmationPanel(g, v, gui.Tr.SLocalize("ClearFilePanel"), gui.Tr.SLocalize("SureResetHardHead"), func(g *gocui.Gui, v *gocui.View) error {
- if err := gui.GitCommand.ResetHard(); err != nil {
+ if err := gui.GitCommand.ResetAndClean(); err != nil {
gui.createErrorPanel(g, err.Error())
}
return gui.refreshFiles(g)
diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go
index 70e61c8d5..d47913c32 100644
--- a/pkg/gui/gui.go
+++ b/pkg/gui/gui.go
@@ -76,11 +76,43 @@ type Gui struct {
introAgree sync.WaitGroup
}
-type stagingState struct {
- StageableLines []int
- HunkStarts []int
- CurrentLineIndex int
- Diff string
+// for now the staging panel state, unlike the other panel states, is going to be
+// non-mutative, so that we don't accidentally end up
+// with mismatches of data. We might change this in the future
+type stagingPanelState struct {
+ SelectedLine int
+ StageableLines []int
+ HunkStarts []int
+ Diff string
+}
+
+type filePanelState struct {
+ SelectedLine int
+}
+
+type branchPanelState struct {
+ SelectedLine int
+}
+
+type commitPanelState struct {
+ SelectedLine int
+}
+
+type stashPanelState struct {
+ SelectedLine int
+}
+
+type menuPanelState struct {
+ SelectedLine int
+}
+
+type panelStates struct {
+ Files *filePanelState
+ Staging *stagingPanelState
+ Branches *branchPanelState
+ Commits *commitPanelState
+ Stash *stashPanelState
+ Menu *menuPanelState
}
type guiState struct {
@@ -96,7 +128,7 @@ type guiState struct {
EditHistory *stack.Stack
Platform commands.Platform
Updating bool
- StagingState *stagingState
+ Panels *panelStates
}
// NewGui builds a new gui handler
@@ -112,6 +144,13 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma
Conflicts: make([]commands.Conflict, 0),
EditHistory: stack.New(),
Platform: *oSCommand.Platform,
+ Panels: &panelStates{
+ Files: &filePanelState{SelectedLine: -1},
+ Branches: &branchPanelState{SelectedLine: 0},
+ Commits: &commitPanelState{SelectedLine: -1},
+ Stash: &stashPanelState{SelectedLine: -1},
+ Menu: &menuPanelState{SelectedLine: 0},
+ },
}
gui := &Gui{
@@ -197,9 +236,11 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
v.Title = gui.Tr.SLocalize("NotEnoughSpace")
v.Wrap = true
- g.SetCurrentView(v.Name())
+ g.SetViewOnTop("limit")
}
return nil
+ } else {
+ _, _ = g.SetViewOnBottom("limit")
}
g.DeleteView("limit")
@@ -251,12 +292,13 @@ func (gui *Gui) layout(g *gocui.Gui) error {
v.FgColor = gocui.ColorWhite
}
- if v, err := g.SetView("branches", 0, filesBranchesBoundary+panelSpacing, leftSideWidth, commitsBranchesBoundary, gocui.TOP|gocui.BOTTOM); err != nil {
+ branchesView, err := g.SetView("branches", 0, filesBranchesBoundary+panelSpacing, leftSideWidth, commitsBranchesBoundary, gocui.TOP|gocui.BOTTOM)
+ if err != nil {
if err != gocui.ErrUnknownView {
return err
}
- v.Title = gui.Tr.SLocalize("BranchesTitle")
- v.FgColor = gocui.ColorWhite
+ branchesView.Title = gui.Tr.SLocalize("BranchesTitle")
+ branchesView.FgColor = gocui.ColorWhite
}
if v, err := g.SetView("commits", 0, commitsBranchesBoundary+panelSpacing, leftSideWidth, commitsStashBoundary, gocui.TOP|gocui.BOTTOM); err != nil {
@@ -346,11 +388,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
return err
}
- gui.handleFileSelect(g, filesView)
- gui.refreshFiles(g)
- gui.refreshBranches(g)
- gui.refreshCommits(g)
- gui.refreshStashEntries(g)
+ if _, err := gui.g.SetCurrentView(filesView.Name()); err != nil {
+ return err
+ }
+
+ if err := gui.refreshSidePanels(gui.g); err != nil {
+ return err
+ }
+
if err := gui.switchFocus(g, nil, filesView); err != nil {
return err
}
@@ -362,6 +407,22 @@ func (gui *Gui) layout(g *gocui.Gui) error {
}
}
+ listViews := map[*gocui.View]int{
+ filesView: gui.State.Panels.Files.SelectedLine,
+ branchesView: gui.State.Panels.Branches.SelectedLine,
+ }
+ for view, selectedLine := range listViews {
+ // check if the selected line is now out of view and if so refocus it
+ if err := gui.focusPoint(0, selectedLine, view); err != nil {
+ return err
+ }
+ }
+
+ // here is a good place log some stuff
+ // if you download humanlog and do tail -f development.log | humanlog
+ // this will let you see these branches as prettified json
+ // gui.Log.Info(utils.AsJson(gui.State.Branches[0:4]))
+
return gui.resizeCurrentPopupPanel(g)
}
@@ -416,8 +477,8 @@ func (gui *Gui) renderAppStatus(g *gocui.Gui) error {
return nil
}
-func (gui *Gui) renderGlobalOptions(g *gocui.Gui) error {
- return gui.renderOptionsMap(g, map[string]string{
+func (gui *Gui) renderGlobalOptions() error {
+ return gui.renderOptionsMap(map[string]string{
"PgUp/PgDn": gui.Tr.SLocalize("scroll"),
"← → ↑ ↓": gui.Tr.SLocalize("navigate"),
"esc/q": gui.Tr.SLocalize("close"),
diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go
index 28647b245..978f6dcdf 100644
--- a/pkg/gui/keybindings.go
+++ b/pkg/gui/keybindings.go
@@ -210,7 +210,7 @@ func (gui *Gui) GetKeybindings() []*Binding {
Vie