diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index e1504bd69..809deaa7d 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -10,127 +10,9 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) -func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error { - index := gui.getItemPosition(gui.getBranchesView(g)) - if index == 0 { - return gui.createErrorPanel(g, gui.Tr.SLocalize("AlreadyCheckedOutBranch")) - } - branch := gui.getSelectedBranch(gui.getBranchesView(g)) - if err := gui.GitCommand.Checkout(branch.Name, false); err != nil { - gui.createErrorPanel(g, err.Error()) - } - return gui.refreshSidePanels(g) -} +// list panel functions -func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error { - branch := gui.getSelectedBranch(gui.getBranchesView(g)) - pullRequest := commands.NewPullRequest(gui.GitCommand) - - if err := pullRequest.Create(branch); err != nil { - return gui.createErrorPanel(g, err.Error()) - } - - return nil -} - -func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { - branch := gui.getSelectedBranch(v) - 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 { - if err := gui.GitCommand.Checkout(branch.Name, true); err != nil { - gui.createErrorPanel(g, err.Error()) - } - return gui.refreshSidePanels(g) - }, nil) -} - -func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error { - gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.Checkout(gui.trimmedContent(v), false); err != nil { - return gui.createErrorPanel(g, err.Error()) - } - return gui.refreshSidePanels(g) - }) - return nil -} - -func (gui *Gui) handleNewBranch(g *gocui.Gui, v *gocui.View) error { - branch := gui.State.Branches[0] - message := gui.Tr.TemplateLocalize( - "NewBranchNameBranchOff", - Teml{ - "branchName": branch.Name, - }, - ) - gui.createPromptPanel(g, v, message, func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.NewBranch(gui.trimmedContent(v)); err != nil { - return gui.createErrorPanel(g, err.Error()) - } - gui.refreshSidePanels(g) - return gui.handleBranchSelect(g, v) - }) - return nil -} - -func (gui *Gui) handleDeleteBranch(g *gocui.Gui, v *gocui.View) error { - return gui.deleteBranch(g, v, false) -} - -func (gui *Gui) handleForceDeleteBranch(g *gocui.Gui, v *gocui.View) error { - return gui.deleteBranch(g, v, true) -} - -func (gui *Gui) deleteBranch(g *gocui.Gui, v *gocui.View, force bool) error { - checkedOutBranch := gui.State.Branches[0] - selectedBranch := gui.getSelectedBranch(v) - if checkedOutBranch.Name == selectedBranch.Name { - return gui.createErrorPanel(g, gui.Tr.SLocalize("CantDeleteCheckOutBranch")) - } - return gui.deleteNamedBranch(g, v, selectedBranch, force) -} - -func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *commands.Branch, force bool) error { - title := gui.Tr.SLocalize("DeleteBranch") - var messageId string - if force { - messageId = "ForceDeleteBranchMessage" - } else { - messageId = "DeleteBranchMessage" - } - message := gui.Tr.TemplateLocalize( - messageId, - Teml{ - "selectedBranchName": selectedBranch.Name, - }, - ) - return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error { - if err := gui.GitCommand.DeleteBranch(selectedBranch.Name, force); err != nil { - errMessage := err.Error() - if !force && strings.Contains(errMessage, "is not fully merged") { - return gui.deleteNamedBranch(g, v, selectedBranch, true) - } else { - return gui.createErrorPanel(g, errMessage) - } - } - return gui.refreshSidePanels(g) - }, nil) -} - -func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error { - checkedOutBranch := gui.State.Branches[0] - selectedBranch := gui.getSelectedBranch(v) - defer gui.refreshSidePanels(g) - if checkedOutBranch.Name == selectedBranch.Name { - return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself")) - } - if err := gui.GitCommand.Merge(selectedBranch.Name); err != nil { - return gui.createErrorPanel(g, err.Error()) - } - return nil -} - -func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch { +func (gui *Gui) getSelectedBranch() *commands.Branch { selectedLine := gui.State.Panels.Branches.SelectedLine if selectedLine == -1 { return nil @@ -145,7 +27,7 @@ func (gui *Gui) handleBranchSelect(g *gocui.Gui, v *gocui.View) error { if len(gui.State.Branches) == 0 { return gui.renderString(g, "main", gui.Tr.SLocalize("NoBranchesThisRepo")) } - branch := gui.getSelectedBranch(v) + branch := gui.getSelectedBranch() if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil { return err } @@ -202,3 +84,132 @@ func (gui *Gui) handleBranchesPrevLine(g *gocui.Gui, v *gocui.View) error { return gui.handleBranchSelect(gui.g, v) } + +// specific functions + +func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error { + 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() + if err := gui.GitCommand.Checkout(branch.Name, false); err != nil { + if err := gui.createErrorPanel(g, err.Error()); err != nil { + return err + } + } + return gui.refreshSidePanels(g) +} + +func (gui *Gui) handleCreatePullRequestPress(g *gocui.Gui, v *gocui.View) error { + pullRequest := commands.NewPullRequest(gui.GitCommand) + + branch := gui.getSelectedBranch() + if err := pullRequest.Create(branch); err != nil { + return gui.createErrorPanel(g, err.Error()) + } + + return nil +} + +func (gui *Gui) handleForceCheckout(g *gocui.Gui, v *gocui.View) error { + 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 { + if err := gui.GitCommand.Checkout(branch.Name, true); err != nil { + gui.createErrorPanel(g, err.Error()) + } + return gui.refreshSidePanels(g) + }, nil) +} + +func (gui *Gui) handleCheckoutByName(g *gocui.Gui, v *gocui.View) error { + gui.createPromptPanel(g, v, gui.Tr.SLocalize("BranchName")+":", func(g *gocui.Gui, v *gocui.View) error { + if err := gui.GitCommand.Checkout(gui.trimmedContent(v), false); err != nil { + return gui.createErrorPanel(g, err.Error()) + } + return gui.refreshSidePanels(g) + }) + return nil +} + +func (gui *Gui) handleNewBranch(g *gocui.Gui, v *gocui.View) error { + branch := gui.State.Branches[0] + message := gui.Tr.TemplateLocalize( + "NewBranchNameBranchOff", + Teml{ + "branchName": branch.Name, + }, + ) + gui.createPromptPanel(g, v, message, func(g *gocui.Gui, v *gocui.View) error { + if err := gui.GitCommand.NewBranch(gui.trimmedContent(v)); err != nil { + return gui.createErrorPanel(g, err.Error()) + } + gui.refreshSidePanels(g) + return gui.handleBranchSelect(g, v) + }) + return nil +} + +func (gui *Gui) handleDeleteBranch(g *gocui.Gui, v *gocui.View) error { + return gui.deleteBranch(g, v, false) +} + +func (gui *Gui) handleForceDeleteBranch(g *gocui.Gui, v *gocui.View) error { + return gui.deleteBranch(g, v, true) +} + +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] + if checkedOutBranch.Name == selectedBranch.Name { + return gui.createErrorPanel(g, gui.Tr.SLocalize("CantDeleteCheckOutBranch")) + } + return gui.deleteNamedBranch(g, v, selectedBranch, force) +} + +func (gui *Gui) deleteNamedBranch(g *gocui.Gui, v *gocui.View, selectedBranch *commands.Branch, force bool) error { + title := gui.Tr.SLocalize("DeleteBranch") + var messageId string + if force { + messageId = "ForceDeleteBranchMessage" + } else { + messageId = "DeleteBranchMessage" + } + message := gui.Tr.TemplateLocalize( + messageId, + Teml{ + "selectedBranchName": selectedBranch.Name, + }, + ) + return gui.createConfirmationPanel(g, v, title, message, func(g *gocui.Gui, v *gocui.View) error { + if err := gui.GitCommand.DeleteBranch(selectedBranch.Name, force); err != nil { + errMessage := err.Error() + if !force && strings.Contains(errMessage, "is not fully merged") { + return gui.deleteNamedBranch(g, v, selectedBranch, true) + } else { + return gui.createErrorPanel(g, errMessage) + } + } + return gui.refreshSidePanels(g) + }, nil) +} + +func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error { + checkedOutBranch := gui.State.Branches[0] + selectedBranch := gui.getSelectedBranch() + defer gui.refreshSidePanels(g) + if checkedOutBranch.Name == selectedBranch.Name { + return gui.createErrorPanel(g, gui.Tr.SLocalize("CantMergeBranchIntoItself")) + } + if err := gui.GitCommand.Merge(selectedBranch.Name); err != nil { + return gui.createErrorPanel(g, err.Error()) + } + return nil +} diff --git a/pkg/gui/commit_message_panel.go b/pkg/gui/commit_message_panel.go index 809442481..60058d48c 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) + v.SetOrigin(0, 0) g.SetViewOnBottom("commitMessage") gui.switchFocus(g, v, gui.getFilesView(g)) - return gui.refreshCommits(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 be291091c..2591fa240 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -9,31 +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 - } gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits)) - v.Clear() - 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 @@ -41,6 +64,22 @@ 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 := gui.getSelectedCommit(g) @@ -61,24 +100,8 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error }, nil) } -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) 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 { @@ -134,7 +157,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 { @@ -149,7 +172,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")) } @@ -160,28 +183,3 @@ func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error { return nil } - -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) handleCommitsNextLine(g *gocui.Gui, v *gocui.View) error { - gui.Log.Info(utils.AsJson(gui.State.Panels)) - 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 { - gui.Log.Info(utils.AsJson(gui.State.Panels)) - panelState := gui.State.Panels.Commits - gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.Commits), true) - - return gui.handleCommitSelect(gui.g, v) -} diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 13ce48d10..231e5d488 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() { + 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,15 +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) { - selectedLine := gui.State.Panels.Files.SelectedLine - if selectedLine == -1 { - return &commands.File{}, gui.Errors.ErrNoFiles - } - - return gui.State.Files[selectedLine], nil -} - func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error { file, err := gui.getSelectedFile(g) if err != nil { @@ -191,27 +255,6 @@ func (gui *Gui) handleIgnoreFile(g *gocui.Gui, v *gocui.View) error { return gui.refreshFiles(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 - } - 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) handleCommitPress(g *gocui.Gui, filesView *gocui.View) error { if len(gui.stagedFiles()) == 0 && !gui.State.HasMergeConflicts { return gui.createErrorPanel(g, gui.Tr.SLocalize("NoStagedFilesToCommit")) @@ -335,45 +378,6 @@ func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) { return cat, 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) -} - -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() { - gui.handleFileSelect(g, filesView) - } - return nil - }) - - return nil -} - func (gui *Gui) pullFiles(g *gocui.Gui, v *gocui.View) error { gui.createMessagePanel(g, v, "", gui.Tr.SLocalize("PullWait")) go func() { @@ -399,8 +403,7 @@ func (gui *Gui) pushWithForceFlag(currentView *gocui.View, force bool) error { _ = gui.createErrorPanel(gui.g, err.Error()) } else { _ = gui.closeConfirmationPrompt(gui.g) - _ = gui.refreshCommits(gui.g) - _ = gui.refreshStatus(gui.g) + _ = gui.refreshSidePanels(gui.g) } }() return nil diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 1af3a5e1f..6a7eaaf98 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -95,12 +95,17 @@ 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 { @@ -137,6 +142,7 @@ func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *comma Branches: &branchPanelState{SelectedLine: 0}, Commits: &commitPanelState{SelectedLine: -1}, Stash: &stashPanelState{SelectedLine: -1}, + Menu: &menuPanelState{SelectedLine: 0}, }, } @@ -359,12 +365,12 @@ func (gui *Gui) layout(g *gocui.Gui) error { } gui.g.SetCurrentView(filesView.Name()) - gui.refreshFiles(g) - gui.refreshBranches(g) - gui.refreshCommits(g) - gui.refreshStashEntries(g) - if err := gui.renderGlobalOptions(g); err != nil { - return err + + gui.refreshSidePanels(gui.g) + if gui.g.CurrentView().Name() != "menu" { + if err := gui.renderGlobalOptions(g); err != nil { + return err + } } if err := gui.switchFocus(g, nil, filesView); err != nil { return err @@ -388,6 +394,9 @@ func (gui *Gui) layout(g *gocui.Gui) error { } } + // TODO: comment-out + gui.Log.Info(utils.AsJson(gui.State)) + return gui.resizeCurrentPopupPanel(g) } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index c8c3d642f..c02074d6f 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -386,7 +386,12 @@ func (gui *Gui) GetKeybindings() []*Binding { Modifier: gocui.ModNone, Handler: gui.handleStashDrop, Description: gui.Tr.SLocalize("drop"), - }, { + }, + {ViewName: "stash", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleStashPrevLine}, + {ViewName: "stash", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleStashPrevLine}, + {ViewName: "stash", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleStashNextLine}, + {ViewName: "stash", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleStashNextLine}, + { ViewName: "commitMessage", Key: gocui.KeyEnter, Modifier: gocui.ModNone, @@ -406,7 +411,11 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: 'q', Modifier: gocui.ModNone, Handler: gui.handleMenuClose, - }, { + }, + {ViewName: "menu", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleMenuPrevLine}, + {ViewName: "menu", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleMenuPrevLine}, + {ViewName: "menu", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleMenuNextLine}, + {ViewName: "menu", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleMenuNextLine}, { ViewName: "staging", Key: gocui.KeyEsc, Modifier: gocui.ModNone, @@ -468,17 +477,6 @@ func (gui *Gui) GetKeybindings() []*Binding { }, } - // Would make these keybindings global but that interferes with editing - // input in the confirmation panel - for _, viewName := range []string{"status", "commits", "stash", "menu"} { - bindings = append(bindings, []*Binding{ - {ViewName: viewName, Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.cursorUp}, - {ViewName: viewName, Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.cursorDown}, - {ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: gui.cursorUp}, - {ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: gui.cursorDown}, - }...) - } - for _, viewName := range []string{"status", "branches", "files", "commits", "stash", "menu"} { bindings = append(bindings, []*Binding{ {ViewName: viewName, Key: gocui.KeyTab, Modifier: gocui.ModNone, Handler: gui.nextView}, diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 753e8f84d..0d3d8cf2b 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -8,12 +8,32 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) +// list panel functions + func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error { - // doing nothing for now - // but it is needed for switch in newLineFocused - return nil + return gui.focusPoint(0, gui.State.Panels.Menu.SelectedLine, v) } +func (gui *Gui) handleMenuNextLine(g *gocui.Gui, v *gocui.View) error { + panelState := gui.State.Panels.Menu + gui.changeSelectedLine(&panelState.SelectedLine, v.LinesHeight(), false) + + return gui.handleMenuSelect(g, v) +} + +func (gui *Gui) handleMenuPrevLine(g *gocui.Gui, v *gocui.View) error { + panelState := gui.State.Panels.Menu + gui.changeSelectedLine(&panelState.SelectedLine, v.LinesHeight(), true) + + if err := gui.focusPoint(0, gui.State.Panels.Commits.SelectedLine, v); err != nil { + return err + } + + return gui.handleMenuSelect(g, v) +} + +// specific functions + func (gui *Gui) renderMenuOptions(g *gocui.Gui) error { optionsMap := map[string]string{ "esc/q": gui.Tr.SLocalize("close"), @@ -46,14 +66,15 @@ func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error menuView.FgColor = gocui.ColorWhite menuView.Clear() fmt.Fprint(menuView, list) + gui.State.Panels.Menu.SelectedLine = 0 if err := gui.renderMenuOptions(gui.g); err != nil { return err } wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error { - lineNumber := gui.getItemPosition(v) - return handlePress(lineNumber) + selectedLine := gui.State.Panels.Menu.SelectedLine + return handlePress(selectedLine) } if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil { diff --git a/pkg/gui/stash_panel.go b/pkg/gui/stash_panel.go index 196a33a08..ef54ef997 100644 --- a/pkg/gui/stash_panel.go +++ b/pkg/gui/stash_panel.go @@ -8,19 +8,46 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) +// list panel functions + +func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry { + selectedLine := gui.State.Panels.Stash.SelectedLine + if selectedLine == -1 { + return nil + } + + return gui.State.StashEntries[selectedLine] +} + +func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error { + stashEntry := gui.getSelectedStashEntry(v) + if stashEntry == nil { + return gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries")) + } + if err := gui.focusPoint(0, gui.State.Panels.Stash.SelectedLine, v); err != nil { + return err + } + go func() { + // doing this asynchronously cos it can take time + diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index) + gui.renderString(g, "main", diff) + }() + return nil +} + func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { g.Update(func(g *gocui.Gui) error { - v, err := g.View("stash") - if err != nil { - panic(err) - } gui.State.StashEntries = gui.GitCommand.GetStashEntries() - v.Clear() + gui.refreshSelectedLine(&gui.State.Panels.Stash.SelectedLine, len(gui.State.StashEntries)) + list, err := utils.RenderList(gui.State.StashEntries) if err != nil { return err } + + v := gui.getStashView(gui.g) + v.Clear() fmt.Fprint(v, list) return gui.resetOrigin(v) @@ -28,28 +55,22 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { return nil } -func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry { - if len(gui.State.StashEntries) == 0 { - return nil - } - stashView, _ := gui.g.View("stash") - lineNumber := gui.getItemPosition(stashView) - return gui.State.StashEntries[lineNumber] +func (gui *Gui) handleStashNextLine(g *gocui.Gui, v *gocui.View) error { + panelState := gui.State.Panels.Stash + gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), false) + + return gui.handleStashEntrySelect(gui.g, v) } -func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error { - go func() { - stashEntry := gui.getSelectedStashEntry(v) - if stashEntry == nil { - gui.renderString(g, "main", gui.Tr.SLocalize("NoStashEntries")) - return - } - diff, _ := gui.GitCommand.GetStashEntryDiff(stashEntry.Index) - gui.renderString(g, "main", diff) - }() - return nil +func (gui *Gui) handleStashPrevLine(g *gocui.Gui, v *gocui.View) error { + panelState := gui.State.Panels.Stash + gui.changeSelectedLine(&panelState.SelectedLine, len(gui.State.StashEntries), true) + + return gui.handleStashEntrySelect(gui.g, v) } +// specific functions + func (gui *Gui) handleStashApply(g *gocui.Gui, v *gocui.View) error { return gui.stashDo(g, v, "apply") } diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 4c5002efe..5b02b1ae9 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -16,6 +16,7 @@ func (gui *Gui) refreshSidePanels(g *gocui.Gui) error { gui.refreshBranches(g) gui.refreshFiles(g) gui.refreshCommits(g) + gui.refreshStashEntries(g) return nil } @@ -78,18 +79,19 @@ func (gui *Gui) previousView(g *gocui.Gui, v *gocui.View) error { } func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { - mainView, _ := g.View("main") - mainView.SetOrigin(0, 0) - switch v.Name() { case "menu": - return gui.handleMenuSelect(g, v) + return nil case "status": return gui.handleStatusSelect(g, v) case "files": return gui.handleFileSelect(g, v) case "branches": return gui.handleBranchSelect(g, v) + case "commits": + return gui.handleCommitSelect(g, v) + case "stash": + return gui.handleStashEntrySelect(g, v) case "confirmation": return nil case "commitMessage": @@ -99,10 +101,6 @@ func (gui *Gui) newLineFocused(g *gocui.Gui, v *gocui.View) error { gui.refreshMergePanel(g) v.Highlight = false return nil - case "commits": - return gui.handleCommitSelect(g, v) - case "stash": - return gui.handleStashEntrySelect(g, v) case "staging": return nil // return gui.handleStagingSelect(g, v) @@ -165,61 +163,6 @@ func (gui *Gui) switchFocus(g *gocui.Gui, oldView, newView *gocui.View) error { return gui.newLineFocused(g, newView) } -func (gui *Gui) getItemPosition(v *gocui.View) int { - gui.correctCursor(v) - _, cy := v.Cursor() - _, oy := v.Origin() - return oy + cy -} - -func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error { - // swallowing cursor movements in main - if v == nil || v.Name() == "main" { - return nil - } - - ox, oy := v.Origin() - cx, cy := v.Cursor() - if err := v.SetCursor(cx, cy-1); err != nil && oy > 0 { - if err := v.SetOrigin(ox, oy-1); err != nil { - return err - } - } - - gui.newLineFocused(g, v) - return nil -} - -func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error { - // swallowing cursor movements in main - if v == nil || v.Name() == "main" { - return nil - } - cx, cy := v.Cursor() - ox, oy := v.Origin() - ly := v.LinesHeight() - 1 - _, height := v.Size() - maxY := height - 1 - - // if we are at the end we just return - if cy+oy == ly { - return nil - } - - var err error - if cy < maxY { - err = v.SetCursor(cx, cy+1) - } else { - err = v.SetOrigin(ox, oy+1) - } - if err != nil { - return err - } - - gui.newLineFocused(g, v) - return nil -} - func (gui *Gui) resetOrigin(v *gocui.View) error { if err := v.SetCursor(0, 0); err != nil { return err @@ -227,12 +170,6 @@ func (gui *Gui) resetOrigin(v *gocui.View) error { return v.SetOrigin(0, 0) } -// if the cursor down past the last item, move it to the last line -func (gui *Gui) correctCursor(v *gocui.View) error { - cx, cy := v.Cursor() - return gui.focusPoint(cx, cy, v) -} - // if the cursor down past the last item, move it to the last line func (gui *Gui) focusPoint(cx int, cy int, v *gocui.View) error { if cy < 0 { @@ -283,6 +220,9 @@ func (gui *Gui) renderString(g *gocui.Gui, viewName, s string) error { return nil } v.Clear() + if err := v.SetOrigin(0, 0); err != nil { + return err + } output := string(bom.Clean([]byte(s))) output = utils.NormalizeLinefeeds(output) fmt.Fprint(v, output) @@ -331,6 +271,16 @@ func (gui *Gui) getStagingView(g *gocui.Gui) *gocui.View { return v } +func (gui *Gui) getMainView(g *gocui.Gui) *gocui.View { + v, _ := g.View("main") + return v +} + +func (gui *Gui) getStashView(g *gocui.Gui) *gocui.View { + v, _ := g.View("stash") + return v +} + func (gui *Gui) trimmedContent(v *gocui.View) string { return strings.TrimSpace(v.Buffer()) }