mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-04 03:48:07 +02:00
making a start on unidirectional data binding to fix these UI bugs
This commit is contained in:
parent
ccc771d8b1
commit
99a8b1ae8b
@ -52,6 +52,8 @@ func newLogger(config config.AppConfigurer) *logrus.Entry {
|
||||
} else {
|
||||
log = newProductionLogger(config)
|
||||
}
|
||||
log.Formatter = &logrus.JSONFormatter{}
|
||||
|
||||
if config.GetUserConfig().GetString("reporting") == "on" {
|
||||
// this isn't really a secret token: it only has permission to push new rollbar items
|
||||
hook := rollrus.NewHook("23432119147a4367abf7c0de2aa99a2d", environment)
|
||||
|
@ -130,6 +130,17 @@ func (c *GitCommand) GetStashEntryDiff(index int) (string, error) {
|
||||
|
||||
// GetStatusFiles git status files
|
||||
func (c *GitCommand) GetStatusFiles() []*File {
|
||||
|
||||
// files := []*File{}
|
||||
// for i := 0; i < 100; i++ {
|
||||
// files = append(files, &File{
|
||||
// Name: strconv.Itoa(i),
|
||||
// DisplayString: strconv.Itoa(i),
|
||||
// Type: "file",
|
||||
// })
|
||||
// }
|
||||
// return files
|
||||
|
||||
statusOutput, _ := c.GitStatus()
|
||||
statusStrings := utils.SplitLines(statusOutput)
|
||||
files := []*File{}
|
||||
|
@ -131,30 +131,30 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch {
|
||||
lineNumber := gui.getItemPosition(v)
|
||||
return gui.State.Branches[lineNumber]
|
||||
}
|
||||
selectedLine := gui.State.Panels.Branches.SelectedLine
|
||||
if selectedLine == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderBranchesOptions(g *gocui.Gui) error {
|
||||
return gui.renderGlobalOptions(g)
|
||||
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 {
|
||||
if err := gui.renderBranchesOptions(g); err != nil {
|
||||
return err
|
||||
}
|
||||
// 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(v)
|
||||
if err := gui.focusPoint(0, gui.State.Panels.Branches.SelectedLine, v); err != nil {
|
||||
return err
|
||||
}
|
||||
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")
|
||||
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", diff)
|
||||
_ = gui.renderString(g, "main", graph)
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
@ -173,6 +173,8 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
||||
}
|
||||
gui.State.Branches = builder.Build()
|
||||
|
||||
gui.refreshSelectedLine(&gui.State.Panels.Branches.SelectedLine, len(gui.State.Branches))
|
||||
|
||||
v.Clear()
|
||||
list, err := utils.RenderList(gui.State.Branches)
|
||||
if err != nil {
|
||||
@ -186,3 +188,17 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error {
|
||||
})
|
||||
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)
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.refreshSelectedLine(&gui.State.Panels.Commits.SelectedLine, len(gui.State.Commits))
|
||||
|
||||
v.Clear()
|
||||
|
||||
list, err := utils.RenderList(gui.State.Commits)
|
||||
@ -41,9 +43,9 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error {
|
||||
|
||||
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())
|
||||
@ -59,21 +61,15 @@ func (gui *Gui) handleResetToCommit(g *gocui.Gui, commitView *gocui.View) error
|
||||
}, 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
|
||||
}
|
||||
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
|
||||
@ -85,12 +81,12 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error {
|
||||
if gui.getItemPosition(v) != 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 +109,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 {
|
||||
@ -165,18 +161,27 @@ 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)
|
||||
func (gui *Gui) getSelectedCommit(g *gocui.Gui) *commands.Commit {
|
||||
selectedLine := gui.State.Panels.Commits.SelectedLine
|
||||
if selectedLine == -1 {
|
||||
return nil
|
||||
}
|
||||
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
|
||||
|
||||
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)
|
||||
}
|
||||
|
@ -140,15 +140,12 @@ func (gui *Gui) handleAddPatch(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) {
|
||||
if len(gui.State.Files) == 0 {
|
||||
selectedLine := gui.State.Panels.Files.SelectedLine
|
||||
if selectedLine == -1 {
|
||||
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
|
||||
|
||||
return gui.State.Files[selectedLine], nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleFileRemove(g *gocui.Gui, v *gocui.View) error {
|
||||
@ -194,26 +191,23 @@ 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
|
||||
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)
|
||||
}
|
||||
@ -309,6 +303,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,6 +335,20 @@ 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 {
|
||||
@ -347,17 +356,21 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error {
|
||||
}
|
||||
gui.refreshStateFiles()
|
||||
|
||||
filesView.Clear()
|
||||
list, err := utils.RenderList(gui.State.Files)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(filesView, list)
|
||||
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
|
||||
})
|
||||
|
||||
gui.correctCursor(filesView)
|
||||
if filesView == g.CurrentView() {
|
||||
gui.handleFileSelect(g, filesView)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -72,11 +72,35 @@ type Gui struct {
|
||||
statusManager *statusManager
|
||||
}
|
||||
|
||||
type stagingState struct {
|
||||
StageableLines []int
|
||||
HunkStarts []int
|
||||
CurrentLineIndex int
|
||||
Diff string
|
||||
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 panelStates struct {
|
||||
Files *filePanelState
|
||||
Staging *stagingPanelState
|
||||
Branches *branchPanelState
|
||||
Commits *commitPanelState
|
||||
Stash *stashPanelState
|
||||
}
|
||||
|
||||
type guiState struct {
|
||||
@ -92,7 +116,7 @@ type guiState struct {
|
||||
EditHistory *stack.Stack
|
||||
Platform commands.Platform
|
||||
Updating bool
|
||||
StagingState *stagingState
|
||||
Panels *panelStates
|
||||
}
|
||||
|
||||
// NewGui builds a new gui handler
|
||||
@ -108,6 +132,12 @@ 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},
|
||||
},
|
||||
}
|
||||
|
||||
gui := &Gui{
|
||||
@ -193,9 +223,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")
|
||||
@ -247,12 +279,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 {
|
||||
@ -325,11 +358,14 @@ func (gui *Gui) layout(g *gocui.Gui) error {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.handleFileSelect(g, filesView)
|
||||
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
|
||||
}
|
||||
if err := gui.switchFocus(g, nil, filesView); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -341,6 +377,17 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
return gui.resizeCurrentPopupPanel(g)
|
||||
}
|
||||
|
||||
@ -411,7 +458,7 @@ func (gui *Gui) Run() error {
|
||||
}
|
||||
|
||||
gui.goEvery(g, time.Second*60, gui.fetch)
|
||||
gui.goEvery(g, time.Second*10, gui.refreshFiles)
|
||||
// gui.goEvery(g, time.Second*2, gui.refreshFiles) // TODO: comment back in
|
||||
gui.goEvery(g, time.Millisecond*50, gui.updateLoader)
|
||||
gui.goEvery(g, time.Millisecond*50, gui.renderAppStatus)
|
||||
|
||||
|
@ -219,7 +219,12 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Handler: gui.handleSwitchToStagingPanel,
|
||||
Description: gui.Tr.SLocalize("StageLines"),
|
||||
KeyReadable: "enter",
|
||||
}, {
|
||||
},
|
||||
{ViewName: "files", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleFilesPrevLine},
|
||||
{ViewName: "files", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleFilesPrevLine},
|
||||
{ViewName: "files", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleFilesNextLine},
|
||||
{ViewName: "files", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleFilesNextLine},
|
||||
{
|
||||
ViewName: "main",
|
||||
Key: gocui.KeyEsc,
|
||||
Modifier: gocui.ModNone,
|
||||
@ -322,7 +327,12 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleMerge,
|
||||
Description: gui.Tr.SLocalize("mergeIntoCurrentBranch"),
|
||||
}, {
|
||||
},
|
||||
{ViewName: "branches", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleBranchesPrevLine},
|
||||
{ViewName: "branches", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleBranchesPrevLine},
|
||||
{ViewName: "branches", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleBranchesNextLine},
|
||||
{ViewName: "branches", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleBranchesNextLine},
|
||||
{
|
||||
ViewName: "commits",
|
||||
Key: 's',
|
||||
Modifier: gocui.ModNone,
|
||||
@ -352,7 +362,12 @@ func (gui *Gui) GetKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleCommitFixup,
|
||||
Description: gui.Tr.SLocalize("fixupCommit"),
|
||||
}, {
|
||||
},
|
||||
{ViewName: "commits", Key: 'k', Modifier: gocui.ModNone, Handler: gui.handleCommitsPrevLine},
|
||||
{ViewName: "commits", Key: gocui.KeyArrowUp, Modifier: gocui.ModNone, Handler: gui.handleCommitsPrevLine},
|
||||
{ViewName: "commits", Key: 'j', Modifier: gocui.ModNone, Handler: gui.handleCommitsNextLine},
|
||||
{ViewName: "commits", Key: gocui.KeyArrowDown, Modifier: gocui.ModNone, Handler: gui.handleCommitsNextLine},
|
||||
{
|
||||
ViewName: "stash",
|
||||
Key: gocui.KeySpace,
|
||||
Modifier: gocui.ModNone,
|
||||
@ -455,17 +470,22 @@ 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", "files", "branches", "commits", "stash", "menu"} {
|
||||
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},
|
||||
{ViewName: viewName, Key: gocui.KeyArrowLeft, Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: viewName, Key: gocui.KeyArrowRight, Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{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: 'h', Modifier: gocui.ModNone, Handler: gui.previousView},
|
||||
{ViewName: viewName, Key: 'l', Modifier: gocui.ModNone, Handler: gui.nextView},
|
||||
{ViewName: viewName, Key: 'k', Modifier: gocui.ModNone, Handler: gui.cursorUp},
|
||||
{ViewName: viewName, Key: 'j', Modifier: gocui.ModNone, Handler: gui.cursorDown},
|
||||
}...)
|
||||
}
|
||||
|
||||
|
@ -40,23 +40,23 @@ func (gui *Gui) refreshStagingPanel() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var currentLineIndex int
|
||||
if gui.State.StagingState != nil {
|
||||
var selectedLine int
|
||||
if gui.State.Panels.Staging != nil {
|
||||
end := len(stageableLines) - 1
|
||||
if end < gui.State.StagingState.CurrentLineIndex {
|
||||
currentLineIndex = end
|
||||
if end < gui.State.Panels.Staging.SelectedLine {
|
||||
selectedLine = end
|
||||
} else {
|
||||
currentLineIndex = gui.State.StagingState.CurrentLineIndex
|
||||
selectedLine = gui.State.Panels.Staging.SelectedLine
|
||||
}
|
||||
} else {
|
||||
currentLineIndex = 0
|
||||
selectedLine = 0
|
||||
}
|
||||
|
||||
gui.State.StagingState = &stagingState{
|
||||
StageableLines: stageableLines,
|
||||
HunkStarts: hunkStarts,
|
||||
CurrentLineIndex: currentLineIndex,
|
||||
Diff: diff,
|
||||
gui.State.Panels.Staging = &stagingPanelState{
|
||||
StageableLines: stageableLines,
|
||||
HunkStarts: hunkStarts,
|
||||
SelectedLine: selectedLine,
|
||||
Diff: diff,
|
||||
}
|
||||
|
||||
if len(stageableLines) == 0 {
|
||||
@ -74,7 +74,7 @@ func (gui *Gui) handleStagingEscape(g *gocui.Gui, v *gocui.View) error {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.State.StagingState = nil
|
||||
gui.State.Panels.Staging = nil
|
||||
|
||||
return gui.switchFocus(gui.g, nil, gui.getFilesView(gui.g))
|
||||
}
|
||||
@ -96,9 +96,9 @@ func (gui *Gui) handleStagingNextHunk(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCycleHunk(prev bool) error {
|
||||
state := gui.State.StagingState
|
||||
state := gui.State.Panels.Staging
|
||||
lineNumbers := state.StageableLines
|
||||
currentLine := lineNumbers[state.CurrentLineIndex]
|
||||
currentLine := lineNumbers[state.SelectedLine]
|
||||
currentHunkIndex := utils.PrevIndex(state.HunkStarts, currentLine)
|
||||
var newHunkIndex int
|
||||
if prev {
|
||||
@ -115,22 +115,22 @@ func (gui *Gui) handleCycleHunk(prev bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
state.CurrentLineIndex = utils.NextIndex(lineNumbers, state.HunkStarts[newHunkIndex])
|
||||
state.SelectedLine = utils.NextIndex(lineNumbers, state.HunkStarts[newHunkIndex])
|
||||
|
||||
return gui.focusLineAndHunk()
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCycleLine(prev bool) error {
|
||||
state := gui.State.StagingState
|
||||
state := gui.State.Panels.Staging
|
||||
lineNumbers := state.StageableLines
|
||||
currentLine := lineNumbers[state.CurrentLineIndex]
|
||||
currentLine := lineNumbers[state.SelectedLine]
|
||||
var newIndex int
|
||||
if prev {
|
||||
newIndex = utils.PrevIndex(lineNumbers, currentLine)
|
||||
} else {
|
||||
newIndex = utils.NextIndex(lineNumbers, currentLine)
|
||||
}
|
||||
state.CurrentLineIndex = newIndex
|
||||
state.SelectedLine = newIndex
|
||||
|
||||
return gui.focusLineAndHunk()
|
||||
}
|
||||
@ -139,9 +139,9 @@ func (gui *Gui) handleCycleLine(prev bool) error {
|
||||
// selected line and size of the hunk
|
||||
func (gui *Gui) focusLineAndHunk() error {
|
||||
stagingView := gui.getStagingView(gui.g)
|
||||
state := gui.State.StagingState
|
||||
state := gui.State.Panels.Staging
|
||||
|
||||
lineNumber := state.StageableLines[state.CurrentLineIndex]
|
||||
lineNumber := state.StageableLines[state.SelectedLine]
|
||||
|
||||
// we want the bottom line of the view buffer to ideally be the bottom line
|
||||
// of the hunk, but if the hunk is too big we'll just go three lines beyond
|
||||
@ -170,23 +170,7 @@ func (gui *Gui) focusLineAndHunk() error {
|
||||
bottomLine = lineNumber + 3
|
||||
}
|
||||
|
||||
return gui.focusLine(lineNumber, bottomLine, stagingView)
|
||||
}
|
||||
|
||||
// focusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
|
||||
func (gui *Gui) focusLine(lineNumber int, bottomLine int, v *gocui.View) error {
|
||||
_, height := v.Size()
|
||||
overScroll := bottomLine - height + 1
|
||||
if overScroll < 0 {
|
||||
overScroll = 0
|
||||
}
|
||||
if err := v.SetOrigin(0, overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return gui.generalFocusLine(lineNumber, bottomLine, stagingView)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStageHunk(g *gocui.Gui, v *gocui.View) error {
|
||||
@ -198,13 +182,13 @@ func (gui *Gui) handleStageLine(g *gocui.Gui, v *gocui.View) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStageLineOrHunk(hunk bool) error {
|
||||
state := gui.State.StagingState
|
||||
state := gui.State.Panels.Staging
|
||||
p, err := git.NewPatchModifier(gui.Log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentLine := state.StageableLines[state.CurrentLineIndex]
|
||||
currentLine := state.StageableLines[state.SelectedLine]
|
||||
var patch string
|
||||
if hunk {
|
||||
patch, err = p.ModifyPatchForHunk(state.Diff, state.HunkStarts, currentLine)
|
||||
|
@ -37,14 +37,7 @@ func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry {
|
||||
return gui.State.StashEntries[lineNumber]
|
||||
}
|
||||
|
||||
func (gui *Gui) renderStashOptions(g *gocui.Gui) error {
|
||||
return gui.renderGlobalOptions(g)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleStashEntrySelect(g *gocui.Gui, v *gocui.View) error {
|
||||
if err := gui.renderStashOptions(g); err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
stashEntry := gui.getSelectedStashEntry(v)
|
||||
if stashEntry == nil {
|
||||
|
@ -42,10 +42,6 @@ func (gui *Gui) refreshStatus(g *gocui.Gui) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) renderStatusOptions(g *gocui.Gui) error {
|
||||
return gui.renderGlobalOptions(g)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCheckForUpdate(g *gocui.Gui, v *gocui.View) error {
|
||||
gui.Updater.CheckForNewUpdate(gui.onUserUpdateCheckFinish, true)
|
||||
return gui.createMessagePanel(gui.g, v, "", gui.Tr.SLocalize("CheckingForUpdates"))
|
||||
@ -63,10 +59,7 @@ func (gui *Gui) handleStatusSelect(g *gocui.Gui, v *gocui.View) error {
|
||||
"Buy Jesse a coffee: https://donorbox.org/lazygit",
|
||||
}, "\n\n")
|
||||
|
||||
if err := gui.renderString(g, "main", dashboardString); err != nil {
|
||||
return err
|
||||
}
|
||||
return gui.renderStatusOptions(g)
|
||||
return gui.renderString(g, "main", dashboardString)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleOpenConfig(g *gocui.Gui, v *gocui.View) error {
|
||||
|
@ -230,19 +230,46 @@ func (gui *Gui) resetOrigin(v *gocui.View) error {
|
||||
// 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()
|
||||
ox, oy := v.Origin()
|
||||
_, height := v.Size()
|
||||
maxY := height - 1
|
||||
ly := v.LinesHeight() - 1
|
||||
if oy+cy <= ly {
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
newCy := utils.Min(ly, maxY)
|
||||
if err := v.SetCursor(cx, newCy); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetOrigin(ox, ly-newCy); err != nil {
|
||||
return err
|
||||
ox, oy := v.Origin()
|
||||
_, height := v.Size()
|
||||
ly := height - 1
|
||||
|
||||
// if line is above origin, move origin and set cursor to zero
|
||||
// if line is below origin + height, move origin and set cursor to max
|
||||
// otherwise set cursor to value - origin
|
||||
if ly > v.LinesHeight() {
|
||||
if err := v.SetCursor(cx, cy); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetOrigin(ox, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if cy < oy {
|
||||
if err := v.SetCursor(cx, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetOrigin(ox, cy); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if cy > oy+ly {
|
||||
if err := v.SetCursor(cx, ly); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetOrigin(ox, cy-ly); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := v.SetCursor(cx, cy-oy); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -334,3 +361,59 @@ func (gui *Gui) resizePopupPanel(g *gocui.Gui, v *gocui.View) error {
|
||||
_, err := g.SetView(v.Name(), x0, y0, x1, y1, 0)
|
||||
return err
|
||||
}
|
||||
|
||||
// focusLine focuses and selects the given line
|
||||
func (gui *Gui) focusLine(lineNumber int, v *gocui.View) error {
|
||||
_, height := v.Size()
|
||||
overScroll := lineNumber - height + 1
|
||||
if overScroll < 0 {
|
||||
overScroll = 0
|
||||
}
|
||||
if err := v.SetOrigin(0, overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generalFocusLine takes a lineNumber to focus, and a bottomLine to ensure we can see
|
||||
func (gui *Gui) generalFocusLine(lineNumber int, bottomLine int, v *gocui.View) error {
|
||||
_, height := v.Size()
|
||||
overScroll := bottomLine - height + 1
|
||||
if overScroll < 0 {
|
||||
overScroll = 0
|
||||
}
|
||||
if err := v.SetOrigin(0, overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.SetCursor(0, lineNumber-overScroll); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) changeSelectedLine(line *int, total int, up bool) {
|
||||
if up {
|
||||
if *line == -1 || *line == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
*line -= 1
|
||||
} else {
|
||||
if *line == -1 || *line == total-1 {
|
||||
return
|
||||
}
|
||||
|
||||
*line += 1
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshSelectedLine(line *int, total int) {
|
||||
if *line == -1 && total > 0 {
|
||||
*line = 0
|
||||
} else if total-1 < *line {
|
||||
*line = total - 1
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@ -235,3 +236,8 @@ func PrevIndex(numbers []int, currentNumber int) int {
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
func AsJson(i interface{}) string {
|
||||
bytes, _ := json.MarshalIndent(i, "", " ")
|
||||
return string(bytes)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user