From ca715c5b23fdc20ad9b3dd983814ab9225c5fdbc Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Fri, 7 Sep 2018 09:41:15 +1000 Subject: [PATCH 01/25] support switching to recent repo --- pkg/app/app.go | 4 ++-- pkg/gui/gui.go | 43 ++++++++++++++++++++++++++++++++++++++++++ pkg/gui/keybindings.go | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pkg/app/app.go b/pkg/app/app.go index b03ec5b42..65acd2e35 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -77,11 +77,11 @@ func Setup(config config.AppConfigurer) (*App, error) { app.Tr = i18n.NewLocalizer(app.Log) - app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr) + app.Updater, err = updates.NewUpdater(app.Log, config, app.OSCommand, app.Tr) if err != nil { return app, err } - app.Updater, err = updates.NewUpdater(app.Log, config, app.OSCommand, app.Tr) + app.GitCommand, err = commands.NewGitCommand(app.Log, app.OSCommand, app.Tr) if err != nil { return app, err } diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 413e48202..e986ed294 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -33,6 +33,7 @@ var OverlappingEdges = false type SentinelErrors struct { ErrSubProcess error ErrNoFiles error + ErrSwitchRepo error } // GenerateSentinelErrors makes the sentinel errors for the gui. We're defining it here @@ -49,6 +50,7 @@ func (gui *Gui) GenerateSentinelErrors() { gui.Errors = SentinelErrors{ ErrSubProcess: errors.New(gui.Tr.SLocalize("RunningSubprocess")), ErrNoFiles: errors.New(gui.Tr.SLocalize("NoChangedFiles")), + ErrSwitchRepo: errors.New("switching repo"), } } @@ -292,6 +294,10 @@ func (gui *Gui) layout(g *gocui.Gui) error { // these are only called once (it's a place to put all the things you want // to happen on startup after the screen is first rendered) gui.Updater.CheckForNewUpdate(gui.onBackgroundUpdateCheckFinish, false) + if err := gui.updateRecentRepoList(); err != nil { + return err + } + gui.handleFileSelect(g, filesView) gui.refreshFiles(g) gui.refreshBranches(g) @@ -311,6 +317,41 @@ func (gui *Gui) layout(g *gocui.Gui) error { return gui.resizeCurrentPopupPanel(g) } +func newRecentReposList(recentRepos []string, currentRepo string) []string { + newRepos := []string{currentRepo} + for _, repo := range recentRepos { + if repo != currentRepo { + newRepos = append(newRepos, repo) + } + } + return newRepos +} + +// updateRecentRepoList registers the fact that we opened lazygit in this repo, +// so that we can open the same repo via a 'recent repos' menu +func (gui *Gui) updateRecentRepoList() error { + recentRepos := gui.Config.GetAppState().RecentRepos + currentRepo, err := os.Getwd() + if err != nil { + return err + } + gui.Config.GetAppState().RecentRepos = newRecentReposList(recentRepos, currentRepo) + return gui.Config.SaveAppState() +} + +func (gui *Gui) handleSwitchRepo(g *gocui.Gui, v *gocui.View) error { + newRepo := gui.Config.GetAppState().RecentRepos[1] + if err := os.Chdir(newRepo); err != nil { + return err + } + newGitCommand, err := commands.NewGitCommand(gui.Log, gui.OSCommand, gui.Tr) + if err != nil { + return err + } + gui.GitCommand = newGitCommand + return gui.Errors.ErrSwitchRepo +} + func (gui *Gui) promptAnonymousReporting() error { return gui.createConfirmationPanel(gui.g, nil, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func(g *gocui.Gui, v *gocui.View) error { return gui.Config.WriteToUserConfig("reporting", "on") @@ -391,6 +432,8 @@ func (gui *Gui) RunWithSubprocesses() { if err := gui.Run(); err != nil { if err == gocui.ErrQuit { break + } else if err == gui.Errors.ErrSwitchRepo { + continue } else if err == gui.Errors.ErrSubProcess { gui.SubProcess.Stdin = os.Stdin gui.SubProcess.Stdout = os.Stdout diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 494381749..287619b8b 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -27,6 +27,7 @@ func (gui *Gui) keybindings(g *gocui.Gui) error { {ViewName: "status", Key: 'e', Modifier: gocui.ModNone, Handler: gui.handleEditConfig}, {ViewName: "status", Key: 'o', Modifier: gocui.ModNone, Handler: gui.handleOpenConfig}, {ViewName: "status", Key: 'u', Modifier: gocui.ModNone, Handler: gui.handleCheckForUpdate}, + {ViewName: "status", Key: 's', Modifier: gocui.ModNone, Handler: gui.handleSwitchRepo}, {ViewName: "files", Key: 'c', Modifier: gocui.ModNone, Handler: gui.handleCommitPress}, {ViewName: "files", Key: 'C', Modifier: gocui.ModNone, Handler: gui.handleCommitEditorPress}, {ViewName: "files", Key: gocui.KeySpace, Modifier: gocui.ModNone, Handler: gui.handleFilePress}, From 52b132fe01156d8c7654180d03e40726e18692cd Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 10 Sep 2018 20:17:39 +1000 Subject: [PATCH 02/25] better handling of cursor and origin positionings --- pkg/gui/files_panel.go | 25 ++++++++++++++++--------- pkg/gui/menu_panel.go | 10 ++++++++-- pkg/gui/view_helpers.go | 39 ++++++++++++++++++++++++++++----------- pkg/utils/utils.go | 8 ++++++++ 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index bfcc938bb..8fd2fb30c 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -275,22 +275,23 @@ func (gui *Gui) updateHasMergeConflictStatus() error { return nil } -func (gui *Gui) renderFile(file commands.File, filesView *gocui.View) { +func (gui *Gui) renderFile(file commands.File) string { // potentially inefficient to be instantiating these color // objects with each render red := color.New(color.FgRed) green := color.New(color.FgGreen) if !file.Tracked && !file.HasStagedChanges { - red.Fprintln(filesView, file.DisplayString) - return + return red.Sprint(file.DisplayString) } - green.Fprint(filesView, file.DisplayString[0:1]) - red.Fprint(filesView, file.DisplayString[1:3]) + + output := green.Sprint(file.DisplayString[0:1]) + output += red.Sprint(file.DisplayString[1:3]) if file.HasUnstagedChanges { - red.Fprintln(filesView, file.Name) + output += red.Sprint(file.Name) } else { - green.Fprintln(filesView, file.Name) + output += green.Sprint(file.Name) } + return output } func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) { @@ -319,8 +320,14 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error { } gui.refreshStateFiles() filesView.Clear() - for _, file := range gui.State.Files { - gui.renderFile(file, filesView) + for i, file := range gui.State.Files { + str := gui.renderFile(file) + if i < len(gui.State.Files)-1 { + str += "\n" + } + if _, err := filesView.Write([]byte(str)); err != nil { + return err + } } gui.correctCursor(filesView) if filesView == g.CurrentView() { diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 68041390d..f52ceda07 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -8,6 +8,12 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) +// I need to store the handler function in state and it will take an interface and do something with it +// I need to have another function describing how to display one of the structs +// perhaps this calls for an interface where the struct is Binding and the interface has the methods Display and Execute +// but this means that for the one struct I can only have one possible display/execute function, but I want to use whatever I want. +// Would I ever need to use different handlers for different things? Maybe I should assume not given that I can cross that bridge when I come to it + func (gui *Gui) handleMenuPress(g *gocui.Gui, v *gocui.View) error { lineNumber := gui.getItemPosition(v) if gui.State.Keys[lineNumber].Key == nil { @@ -106,11 +112,11 @@ func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { content := append(contentPanel, contentGlobal...) gui.State.Keys = append(bindingsPanel, bindingsGlobal...) // append newline at the end so the last line would be selectable - contentJoined := strings.Join(content, "\n") + "\n" + contentJoined := strings.Join(content, "\n") // y1-1 so there will not be an extra space at the end of panel x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, contentJoined) - menuView, _ := g.SetView("menu", x0, y0, x1, y1-1, 0) + menuView, _ := g.SetView("menu", x0, y0, x1, y1, 0) menuView.Title = strings.Title(gui.Tr.SLocalize("menu")) menuView.FgColor = gocui.ColorWhite diff --git a/pkg/gui/view_helpers.go b/pkg/gui/view_helpers.go index 7a5bd8874..5178bd4d9 100644 --- a/pkg/gui/view_helpers.go +++ b/pkg/gui/view_helpers.go @@ -160,7 +160,6 @@ func (gui *Gui) getItemPosition(v *gocui.View) int { func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error { // swallowing cursor movements in main - // TODO: pull this out if v == nil || v.Name() == "main" { return nil } @@ -179,19 +178,28 @@ func (gui *Gui) cursorUp(g *gocui.Gui, v *gocui.View) error { func (gui *Gui) cursorDown(g *gocui.Gui, v *gocui.View) error { // swallowing cursor movements in main - // TODO: pull this out if v == nil || v.Name() == "main" { return nil } cx, cy := v.Cursor() ox, oy := v.Origin() - if cy+oy >= len(v.BufferLines())-2 { + ly := len(v.BufferLines()) - 1 + _, height := v.Size() + maxY := height - 1 + + // if we are at the end we just return + if cy+oy == ly { return nil } - if err := v.SetCursor(cx, cy+1); err != nil { - if err := v.SetOrigin(ox, oy+1); err != nil { - return err - } + + 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) @@ -208,10 +216,19 @@ 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() - _, oy := v.Origin() - lineCount := len(v.BufferLines()) - 2 - if cy >= lineCount-oy { - return v.SetCursor(cx, lineCount-oy) + ox, oy := v.Origin() + _, height := v.Size() + maxY := height - 1 + ly := len(v.BufferLines()) - 1 + if oy+cy <= ly { + 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 } return nil } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index e28ab1824..cb23eb75e 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -99,3 +99,11 @@ func ResolvePlaceholderString(str string, arguments map[string]string) string { } return str } + +// Min returns the minimum of two integers +func Min(x, y int) int { + if x < y { + return x + } + return y +} From f8b484f638e813537b9b968cf65d378b900fbcee Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 12 Sep 2018 18:23:25 +1000 Subject: [PATCH 03/25] don't use newlines at the end of panel buffers --- pkg/gui/branches_panel.go | 4 +++- pkg/gui/commits_panel.go | 31 +++++++++++++++++++------------ pkg/gui/files_panel.go | 16 ++++++++-------- pkg/gui/stash_panel.go | 12 +++++++++--- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 26c3c5618..d164003e5 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -152,9 +152,11 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error { } gui.State.Branches = builder.Build() v.Clear() + displayStrings := []string{} for _, branch := range gui.State.Branches { - fmt.Fprintln(v, branch.GetDisplayString()) + displayStrings = append(displayStrings, branch.GetDisplayString()) } + fmt.Fprint(v, strings.Join(displayStrings, "\n")) gui.resetOrigin(v) return gui.refreshStatus(g) }) diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index 1c1662475..dd33369a5 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -2,12 +2,27 @@ package gui import ( "errors" + "fmt" + "strings" "github.com/fatih/color" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" ) +func (gui *Gui) renderCommit(commit commands.Commit) string { + red := color.New(color.FgRed) + yellow := color.New(color.FgYellow) + white := color.New(color.FgWhite) + + shaColor := yellow + if commit.Pushed { + shaColor = red + } + + return shaColor.Sprint(commit.Sha) + " " + white.Sprint(commit.Name) +} + func (gui *Gui) refreshCommits(g *gocui.Gui) error { g.Update(func(*gocui.Gui) error { gui.State.Commits = gui.GitCommand.GetCommits() @@ -16,19 +31,11 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error { panic(err) } v.Clear() - red := color.New(color.FgRed) - yellow := color.New(color.FgYellow) - white := color.New(color.FgWhite) - shaColor := white - for _, commit := range gui.State.Commits { - if commit.Pushed { - shaColor = red - } else { - shaColor = yellow - } - shaColor.Fprint(v, commit.Sha+" ") - white.Fprintln(v, commit.Name) + displayStrings := make([]string, len(gui.State.Commits)) + for i, commit := range gui.State.Commits { + displayStrings[i] = gui.renderCommit(commit) } + fmt.Fprint(v, strings.Join(displayStrings, "\n")) gui.refreshStatus(g) if g.CurrentView().Name() == "commits" { gui.handleCommitSelect(g, v) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 8fd2fb30c..6ad2aed5b 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -7,6 +7,7 @@ import ( // "strings" + "fmt" "strings" "github.com/fatih/color" @@ -319,16 +320,15 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error { return err } gui.refreshStateFiles() - filesView.Clear() + + displayStrings := make([]string, len(gui.State.Files)) for i, file := range gui.State.Files { - str := gui.renderFile(file) - if i < len(gui.State.Files)-1 { - str += "\n" - } - if _, err := filesView.Write([]byte(str)); err != nil { - return err - } + displayStrings[i] = gui.renderFile(file) } + + filesView.Clear() + fmt.Fprint(filesView, strings.Join(displayStrings, "\n")) + gui.correctCursor(filesView) if filesView == g.CurrentView() { gui.handleFileSelect(g, filesView) diff --git a/pkg/gui/stash_panel.go b/pkg/gui/stash_panel.go index 9ca07717b..3a9fac8d4 100644 --- a/pkg/gui/stash_panel.go +++ b/pkg/gui/stash_panel.go @@ -2,6 +2,7 @@ package gui import ( "fmt" + "strings" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" @@ -14,10 +15,15 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { panic(err) } gui.State.StashEntries = gui.GitCommand.GetStashEntries() - v.Clear() - for _, stashEntry := range gui.State.StashEntries { - fmt.Fprintln(v, stashEntry.DisplayString) + + displayStrings := make([]string, len(gui.State.StashEntries)) + for i, stashEntry := range gui.State.StashEntries { + displayStrings[i] = stashEntry.DisplayString } + + v.Clear() + fmt.Fprint(v, strings.Join(displayStrings, "\n")) + return gui.resetOrigin(v) }) return nil From 31c33dfdcb4f8e27a8b50493876b17825c25c0ec Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 12 Sep 2018 18:47:37 +1000 Subject: [PATCH 04/25] remove redundant comments --- pkg/gui/menu_panel.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index f52ceda07..983543c94 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -8,12 +8,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) -// I need to store the handler function in state and it will take an interface and do something with it -// I need to have another function describing how to display one of the structs -// perhaps this calls for an interface where the struct is Binding and the interface has the methods Display and Execute -// but this means that for the one struct I can only have one possible display/execute function, but I want to use whatever I want. -// Would I ever need to use different handlers for different things? Maybe I should assume not given that I can cross that bridge when I come to it - func (gui *Gui) handleMenuPress(g *gocui.Gui, v *gocui.View) error { lineNumber := gui.getItemPosition(v) if gui.State.Keys[lineNumber].Key == nil { @@ -111,10 +105,8 @@ func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { content := append(contentPanel, contentGlobal...) gui.State.Keys = append(bindingsPanel, bindingsGlobal...) - // append newline at the end so the last line would be selectable contentJoined := strings.Join(content, "\n") - // y1-1 so there will not be an extra space at the end of panel x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, contentJoined) menuView, _ := g.SetView("menu", x0, y0, x1, y1, 0) menuView.Title = strings.Title(gui.Tr.SLocalize("menu")) From 35cae80de962ba17a489c3a432c9f0db5149158c Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 12 Sep 2018 18:49:09 +1000 Subject: [PATCH 05/25] more efficient building of branch displaystrings --- pkg/gui/branches_panel.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index d164003e5..659725ef2 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -152,9 +152,9 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error { } gui.State.Branches = builder.Build() v.Clear() - displayStrings := []string{} - for _, branch := range gui.State.Branches { - displayStrings = append(displayStrings, branch.GetDisplayString()) + displayStrings := make([]string, len(gui.State.Branches)) + for i, branch := range gui.State.Branches { + displayStrings[i] = branch.GetDisplayString() } fmt.Fprint(v, strings.Join(displayStrings, "\n")) gui.resetOrigin(v) From 3b765e5417501a39bca5c2f0038488dbbeb6b200 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 12 Sep 2018 19:39:36 +1000 Subject: [PATCH 06/25] add test for min method --- pkg/utils/utils_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 0b2d35959..a088ed849 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -167,3 +167,33 @@ func TestResolvePlaceholderString(t *testing.T) { assert.EqualValues(t, string(s.expected), ResolvePlaceholderString(s.templateString, s.arguments)) } } + +func TestMin(t *testing.T) { + type scenario struct { + a int + b int + expected int + } + + scenarios := []scenario{ + { + 2, + 4, + 2, + }, + { + 2, + 1, + 1, + }, + { + 1, + 1, + 1, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, Min(s.a, s.b)) + } +} From bbc88071e9a06ac8c3c376abed5d906c77931cbc Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 16 Sep 2018 20:46:25 +0200 Subject: [PATCH 07/25] gui : remove unreachable code --- pkg/gui/commits_panel.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index 1c1662475..075d66c83 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -146,7 +146,6 @@ func (gui *Gui) handleRenameCommit(g *gocui.Gui, v *gocui.View) error { } return gui.handleCommitSelect(g, v) }) - return nil } func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error { From 67a42f49b484a22f8d593dc4e03d963e400f2cea Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 16 Sep 2018 22:03:56 +0200 Subject: [PATCH 08/25] commands/git : add test to RemoveFile, refactor --- pkg/commands/git.go | 7 +- pkg/commands/git_test.go | 220 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 215 insertions(+), 12 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 0c7509b91..7143309e4 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -65,6 +65,7 @@ type GitCommand struct { Tr *i18n.Localizer getGlobalGitConfig func(string) (string, error) getLocalGitConfig func(string) (string, error) + removeFile func(string) error } // NewGitCommand it runs git commands @@ -410,15 +411,15 @@ func (c *GitCommand) IsInMergeState() (bool, error) { func (c *GitCommand) RemoveFile(file File) error { // if the file isn't tracked, we assume you want to delete it if file.HasStagedChanges { - if err := c.OSCommand.RunCommand("git reset -- " + file.Name); err != nil { + if err := c.OSCommand.RunCommand(fmt.Sprintf("git reset -- %s", file.Name)); err != nil { return err } } if !file.Tracked { - return os.RemoveAll(file.Name) + return c.removeFile(file.Name) } // if the file is tracked, we assume you want to just check it out - return c.OSCommand.RunCommand("git checkout -- " + file.Name) + return c.OSCommand.RunCommand(fmt.Sprintf("git checkout -- %s", file.Name)) } // Checkout checks out a branch, with --force if you set the force arg to true diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 88ecaea66..0ee0749a7 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -61,6 +61,7 @@ func newDummyGitCommand() *GitCommand { Tr: i18n.NewLocalizer(newDummyLog()), getGlobalGitConfig: func(string) (string, error) { return "", nil }, getLocalGitConfig: func(string) (string, error) { return "", nil }, + removeFile: func(string) error { return nil }, } } @@ -551,7 +552,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) { { "Can't retrieve pushable count", func(string, ...string) *exec.Cmd { - return exec.Command("exit", "1") + return exec.Command("test") }, func(pushableCount string, pullableCount string) { assert.EqualValues(t, "?", pushableCount) @@ -562,7 +563,7 @@ func TestGitCommandUpstreamDifferentCount(t *testing.T) { "Can't retrieve pullable count", func(cmd string, args ...string) *exec.Cmd { if args[1] == "head..@{u}" { - return exec.Command("exit", "1") + return exec.Command("test") } return exec.Command("echo") @@ -608,7 +609,7 @@ func TestGitCommandGetCommitsToPush(t *testing.T) { { "Can't retrieve pushable commits", func(string, ...string) *exec.Cmd { - return exec.Command("exit", "1") + return exec.Command("test") }, func(pushables []string) { assert.EqualValues(t, []string{}, pushables) @@ -872,7 +873,7 @@ func TestGitCommandCommit(t *testing.T) { assert.EqualValues(t, "git", cmd) assert.EqualValues(t, []string{"commit", "-m", "test"}, args) - return exec.Command("exit", "1") + return exec.Command("test") }, func(string) (string, error) { return "false", nil @@ -935,7 +936,7 @@ func TestGitCommandPush(t *testing.T) { assert.EqualValues(t, "git", cmd) assert.EqualValues(t, []string{"push", "-u", "origin", "test"}, args) - return exec.Command("exit", "1") + return exec.Command("test") }, false, func(err error) { @@ -967,7 +968,7 @@ func TestGitCommandSquashPreviousTwoCommits(t *testing.T) { assert.EqualValues(t, "git", cmd) assert.EqualValues(t, []string{"reset", "--soft", "HEAD^"}, args) - return exec.Command("exit", "1") + return exec.Command("test") }, func(err error) { assert.NotNil(t, err) @@ -983,7 +984,7 @@ func TestGitCommandSquashPreviousTwoCommits(t *testing.T) { assert.EqualValues(t, "git", cmd) assert.EqualValues(t, []string{"commit", "--amend", "-m", "test"}, args) - return exec.Command("exit", "1") + return exec.Command("test") }, func(err error) { assert.NotNil(t, err) @@ -1031,7 +1032,7 @@ func TestGitCommandSquashFixupCommit(t *testing.T) { return func(cmd string, args ...string) *exec.Cmd { cmdsCalled = append(cmdsCalled, args) if len(args) > 0 && args[0] == "checkout" { - return exec.Command("exit", "1") + return exec.Command("test") } return exec.Command("echo") @@ -1165,7 +1166,7 @@ func TestGitCommandIsInMergeState(t *testing.T) { assert.EqualValues(t, "git", cmd) assert.EqualValues(t, []string{"status", "--untracked-files=all"}, args) - return exec.Command("exit", "1") + return exec.Command("test") }, func(isInMergeState bool, err error) { assert.Error(t, err) @@ -1219,6 +1220,207 @@ func TestGitCommandIsInMergeState(t *testing.T) { } } +func TestGitCommandRemoveFile(t *testing.T) { + type scenario struct { + testName string + command func() (func(string, ...string) *exec.Cmd, *[][]string) + test func(*[][]string, error) + file File + removeFile func(string) error + } + + scenarios := []scenario{ + { + "An error occurred when resetting", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("test") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.Error(t, err) + assert.Len(t, *cmdsCalled, 1) + assert.EqualValues(t, *cmdsCalled, [][]string{ + {"reset", "--", "test"}, + }) + }, + File{ + Name: "test", + HasStagedChanges: true, + }, + func(string) error { + return nil + }, + }, + { + "An error occurred when removing file", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("test") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.Error(t, err) + assert.EqualError(t, err, "an error occurred when removing file") + assert.Len(t, *cmdsCalled, 0) + }, + File{ + Name: "test", + Tracked: false, + }, + func(string) error { + return fmt.Errorf("an error occurred when removing file") + }, + }, + { + "An error occurred with checkout", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("test") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.Error(t, err) + assert.Len(t, *cmdsCalled, 1) + assert.EqualValues(t, *cmdsCalled, [][]string{ + {"checkout", "--", "test"}, + }) + }, + File{ + Name: "test", + Tracked: true, + HasStagedChanges: false, + }, + func(string) error { + return nil + }, + }, + { + "Checkout only", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("echo") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.NoError(t, err) + assert.Len(t, *cmdsCalled, 1) + assert.EqualValues(t, *cmdsCalled, [][]string{ + {"checkout", "--", "test"}, + }) + }, + File{ + Name: "test", + Tracked: true, + HasStagedChanges: false, + }, + func(string) error { + return nil + }, + }, + { + "Reset and checkout", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("echo") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.NoError(t, err) + assert.Len(t, *cmdsCalled, 2) + assert.EqualValues(t, *cmdsCalled, [][]string{ + {"reset", "--", "test"}, + {"checkout", "--", "test"}, + }) + }, + File{ + Name: "test", + Tracked: true, + HasStagedChanges: true, + }, + func(string) error { + return nil + }, + }, + { + "Reset and remove", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("echo") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.NoError(t, err) + assert.Len(t, *cmdsCalled, 1) + assert.EqualValues(t, *cmdsCalled, [][]string{ + {"reset", "--", "test"}, + }) + }, + File{ + Name: "test", + Tracked: false, + HasStagedChanges: true, + }, + func(filename string) error { + assert.Equal(t, "test", filename) + return nil + }, + }, + { + "Remove only", + func() (func(string, ...string) *exec.Cmd, *[][]string) { + cmdsCalled := [][]string{} + return func(cmd string, args ...string) *exec.Cmd { + cmdsCalled = append(cmdsCalled, args) + + return exec.Command("echo") + }, &cmdsCalled + }, + func(cmdsCalled *[][]string, err error) { + assert.NoError(t, err) + assert.Len(t, *cmdsCalled, 0) + }, + File{ + Name: "test", + Tracked: false, + HasStagedChanges: false, + }, + func(filename string) error { + assert.Equal(t, "test", filename) + return nil + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + var cmdsCalled *[][]string + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command, cmdsCalled = s.command() + gitCmd.removeFile = s.removeFile + s.test(cmdsCalled, gitCmd.RemoveFile(s.file)) + }) + } +} + func TestGitCommandDiff(t *testing.T) { gitCommand := newDummyGitCommand() assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) From b641d6bd965fb24b18ebf8475aa4f72c39988dd7 Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 16 Sep 2018 22:08:23 +0200 Subject: [PATCH 09/25] commands/git : add test to Checkout, refactor --- pkg/commands/git.go | 2 +- pkg/commands/git_test.go | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 7143309e4..402bee7ac 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -428,7 +428,7 @@ func (c *GitCommand) Checkout(branch string, force bool) error { if force { forceArg = "--force " } - return c.OSCommand.RunCommand("git checkout " + forceArg + branch) + return c.OSCommand.RunCommand(fmt.Sprintf("git checkout %s %s", forceArg, branch)) } // AddPatch prepares a subprocess for adding a patch by patch diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 0ee0749a7..87b0e4cee 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1421,6 +1421,52 @@ func TestGitCommandRemoveFile(t *testing.T) { } } +func TestGitCommandCheckout(t *testing.T) { + type scenario struct { + testName string + command func(string, ...string) *exec.Cmd + test func(error) + force bool + } + + scenarios := []scenario{ + { + "Checkout", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"checkout", "test"}, args) + + return exec.Command("echo") + }, + func(err error) { + assert.NoError(t, err) + }, + false, + }, + { + "Checkout forced", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"checkout", "--force", "test"}, args) + + return exec.Command("echo") + }, + func(err error) { + assert.NoError(t, err) + }, + true, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = s.command + s.test(gitCmd.Checkout("test", s.force)) + }) + } +} + func TestGitCommandDiff(t *testing.T) { gitCommand := newDummyGitCommand() assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) From 9713a151672d0787c24b922ab70e0177ac4607de Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 16 Sep 2018 22:12:03 +0200 Subject: [PATCH 10/25] commands/git : add test to GetBranchGraph, refactor --- pkg/commands/git.go | 2 +- pkg/commands/git_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 402bee7ac..1c5b31b81 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -451,7 +451,7 @@ func (c *GitCommand) PrepareCommitAmendSubProcess() *exec.Cmd { // Currently it limits the result to 100 commits, but when we get async stuff // working we can do lazy loading func (c *GitCommand) GetBranchGraph(branchName string) (string, error) { - return c.OSCommand.RunCommandWithOutput("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 " + branchName) + return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 %s", branchName)) } func includesString(list []string, a string) bool { diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 87b0e4cee..7a7af0991 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1467,6 +1467,19 @@ func TestGitCommandCheckout(t *testing.T) { } } +func TestGitCommandGetBranchGraph(t *testing.T) { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + assert.EqualValues(t, []string{"log", "--graph", "--color", "--abbrev-commit", "--decorate", "--date=relative", "--pretty=medium", "-100", "test"}, args) + + return exec.Command("echo") + } + + _, err := gitCmd.GetBranchGraph("test") + assert.NoError(t, err) +} + func TestGitCommandDiff(t *testing.T) { gitCommand := newDummyGitCommand() assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) From 38036e0d20f3886cbbeba5ff8965f86a0db01a0e Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 16 Sep 2018 22:41:41 +0200 Subject: [PATCH 11/25] circle : kill old cache --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ddfd88844..9e32166db 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -29,7 +29,7 @@ jobs: fi - restore_cache: keys: - - pkg-cache-{{ checksum "Gopkg.lock" }}-v2 + - pkg-cache-{{ checksum "Gopkg.lock" }}-v3 - run: name: Run tests command: | @@ -44,7 +44,7 @@ jobs: command: | bash <(curl -s https://codecov.io/bash) - save_cache: - key: pkg-cache-{{ checksum "Gopkg.lock" }}-v2 + key: pkg-cache-{{ checksum "Gopkg.lock" }}-v3 paths: - ~/.cache/go-build From 9d9d775f50657a6ae528cbd6781e6317fabb1f4a Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Sun, 16 Sep 2018 22:49:17 +0200 Subject: [PATCH 12/25] circle : remove new line --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9e32166db..14c848257 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,6 @@ jobs: build: docker: - image: circleci/golang:1.11 - working_directory: /go/src/github.com/jesseduffield/lazygit steps: - checkout From c00c834b359bc0ebcd6e940e5cb5ef6f7247a6c7 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 17 Sep 2018 21:02:30 +1000 Subject: [PATCH 13/25] standardise rendering of lists in panels --- pkg/commands/branch.go | 6 ++-- pkg/commands/commit.go | 26 ++++++++++++++ pkg/commands/file.go | 36 +++++++++++++++++++ pkg/commands/git.go | 28 +++++++-------- pkg/commands/git_structs.go | 27 -------------- pkg/commands/stash_entry.go | 13 +++++++ pkg/git/branch_list_builder.go | 32 ++++++++--------- pkg/gui/branches_panel.go | 14 +++++--- pkg/gui/commits_panel.go | 32 ++++++----------- pkg/gui/files_panel.go | 46 +++++++----------------- pkg/gui/gui.go | 16 ++++----- pkg/gui/keybindings.go | 28 ++++++++++++--- pkg/gui/menu_panel.go | 51 ++++++-------------------- pkg/gui/stash_panel.go | 15 ++++---- pkg/utils/utils.go | 66 ++++++++++++++++++++++++++++++++++ 15 files changed, 256 insertions(+), 180 deletions(-) create mode 100644 pkg/commands/commit.go create mode 100644 pkg/commands/file.go create mode 100644 pkg/commands/stash_entry.go diff --git a/pkg/commands/branch.go b/pkg/commands/branch.go index 13c26e766..19553a26b 100644 --- a/pkg/commands/branch.go +++ b/pkg/commands/branch.go @@ -14,9 +14,9 @@ type Branch struct { Recency string } -// GetDisplayString returns the dispaly string of branch -func (b *Branch) GetDisplayString() string { - return utils.WithPadding(b.Recency, 4) + utils.ColoredString(b.Name, b.GetColor()) +// GetDisplayStrings returns the dispaly string of branch +func (b *Branch) GetDisplayStrings() []string { + return []string{b.Recency, utils.ColoredString(b.Name, b.GetColor())} } // GetColor branch color diff --git a/pkg/commands/commit.go b/pkg/commands/commit.go new file mode 100644 index 000000000..37c3e9525 --- /dev/null +++ b/pkg/commands/commit.go @@ -0,0 +1,26 @@ +package commands + +import ( + "github.com/fatih/color" +) + +// Commit : A git commit +type Commit struct { + Sha string + Name string + Pushed bool + DisplayString string +} + +func (c *Commit) GetDisplayStrings() []string { + red := color.New(color.FgRed) + yellow := color.New(color.FgYellow) + white := color.New(color.FgWhite) + + shaColor := yellow + if c.Pushed { + shaColor = red + } + + return []string{shaColor.Sprint(c.Sha), white.Sprint(c.Name)} +} diff --git a/pkg/commands/file.go b/pkg/commands/file.go new file mode 100644 index 000000000..8fcd9aff9 --- /dev/null +++ b/pkg/commands/file.go @@ -0,0 +1,36 @@ +package commands + +import "github.com/fatih/color" + +// File : A file from git status +// duplicating this for now +type File struct { + Name string + HasStagedChanges bool + HasUnstagedChanges bool + Tracked bool + Deleted bool + HasMergeConflicts bool + DisplayString string + Type string // one of 'file', 'directory', and 'other' +} + +// GetDisplayStrings returns the display string of a file +func (f *File) GetDisplayStrings() []string { + // potentially inefficient to be instantiating these color + // objects with each render + red := color.New(color.FgRed) + green := color.New(color.FgGreen) + if !f.Tracked && !f.HasStagedChanges { + return []string{red.Sprint(f.DisplayString)} + } + + output := green.Sprint(f.DisplayString[0:1]) + output += red.Sprint(f.DisplayString[1:3]) + if f.HasUnstagedChanges { + output += red.Sprint(f.Name) + } else { + output += green.Sprint(f.Name) + } + return []string{output} +} diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 5744fa6aa..3fb46fff7 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -104,17 +104,17 @@ func NewGitCommand(log *logrus.Entry, osCommand *OSCommand, tr *i18n.Localizer) } // GetStashEntries stash entryies -func (c *GitCommand) GetStashEntries() []StashEntry { +func (c *GitCommand) GetStashEntries() []*StashEntry { rawString, _ := c.OSCommand.RunCommandWithOutput("git stash list --pretty='%gs'") - stashEntries := []StashEntry{} + stashEntries := []*StashEntry{} for i, line := range utils.SplitLines(rawString) { stashEntries = append(stashEntries, stashEntryFromLine(line, i)) } return stashEntries } -func stashEntryFromLine(line string, index int) StashEntry { - return StashEntry{ +func stashEntryFromLine(line string, index int) *StashEntry { + return &StashEntry{ Name: line, Index: index, DisplayString: line, @@ -127,10 +127,10 @@ func (c *GitCommand) GetStashEntryDiff(index int) (string, error) { } // GetStatusFiles git status files -func (c *GitCommand) GetStatusFiles() []File { +func (c *GitCommand) GetStatusFiles() []*File { statusOutput, _ := c.GitStatus() statusStrings := utils.SplitLines(statusOutput) - files := []File{} + files := []*File{} for _, statusString := range statusStrings { change := statusString[0:2] @@ -140,7 +140,7 @@ func (c *GitCommand) GetStatusFiles() []File { _, untracked := map[string]bool{"??": true, "A ": true, "AM": true}[change] _, hasNoStagedChanges := map[string]bool{" ": true, "U": true, "?": true}[stagedChange] - file := File{ + file := &File{ Name: filename, DisplayString: statusString, HasStagedChanges: !hasNoStagedChanges, @@ -168,7 +168,7 @@ func (c *GitCommand) StashSave(message string) error { } // MergeStatusFiles merge status files -func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File { +func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []*File) []*File { if len(oldFiles) == 0 { return newFiles } @@ -176,7 +176,7 @@ func (c *GitCommand) MergeStatusFiles(oldFiles, newFiles []File) []File { appendedIndexes := []int{} // retain position of files we already could see - result := []File{} + result := []*File{} for _, oldFile := range oldFiles { for newIndex, newFile := range newFiles { if oldFile.Name == newFile.Name { @@ -415,7 +415,7 @@ func (c *GitCommand) IsInMergeState() (bool, error) { } // RemoveFile directly -func (c *GitCommand) RemoveFile(file File) error { +func (c *GitCommand) RemoveFile(file *File) error { // if the file isn't tracked, we assume you want to delete it if file.HasStagedChanges { if err := c.OSCommand.RunCommand("git reset -- " + file.Name); err != nil { @@ -471,17 +471,17 @@ func includesString(list []string, a string) bool { } // GetCommits obtains the commits of the current branch -func (c *GitCommand) GetCommits() []Commit { +func (c *GitCommand) GetCommits() []*Commit { pushables := c.GetCommitsToPush() log := c.GetLog() - commits := []Commit{} + commits := []*Commit{} // now we can split it up and turn it into commits lines := utils.SplitLines(log) for _, line := range lines { splitLine := strings.Split(line, " ") sha := splitLine[0] pushed := includesString(pushables, sha) - commits = append(commits, Commit{ + commits = append(commits, &Commit{ Sha: sha, Name: strings.Join(splitLine[1:], " "), Pushed: pushed, @@ -519,7 +519,7 @@ func (c *GitCommand) Show(sha string) string { } // Diff returns the diff of a file -func (c *GitCommand) Diff(file File) string { +func (c *GitCommand) Diff(file *File) string { cachedArg := "" fileName := c.OSCommand.Quote(file.Name) if file.HasStagedChanges && !file.HasUnstagedChanges { diff --git a/pkg/commands/git_structs.go b/pkg/commands/git_structs.go index 2b3b3f2db..1590ce32e 100644 --- a/pkg/commands/git_structs.go +++ b/pkg/commands/git_structs.go @@ -1,32 +1,5 @@ package commands -// File : A staged/unstaged file -type File struct { - Name string - HasStagedChanges bool - HasUnstagedChanges bool - Tracked bool - Deleted bool - HasMergeConflicts bool - DisplayString string - Type string // one of 'file', 'directory', and 'other' -} - -// Commit : A git commit -type Commit struct { - Sha string - Name string - Pushed bool - DisplayString string -} - -// StashEntry : A git stash entry -type StashEntry struct { - Index int - Name string - DisplayString string -} - // Conflict : A git conflict with a start middle and end corresponding to line // numbers in the file where the conflict bars appear type Conflict struct { diff --git a/pkg/commands/stash_entry.go b/pkg/commands/stash_entry.go new file mode 100644 index 000000000..3886fd4c9 --- /dev/null +++ b/pkg/commands/stash_entry.go @@ -0,0 +1,13 @@ +package commands + +// StashEntry : A git stash entry +type StashEntry struct { + Index int + Name string + DisplayString string +} + +// GetDisplayStrings returns the dispaly string of branch +func (s *StashEntry) GetDisplayStrings() []string { + return []string{s.DisplayString} +} diff --git a/pkg/git/branch_list_builder.go b/pkg/git/branch_list_builder.go index 651b684f5..151e5b0b4 100644 --- a/pkg/git/branch_list_builder.go +++ b/pkg/git/branch_list_builder.go @@ -34,7 +34,7 @@ func NewBranchListBuilder(log *logrus.Entry, gitCommand *commands.GitCommand) (* }, nil } -func (b *BranchListBuilder) obtainCurrentBranch() commands.Branch { +func (b *BranchListBuilder) obtainCurrentBranch() *commands.Branch { // I used go-git for this, but that breaks if you've just done a git init, // even though you're on 'master' branchName, err := b.GitCommand.OSCommand.RunCommandWithOutput("git symbolic-ref --short HEAD") @@ -44,11 +44,11 @@ func (b *BranchListBuilder) obtainCurrentBranch() commands.Branch { panic(err.Error()) } } - return commands.Branch{Name: strings.TrimSpace(branchName), Recency: " *"} + return &commands.Branch{Name: strings.TrimSpace(branchName), Recency: " *"} } -func (b *BranchListBuilder) obtainReflogBranches() []commands.Branch { - branches := make([]commands.Branch, 0) +func (b *BranchListBuilder) obtainReflogBranches() []*commands.Branch { + branches := make([]*commands.Branch, 0) rawString, err := b.GitCommand.OSCommand.RunCommandWithOutput("git reflog -n100 --pretty='%cr|%gs' --grep-reflog='checkout: moving' HEAD") if err != nil { return branches @@ -58,14 +58,14 @@ func (b *BranchListBuilder) obtainReflogBranches() []commands.Branch { for _, line := range branchLines { timeNumber, timeUnit, branchName := branchInfoFromLine(line) timeUnit = abbreviatedTimeUnit(timeUnit) - branch := commands.Branch{Name: branchName, Recency: timeNumber + timeUnit} + branch := &commands.Branch{Name: branchName, Recency: timeNumber + timeUnit} branches = append(branches, branch) } return branches } -func (b *BranchListBuilder) obtainSafeBranches() []commands.Branch { - branches := make([]commands.Branch, 0) +func (b *BranchListBuilder) obtainSafeBranches() []*commands.Branch { + branches := make([]*commands.Branch, 0) bIter, err := b.GitCommand.Repo.Branches() if err != nil { @@ -73,14 +73,14 @@ func (b *BranchListBuilder) obtainSafeBranches() []commands.Branch { } err = bIter.ForEach(func(b *plumbing.Reference) error { name := b.Name().Short() - branches = append(branches, commands.Branch{Name: name}) + branches = append(branches, &commands.Branch{Name: name}) return nil }) return branches } -func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []commands.Branch, included bool) []commands.Branch { +func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existingBranches []*commands.Branch, included bool) []*commands.Branch { for _, newBranch := range newBranches { if included == branchIncluded(newBranch.Name, existingBranches) { finalBranches = append(finalBranches, newBranch) @@ -89,7 +89,7 @@ func (b *BranchListBuilder) appendNewBranches(finalBranches, newBranches, existi return finalBranches } -func sanitisedReflogName(reflogBranch commands.Branch, safeBranches []commands.Branch) string { +func sanitisedReflogName(reflogBranch *commands.Branch, safeBranches []*commands.Branch) string { for _, safeBranch := range safeBranches { if strings.ToLower(safeBranch.Name) == strings.ToLower(reflogBranch.Name) { return safeBranch.Name @@ -99,15 +99,15 @@ func sanitisedReflogName(reflogBranch commands.Branch, safeBranches []commands.B } // Build the list of branches for the current repo -func (b *BranchListBuilder) Build() []commands.Branch { - branches := make([]commands.Branch, 0) +func (b *BranchListBuilder) Build() []*commands.Branch { + branches := make([]*commands.Branch, 0) head := b.obtainCurrentBranch() safeBranches := b.obtainSafeBranches() if len(safeBranches) == 0 { return append(branches, head) } reflogBranches := b.obtainReflogBranches() - reflogBranches = uniqueByName(append([]commands.Branch{head}, reflogBranches...)) + reflogBranches = uniqueByName(append([]*commands.Branch{head}, reflogBranches...)) for i, reflogBranch := range reflogBranches { reflogBranches[i].Name = sanitisedReflogName(reflogBranch, safeBranches) } @@ -118,7 +118,7 @@ func (b *BranchListBuilder) Build() []commands.Branch { return branches } -func branchIncluded(branchName string, branches []commands.Branch) bool { +func branchIncluded(branchName string, branches []*commands.Branch) bool { for _, existingBranch := range branches { if strings.ToLower(existingBranch.Name) == strings.ToLower(branchName) { return true @@ -127,8 +127,8 @@ func branchIncluded(branchName string, branches []commands.Branch) bool { return false } -func uniqueByName(branches []commands.Branch) []commands.Branch { - finalBranches := make([]commands.Branch, 0) +func uniqueByName(branches []*commands.Branch) []*commands.Branch { + finalBranches := make([]*commands.Branch, 0) for _, branch := range branches { if branchIncluded(branch.Name, finalBranches) { continue diff --git a/pkg/gui/branches_panel.go b/pkg/gui/branches_panel.go index 659725ef2..0f66533b1 100644 --- a/pkg/gui/branches_panel.go +++ b/pkg/gui/branches_panel.go @@ -7,6 +7,7 @@ import ( "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/git" + "github.com/jesseduffield/lazygit/pkg/utils" ) func (gui *Gui) handleBranchPress(g *gocui.Gui, v *gocui.View) error { @@ -109,7 +110,7 @@ func (gui *Gui) handleMerge(g *gocui.Gui, v *gocui.View) error { return nil } -func (gui *Gui) getSelectedBranch(v *gocui.View) commands.Branch { +func (gui *Gui) getSelectedBranch(v *gocui.View) *commands.Branch { lineNumber := gui.getItemPosition(v) return gui.State.Branches[lineNumber] } @@ -151,12 +152,15 @@ func (gui *Gui) refreshBranches(g *gocui.Gui) error { return err } gui.State.Branches = builder.Build() + v.Clear() - displayStrings := make([]string, len(gui.State.Branches)) - for i, branch := range gui.State.Branches { - displayStrings[i] = branch.GetDisplayString() + list, err := utils.RenderList(gui.State.Branches) + if err != nil { + return err } - fmt.Fprint(v, strings.Join(displayStrings, "\n")) + + fmt.Fprint(v, list) + gui.resetOrigin(v) return gui.refreshStatus(g) }) diff --git a/pkg/gui/commits_panel.go b/pkg/gui/commits_panel.go index dd33369a5..f0b96586a 100644 --- a/pkg/gui/commits_panel.go +++ b/pkg/gui/commits_panel.go @@ -3,26 +3,12 @@ package gui import ( "errors" "fmt" - "strings" - "github.com/fatih/color" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" + "github.com/jesseduffield/lazygit/pkg/utils" ) -func (gui *Gui) renderCommit(commit commands.Commit) string { - red := color.New(color.FgRed) - yellow := color.New(color.FgYellow) - white := color.New(color.FgWhite) - - shaColor := yellow - if commit.Pushed { - shaColor = red - } - - return shaColor.Sprint(commit.Sha) + " " + white.Sprint(commit.Name) -} - func (gui *Gui) refreshCommits(g *gocui.Gui) error { g.Update(func(*gocui.Gui) error { gui.State.Commits = gui.GitCommand.GetCommits() @@ -30,12 +16,14 @@ func (gui *Gui) refreshCommits(g *gocui.Gui) error { if err != nil { panic(err) } + v.Clear() - displayStrings := make([]string, len(gui.State.Commits)) - for i, commit := range gui.State.Commits { - displayStrings[i] = gui.renderCommit(commit) + list, err := utils.RenderList(gui.State.Commits) + if err != nil { + return err } - fmt.Fprint(v, strings.Join(displayStrings, "\n")) + fmt.Fprint(v, list) + gui.refreshStatus(g) if g.CurrentView().Name() == "commits" { gui.handleCommitSelect(g, v) @@ -106,7 +94,7 @@ func (gui *Gui) handleCommitSquashDown(g *gocui.Gui, v *gocui.View) error { } // TODO: move to files panel -func (gui *Gui) anyUnStagedChanges(files []commands.File) bool { +func (gui *Gui) anyUnStagedChanges(files []*commands.File) bool { for _, file := range files { if file.Tracked && file.HasUnstagedChanges { return true @@ -169,13 +157,13 @@ func (gui *Gui) handleRenameCommitEditor(g *gocui.Gui, v *gocui.View) error { return nil } -func (gui *Gui) getSelectedCommit(g *gocui.Gui) (commands.Commit, error) { +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")) + return &commands.Commit{}, errors.New(gui.Tr.SLocalize("NoCommitsThisBranch")) } lineNumber := gui.getItemPosition(v) if lineNumber > len(gui.State.Commits)-1 { diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 6ad2aed5b..6fd05552f 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -10,14 +10,14 @@ import ( "fmt" "strings" - "github.com/fatih/color" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" + "github.com/jesseduffield/lazygit/pkg/utils" ) -func (gui *Gui) stagedFiles() []commands.File { +func (gui *Gui) stagedFiles() []*commands.File { files := gui.State.Files - result := make([]commands.File, 0) + result := make([]*commands.File, 0) for _, file := range files { if file.HasStagedChanges { result = append(result, file) @@ -26,9 +26,9 @@ func (gui *Gui) stagedFiles() []commands.File { return result } -func (gui *Gui) trackedFiles() []commands.File { +func (gui *Gui) trackedFiles() []*commands.File { files := gui.State.Files - result := make([]commands.File, 0) + result := make([]*commands.File, 0) for _, file := range files { if file.Tracked { result = append(result, file) @@ -117,9 +117,9 @@ 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) { +func (gui *Gui) getSelectedFile(g *gocui.Gui) (*commands.File, error) { if len(gui.State.Files) == 0 { - return commands.File{}, gui.Errors.ErrNoFiles + return &commands.File{}, gui.Errors.ErrNoFiles } filesView, err := g.View("files") if err != nil { @@ -185,7 +185,7 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error { gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles")) return gui.renderfilesOptions(g, nil) } - gui.renderfilesOptions(g, &file) + gui.renderfilesOptions(g, file) var content string if file.HasMergeConflicts { return gui.refreshMergePanel(g) @@ -276,25 +276,6 @@ func (gui *Gui) updateHasMergeConflictStatus() error { return nil } -func (gui *Gui) renderFile(file commands.File) string { - // potentially inefficient to be instantiating these color - // objects with each render - red := color.New(color.FgRed) - green := color.New(color.FgGreen) - if !file.Tracked && !file.HasStagedChanges { - return red.Sprint(file.DisplayString) - } - - output := green.Sprint(file.DisplayString[0:1]) - output += red.Sprint(file.DisplayString[1:3]) - if file.HasUnstagedChanges { - output += red.Sprint(file.Name) - } else { - output += green.Sprint(file.Name) - } - return output -} - func (gui *Gui) catSelectedFile(g *gocui.Gui) (string, error) { item, err := gui.getSelectedFile(g) if err != nil { @@ -321,13 +302,12 @@ func (gui *Gui) refreshFiles(g *gocui.Gui) error { } gui.refreshStateFiles() - displayStrings := make([]string, len(gui.State.Files)) - for i, file := range gui.State.Files { - displayStrings[i] = gui.renderFile(file) - } - filesView.Clear() - fmt.Fprint(filesView, strings.Join(displayStrings, "\n")) + list, err := utils.RenderList(gui.State.Files) + if err != nil { + return err + } + fmt.Fprint(filesView, list) gui.correctCursor(filesView) if filesView == g.CurrentView() { diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index e3cc61529..f527e8c5d 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -71,10 +71,10 @@ type Gui struct { } type guiState struct { - Files []commands.File - Branches []commands.Branch - Commits []commands.Commit - StashEntries []commands.StashEntry + Files []*commands.File + Branches []*commands.Branch + Commits []*commands.Commit + StashEntries []*commands.StashEntry PreviousView string HasMergeConflicts bool ConflictIndex int @@ -83,17 +83,17 @@ type guiState struct { EditHistory *stack.Stack Platform commands.Platform Updating bool - Keys []Binding + Keys []*Binding } // NewGui builds a new gui handler func NewGui(log *logrus.Entry, gitCommand *commands.GitCommand, oSCommand *commands.OSCommand, tr *i18n.Localizer, config config.AppConfigurer, updater *updates.Updater) (*Gui, error) { initialState := guiState{ - Files: make([]commands.File, 0), + Files: make([]*commands.File, 0), PreviousView: "files", - Commits: make([]commands.Commit, 0), - StashEntries: make([]commands.StashEntry, 0), + Commits: make([]*commands.Commit, 0), + StashEntries: make([]*commands.StashEntry, 0), ConflictIndex: 0, ConflictTop: true, Conflicts: make([]commands.Conflict, 0), diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index c8ed7d3c8..e05caaec7 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -1,6 +1,8 @@ package gui -import "github.com/jesseduffield/gocui" +import ( + "github.com/jesseduffield/gocui" +) // Binding - a keybinding mapping a key and modifier to a handler. The keypress // is only handled if the given view has focus, or handled globally if the view @@ -14,8 +16,26 @@ type Binding struct { Description string } -func (gui *Gui) GetKeybindings() []Binding { - bindings := []Binding{ +// GetDisplayStrings returns the display string of a file +func (b *Binding) GetDisplayStrings() []string { + return []string{b.GetKey(), b.Description} +} + +func (b *Binding) GetKey() string { + r, ok := b.Key.(rune) + key := "" + + if ok { + key = string(r) + } else if b.KeyReadable != "" { + key = b.KeyReadable + } + + return key +} + +func (gui *Gui) GetKeybindings() []*Binding { + bindings := []*Binding{ { ViewName: "", Key: 'q', @@ -360,7 +380,7 @@ 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"} { - bindings = append(bindings, []Binding{ + 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}, diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 983543c94..ee0773a51 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -49,50 +49,20 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { return gui.returnFocus(g, v) } -func (gui *Gui) GetKey(binding Binding) string { - r, ok := binding.Key.(rune) - key := "" - - if ok { - key = string(r) - } else if binding.KeyReadable != "" { - key = binding.KeyReadable - } - - return key -} - -func (gui *Gui) GetMaxKeyLength(bindings []Binding) int { - max := 0 - for _, binding := range bindings { - keyLength := len(gui.GetKey(binding)) - if keyLength > max { - max = keyLength - } - } - return max -} - func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { var ( - contentGlobal, contentPanel []string - bindingsGlobal, bindingsPanel []Binding + bindingsGlobal, bindingsPanel []*Binding ) // clear keys slice, so we don't have ghost elements gui.State.Keys = gui.State.Keys[:0] bindings := gui.GetKeybindings() - padWidth := gui.GetMaxKeyLength(bindings) for _, binding := range bindings { - key := gui.GetKey(binding) - if key != "" && binding.Description != "" { - content := fmt.Sprintf("%s %s", utils.WithPadding(key, padWidth), binding.Description) + if binding.GetKey() != "" && binding.Description != "" { switch binding.ViewName { case "": - contentGlobal = append(contentGlobal, content) bindingsGlobal = append(bindingsGlobal, binding) case v.Name(): - contentPanel = append(contentPanel, content) bindingsPanel = append(bindingsPanel, binding) } } @@ -100,24 +70,25 @@ func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { // append dummy element to have a separator between // panel and global keybindings - contentPanel = append(contentPanel, "") - bindingsPanel = append(bindingsPanel, Binding{}) - - content := append(contentPanel, contentGlobal...) + bindingsPanel = append(bindingsPanel, &Binding{}) gui.State.Keys = append(bindingsPanel, bindingsGlobal...) - contentJoined := strings.Join(content, "\n") - x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, contentJoined) + list, err := utils.RenderList(gui.State.Keys) + if err != nil { + return err + } + + x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, list) menuView, _ := g.SetView("menu", x0, y0, x1, y1, 0) menuView.Title = strings.Title(gui.Tr.SLocalize("menu")) menuView.FgColor = gocui.ColorWhite + menuView.Clear() + fmt.Fprint(menuView, list) if err := gui.renderMenuOptions(g); err != nil { return err } - fmt.Fprint(menuView, contentJoined) - g.Update(func(g *gocui.Gui) error { _, err := g.SetViewOnTop("menu") if err != nil { diff --git a/pkg/gui/stash_panel.go b/pkg/gui/stash_panel.go index 3a9fac8d4..62b4efda7 100644 --- a/pkg/gui/stash_panel.go +++ b/pkg/gui/stash_panel.go @@ -2,10 +2,10 @@ package gui import ( "fmt" - "strings" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/commands" + "github.com/jesseduffield/lazygit/pkg/utils" ) func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { @@ -16,13 +16,12 @@ func (gui *Gui) refreshStashEntries(g *gocui.Gui) error { } gui.State.StashEntries = gui.GitCommand.GetStashEntries() - displayStrings := make([]string, len(gui.State.StashEntries)) - for i, stashEntry := range gui.State.StashEntries { - displayStrings[i] = stashEntry.DisplayString - } - v.Clear() - fmt.Fprint(v, strings.Join(displayStrings, "\n")) + list, err := utils.RenderList(gui.State.StashEntries) + if err != nil { + return err + } + fmt.Fprint(v, list) return gui.resetOrigin(v) }) @@ -35,7 +34,7 @@ func (gui *Gui) getSelectedStashEntry(v *gocui.View) *commands.StashEntry { } stashView, _ := gui.g.View("stash") lineNumber := gui.getItemPosition(stashView) - return &gui.State.StashEntries[lineNumber] + return gui.State.StashEntries[lineNumber] } func (gui *Gui) renderStashOptions(g *gocui.Gui) error { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index cb23eb75e..f1a56116b 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,10 +1,12 @@ package utils import ( + "errors" "fmt" "log" "os" "path/filepath" + "reflect" "strings" "time" @@ -107,3 +109,67 @@ func Min(x, y int) int { } return y } + +type Displayable interface { + GetDisplayStrings() []string +} + +// RenderList takes a slice of items, confirms they implement the Displayable +// interface, then generates a list of their displaystrings to write to a panel's +// buffer +func RenderList(slice interface{}) (string, error) { + s := reflect.ValueOf(slice) + if s.Kind() != reflect.Slice { + return "", errors.New("RenderList given a non-slice type") + } + + displayables := make([]Displayable, s.Len()) + + for i := 0; i < s.Len(); i++ { + value, ok := s.Index(i).Interface().(Displayable) + if !ok { + return "", errors.New("item does not implement the Displayable interface") + } + displayables[i] = value + } + + return renderDisplayableList(displayables) +} + +// renderDisplayableList takes a list of displayable items, obtains their display +// strings via GetDisplayStrings() and then returns a single string containing +// each item's string representation on its own line, with appropriate horizontal +// padding between the item's own strings +func renderDisplayableList(items []Displayable) (string, error) { + displayStrings := make([][]string, len(items)) + + for i, item := range items { + displayStrings[i] = item.GetDisplayStrings() + } + + if len(displayStrings) == 0 { + return "", nil + } + + // use first element to determine how many times to do this + padWidths := make([]int, len(displayStrings[0])) + for i, _ := range padWidths { + for _, strings := range displayStrings { + if len(strings) != len(padWidths) { + return "", errors.New("Each item must return the same number of strings to display") + } + if len(strings[i]) > padWidths[i] { + padWidths[i] = len(strings[i]) + } + } + } + + paddedDisplayStrings := make([]string, len(displayStrings)) + for i, strings := range displayStrings { + for j, padWidth := range padWidths { + paddedDisplayStrings[i] += WithPadding(strings[j], padWidth) + " " + } + } + + return strings.Join(paddedDisplayStrings, "\n"), nil +} From a66ac8092e30f6c3f9a72c10a6b3eed5fb2265eb Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 17 Sep 2018 21:11:47 +1000 Subject: [PATCH 14/25] minor refactor --- pkg/gui/menu_panel.go | 11 +++++--- pkg/utils/utils.go | 60 ++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index ee0773a51..945d0020a 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -49,12 +49,11 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { return gui.returnFocus(g, v) } -func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { +func (gui *Gui) getBindings(v *gocui.View) []*Binding { var ( bindingsGlobal, bindingsPanel []*Binding ) - // clear keys slice, so we don't have ghost elements - gui.State.Keys = gui.State.Keys[:0] + bindings := gui.GetKeybindings() for _, binding := range bindings { @@ -71,7 +70,11 @@ func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { // append dummy element to have a separator between // panel and global keybindings bindingsPanel = append(bindingsPanel, &Binding{}) - gui.State.Keys = append(bindingsPanel, bindingsGlobal...) + return append(bindingsPanel, bindingsGlobal...) +} + +func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { + gui.State.Keys = gui.getBindings(v) list, err := utils.RenderList(gui.State.Keys) if err != nil { diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index f1a56116b..3efa67a91 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -141,35 +141,59 @@ func RenderList(slice interface{}) (string, error) { // each item's string representation on its own line, with appropriate horizontal // padding between the item's own strings func renderDisplayableList(items []Displayable) (string, error) { - displayStrings := make([][]string, len(items)) - - for i, item := range items { - displayStrings[i] = item.GetDisplayStrings() - } - - if len(displayStrings) == 0 { + if len(items) == 0 { return "", nil } - // use first element to determine how many times to do this - padWidths := make([]int, len(displayStrings[0])) + stringArrays := getDisplayStringArrays(items) + + if !displayArraysAligned(stringArrays) { + return "", errors.New("Each item must return the same number of strings to display") + } + + padWidths := getPadWidths(stringArrays) + paddedDisplayStrings := getPaddedDisplayStrings(stringArrays, padWidths) + + return strings.Join(paddedDisplayStrings, "\n"), nil +} + +func getPadWidths(stringArrays [][]string) []int { + padWidths := make([]int, len(stringArrays[0])) for i, _ := range padWidths { - for _, strings := range displayStrings { - if len(strings) != len(padWidths) { - return "", errors.New("Each item must return the same number of strings to display") - } + for _, strings := range stringArrays { if len(strings[i]) > padWidths[i] { padWidths[i] = len(strings[i]) } } } + return padWidths +} - paddedDisplayStrings := make([]string, len(displayStrings)) - for i, strings := range displayStrings { +func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string { + paddedDisplayStrings := make([]string, len(stringArrays)) + for i, stringArray := range stringArrays { for j, padWidth := range padWidths { - paddedDisplayStrings[i] += WithPadding(strings[j], padWidth) + " " + paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " " } } - - return strings.Join(paddedDisplayStrings, "\n"), nil + return paddedDisplayStrings +} + +// displayArraysAligned returns true if every string array returned from our +// list of displayables has the same length +func displayArraysAligned(stringArrays [][]string) bool { + for _, strings := range stringArrays { + if len(strings) != len(stringArrays[0]) { + return false + } + } + return true +} + +func getDisplayStringArrays(displayables []Displayable) [][]string { + stringArrays := make([][]string, len(displayables)) + for i, item := range displayables { + stringArrays[i] = item.GetDisplayStrings() + } + return stringArrays } From f89bc10af1f9fb4211badd2fee51e903bbc03f6c Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 17 Sep 2018 21:32:19 +1000 Subject: [PATCH 15/25] appease golangci --- pkg/gui/files_panel.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 6fd05552f..574e6bc1c 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -185,7 +185,9 @@ func (gui *Gui) handleFileSelect(g *gocui.Gui, v *gocui.View) error { gui.renderString(g, "main", gui.Tr.SLocalize("NoChangedFiles")) return gui.renderfilesOptions(g, nil) } - gui.renderfilesOptions(g, file) + if err := gui.renderfilesOptions(g, file); err != nil { + return err + } var content string if file.HasMergeConflicts { return gui.refreshMergePanel(g) From 6f0b32f95e3b14209553de7d289a71d49fa4623f Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Mon, 17 Sep 2018 21:19:17 +0200 Subject: [PATCH 16/25] commands/git : add GetCommits tests refactor * switch GetCommitsToPush scope to private * return a map instead of slice for look up * remove useless includesString function --- pkg/commands/git.go | 43 +++++++++----------- pkg/commands/git_test.go | 85 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 30 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 1c5b31b81..cca406d9a 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -231,14 +231,19 @@ func (c *GitCommand) UpstreamDifferenceCount() (string, string) { return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount) } -// GetCommitsToPush Returns the sha's of the commits that have not yet been pushed -// to the remote branch of the current branch -func (c *GitCommand) GetCommitsToPush() []string { - pushables, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit") +// getCommitsToPush Returns the sha's of the commits that have not yet been pushed +// to the remote branch of the current branch, a map is returned to ease look up +func (c *GitCommand) getCommitsToPush() map[string]bool { + pushables := map[string]bool{} + o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit") if err != nil { - return []string{} + return pushables } - return utils.SplitLines(pushables) + for _, p := range utils.SplitLines(o) { + pushables[p] = true + } + + return pushables } // RenameCommit renames the topmost commit with the given name @@ -454,26 +459,16 @@ func (c *GitCommand) GetBranchGraph(branchName string) (string, error) { return c.OSCommand.RunCommandWithOutput(fmt.Sprintf("git log --graph --color --abbrev-commit --decorate --date=relative --pretty=medium -100 %s", branchName)) } -func includesString(list []string, a string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - // GetCommits obtains the commits of the current branch func (c *GitCommand) GetCommits() []Commit { - pushables := c.GetCommitsToPush() + pushables := c.getCommitsToPush() log := c.GetLog() commits := []Commit{} // now we can split it up and turn it into commits - lines := utils.SplitLines(log) - for _, line := range lines { + for _, line := range utils.SplitLines(log) { splitLine := strings.Split(line, " ") sha := splitLine[0] - pushed := includesString(pushables, sha) + _, pushed := pushables[sha] commits = append(commits, Commit{ Sha: sha, Name: strings.Join(splitLine[1:], " "), @@ -489,12 +484,12 @@ func (c *GitCommand) GetCommits() []Commit { func (c *GitCommand) GetLog() string { // currently limiting to 30 for performance reasons // TODO: add lazyloading when you scroll down - result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30") - if err != nil { - // assume if there is an error there are no commits yet for this branch - return "" + if result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30"); err == nil { + return result } - return result + + // assume if there is an error there are no commits yet for this branch + return "" } // Ignore adds a file to the gitignore for the repo diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 7a7af0991..aedf5253b 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -602,7 +602,7 @@ func TestGitCommandGetCommitsToPush(t *testing.T) { type scenario struct { testName string command func(string, ...string) *exec.Cmd - test func([]string) + test func(map[string]bool) } scenarios := []scenario{ @@ -611,8 +611,8 @@ func TestGitCommandGetCommitsToPush(t *testing.T) { func(string, ...string) *exec.Cmd { return exec.Command("test") }, - func(pushables []string) { - assert.EqualValues(t, []string{}, pushables) + func(pushables map[string]bool) { + assert.EqualValues(t, map[string]bool{}, pushables) }, }, { @@ -620,9 +620,9 @@ func TestGitCommandGetCommitsToPush(t *testing.T) { func(cmd string, args ...string) *exec.Cmd { return exec.Command("echo", "8a2bb0e\n78976bc") }, - func(pushables []string) { + func(pushables map[string]bool) { assert.Len(t, pushables, 2) - assert.EqualValues(t, []string{"8a2bb0e", "78976bc"}, pushables) + assert.EqualValues(t, map[string]bool{"8a2bb0e": true, "78976bc": true}, pushables) }, }, } @@ -631,7 +631,7 @@ func TestGitCommandGetCommitsToPush(t *testing.T) { t.Run(s.testName, func(t *testing.T) { gitCmd := newDummyGitCommand() gitCmd.OSCommand.command = s.command - s.test(gitCmd.GetCommitsToPush()) + s.test(gitCmd.getCommitsToPush()) }) } } @@ -1480,6 +1480,79 @@ func TestGitCommandGetBranchGraph(t *testing.T) { assert.NoError(t, err) } +func TestGitCommandGetCommits(t *testing.T) { + type scenario struct { + testName string + command func(string, ...string) *exec.Cmd + test func([]Commit) + } + + scenarios := []scenario{ + { + "No data found", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + + switch args[0] { + case "rev-list": + assert.EqualValues(t, []string{"rev-list", "@{u}..head", "--abbrev-commit"}, args) + return exec.Command("echo") + case "log": + assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args) + return exec.Command("echo") + } + + return nil + }, + func(commits []Commit) { + assert.Len(t, commits, 0) + }, + }, + { + "GetCommits returns 2 commits, 1 pushed the other not", + func(cmd string, args ...string) *exec.Cmd { + assert.EqualValues(t, "git", cmd) + + switch args[0] { + case "rev-list": + assert.EqualValues(t, []string{"rev-list", "@{u}..head", "--abbrev-commit"}, args) + return exec.Command("echo", "8a2bb0e") + case "log": + assert.EqualValues(t, []string{"log", "--oneline", "-30"}, args) + return exec.Command("echo", "8a2bb0e commit 1\n78976bc commit 2") + } + + return nil + }, + func(commits []Commit) { + assert.Len(t, commits, 2) + assert.EqualValues(t, []Commit{ + { + Sha: "8a2bb0e", + Name: "commit 1", + Pushed: true, + DisplayString: "8a2bb0e commit 1", + }, + { + Sha: "78976bc", + Name: "commit 2", + Pushed: false, + DisplayString: "78976bc commit 2", + }, + }, commits) + }, + }, + } + + for _, s := range scenarios { + t.Run(s.testName, func(t *testing.T) { + gitCmd := newDummyGitCommand() + gitCmd.OSCommand.command = s.command + s.test(gitCmd.GetCommits()) + }) + } +} + func TestGitCommandDiff(t *testing.T) { gitCommand := newDummyGitCommand() assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) From 60cf549a3285531047009d9cf8cbb58c86a8e488 Mon Sep 17 00:00:00 2001 From: Anthony HAMON Date: Tue, 18 Sep 2018 09:23:41 +0200 Subject: [PATCH 17/25] commands/git : reverse the logic --- pkg/commands/git.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index cca406d9a..a106d8dce 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -484,12 +484,13 @@ func (c *GitCommand) GetCommits() []Commit { func (c *GitCommand) GetLog() string { // currently limiting to 30 for performance reasons // TODO: add lazyloading when you scroll down - if result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30"); err == nil { - return result + result, err := c.OSCommand.RunCommandWithOutput("git log --oneline -30") + if err != nil { + // assume if there is an error there are no commits yet for this branch + return "" } - // assume if there is an error there are no commits yet for this branch - return "" + return result } // Ignore adds a file to the gitignore for the repo From b384fcf6af4e5248616712414e0164887d7bbcbd Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Tue, 18 Sep 2018 21:07:25 +1000 Subject: [PATCH 18/25] generalise popup menu panel --- pkg/gui/gui.go | 1 - pkg/gui/keybindings.go | 7 +--- pkg/gui/menu_panel.go | 74 ++++++++++------------------------- pkg/gui/options_menu_panel.go | 51 ++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 60 deletions(-) create mode 100644 pkg/gui/options_menu_panel.go diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index f527e8c5d..8a2aaea81 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -83,7 +83,6 @@ type guiState struct { EditHistory *stack.Stack Platform commands.Platform Updating bool - Keys []*Binding } // NewGui builds a new gui handler diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index e05caaec7..72a187472 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -93,7 +93,7 @@ func (gui *Gui) GetKeybindings() []*Binding { ViewName: "", Key: 'x', Modifier: gocui.ModNone, - Handler: gui.handleMenu, + Handler: gui.handleCreateOptionsMenu, }, { ViewName: "status", Key: 'e', @@ -369,11 +369,6 @@ func (gui *Gui) GetKeybindings() []*Binding { Key: 'q', Modifier: gocui.ModNone, Handler: gui.handleMenuClose, - }, { - ViewName: "menu", - Key: gocui.KeySpace, - Modifier: gocui.ModNone, - Handler: gui.handleMenuPress, }, } diff --git a/pkg/gui/menu_panel.go b/pkg/gui/menu_panel.go index 945d0020a..753e8f84d 100644 --- a/pkg/gui/menu_panel.go +++ b/pkg/gui/menu_panel.go @@ -8,21 +8,6 @@ import ( "github.com/jesseduffield/lazygit/pkg/utils" ) -func (gui *Gui) handleMenuPress(g *gocui.Gui, v *gocui.View) error { - lineNumber := gui.getItemPosition(v) - if gui.State.Keys[lineNumber].Key == nil { - return nil - } - if len(gui.State.Keys) > lineNumber { - err := gui.handleMenuClose(g, v) - if err != nil { - return err - } - return gui.State.Keys[lineNumber].Handler(g, v) - } - return nil -} - func (gui *Gui) handleMenuSelect(g *gocui.Gui, v *gocui.View) error { // doing nothing for now // but it is needed for switch in newLineFocused @@ -39,9 +24,9 @@ func (gui *Gui) renderMenuOptions(g *gocui.Gui) error { } func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { - // better to delete because for example after closing update confirmation panel, - // the focus isn't set back to any of panels and one is unable to even quit - //_, err := g.SetViewOnBottom(v.Name()) + if err := g.DeleteKeybinding("menu", gocui.KeySpace, gocui.ModNone); err != nil { + return err + } err := g.DeleteView("menu") if err != nil { return err @@ -49,55 +34,38 @@ func (gui *Gui) handleMenuClose(g *gocui.Gui, v *gocui.View) error { return gui.returnFocus(g, v) } -func (gui *Gui) getBindings(v *gocui.View) []*Binding { - var ( - bindingsGlobal, bindingsPanel []*Binding - ) - - bindings := gui.GetKeybindings() - - for _, binding := range bindings { - if binding.GetKey() != "" && binding.Description != "" { - switch binding.ViewName { - case "": - bindingsGlobal = append(bindingsGlobal, binding) - case v.Name(): - bindingsPanel = append(bindingsPanel, binding) - } - } - } - - // append dummy element to have a separator between - // panel and global keybindings - bindingsPanel = append(bindingsPanel, &Binding{}) - return append(bindingsPanel, bindingsGlobal...) -} - -func (gui *Gui) handleMenu(g *gocui.Gui, v *gocui.View) error { - gui.State.Keys = gui.getBindings(v) - - list, err := utils.RenderList(gui.State.Keys) +func (gui *Gui) createMenu(items interface{}, handlePress func(int) error) error { + list, err := utils.RenderList(items) if err != nil { return err } - x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(g, list) - menuView, _ := g.SetView("menu", x0, y0, x1, y1, 0) + x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(gui.g, list) + menuView, _ := gui.g.SetView("menu", x0, y0, x1, y1, 0) menuView.Title = strings.Title(gui.Tr.SLocalize("menu")) menuView.FgColor = gocui.ColorWhite menuView.Clear() fmt.Fprint(menuView, list) - if err := gui.renderMenuOptions(g); err != nil { + if err := gui.renderMenuOptions(gui.g); err != nil { return err } - g.Update(func(g *gocui.Gui) error { - _, err := g.SetViewOnTop("menu") - if err != nil { + wrappedHandlePress := func(g *gocui.Gui, v *gocui.View) error { + lineNumber := gui.getItemPosition(v) + return handlePress(lineNumber) + } + + if err := gui.g.SetKeybinding("menu", gocui.KeySpace, gocui.ModNone, wrappedHandlePress); err != nil { + return err + } + + gui.g.Update(func(g *gocui.Gui) error { + if _, err := g.SetViewOnTop("menu"); err != nil { return err } - return gui.switchFocus(g, v, menuView) + currentView := gui.g.CurrentView() + return gui.switchFocus(gui.g, currentView, menuView) }) return nil } diff --git a/pkg/gui/options_menu_panel.go b/pkg/gui/options_menu_panel.go new file mode 100644 index 000000000..2da002b43 --- /dev/null +++ b/pkg/gui/options_menu_panel.go @@ -0,0 +1,51 @@ +package gui + +import ( + "errors" + + "github.com/jesseduffield/gocui" +) + +func (gui *Gui) getBindings(v *gocui.View) []*Binding { + var ( + bindingsGlobal, bindingsPanel []*Binding + ) + + bindings := gui.GetKeybindings() + + for _, binding := range bindings { + if binding.GetKey() != "" && binding.Description != "" { + switch binding.ViewName { + case "": + bindingsGlobal = append(bindingsGlobal, binding) + case v.Name(): + bindingsPanel = append(bindingsPanel, binding) + } + } + } + + // append dummy element to have a separator between + // panel and global keybindings + bindingsPanel = append(bindingsPanel, &Binding{}) + return append(bindingsPanel, bindingsGlobal...) +} + +func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error { + bindings := gui.getBindings(v) + + handleOptionsMenuPress := func(index int) error { + if bindings[index].Key == nil { + return nil + } + if index <= len(bindings) { + return errors.New("Index is greater than size of bindings") + } + err := gui.handleMenuClose(g, v) + if err != nil { + return err + } + return bindings[index].Handler(g, v) + } + + return gui.createMenu(bindings, handleOptionsMenuPress) +} From 950cfeff6f97d94b4ab5166214ea6cb5b38b77f0 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 18:03:44 +1000 Subject: [PATCH 19/25] add specs for menu utils --- pkg/utils/utils.go | 11 ++- pkg/utils/utils_test.go | 181 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 174 insertions(+), 18 deletions(-) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 3efa67a91..aef653c50 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -158,8 +158,11 @@ func renderDisplayableList(items []Displayable) (string, error) { } func getPadWidths(stringArrays [][]string) []int { - padWidths := make([]int, len(stringArrays[0])) - for i, _ := range padWidths { + if len(stringArrays[0]) <= 1 { + return []int{} + } + padWidths := make([]int, len(stringArrays[0])-1) + for i := range padWidths { for _, strings := range stringArrays { if len(strings[i]) > padWidths[i] { padWidths[i] = len(strings[i]) @@ -172,9 +175,13 @@ func getPadWidths(stringArrays [][]string) []int { func getPaddedDisplayStrings(stringArrays [][]string, padWidths []int) []string { paddedDisplayStrings := make([]string, len(stringArrays)) for i, stringArray := range stringArrays { + if len(stringArray) == 0 { + continue + } for j, padWidth := range padWidths { paddedDisplayStrings[i] += WithPadding(stringArray[j], padWidth) + " " } + paddedDisplayStrings[i] += stringArray[len(padWidths)] } return paddedDisplayStrings } diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index a088ed849..045bf9c64 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -1,6 +1,7 @@ package utils import ( + "errors" "testing" "github.com/stretchr/testify/assert" @@ -168,32 +169,180 @@ func TestResolvePlaceholderString(t *testing.T) { } } -func TestMin(t *testing.T) { +func TestDisplayArraysAligned(t *testing.T) { type scenario struct { - a int - b int - expected int + input [][]string + expected bool } scenarios := []scenario{ { - 2, - 4, - 2, + [][]string{{"", ""}, {"", ""}}, + true, }, { - 2, - 1, - 1, - }, - { - 1, - 1, - 1, + [][]string{{""}, {"", ""}}, + false, }, } for _, s := range scenarios { - assert.EqualValues(t, s.expected, Min(s.a, s.b)) + assert.EqualValues(t, s.expected, displayArraysAligned(s.input)) + } +} + +type myDisplayable struct { + strings []string +} + +type myStruct struct{} + +func (d *myDisplayable) GetDisplayStrings() []string { + return d.strings +} + +func TestGetDisplayStringArrays(t *testing.T) { + type scenario struct { + input []Displayable + expected [][]string + } + + scenarios := []scenario{ + { + []Displayable{ + Displayable(&myDisplayable{[]string{"a", "b"}}), + Displayable(&myDisplayable{[]string{"a", "b"}}), + }, + [][]string{{"a", "b"}, {"c", "d"}}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, getDisplayStringArrays(s.input)) + } +} + +func TestRenderDisplayableList(t *testing.T) { + type scenario struct { + input []Displayable + expectedString string + expectedError error + } + + scenarios := []scenario{ + { + []Displayable{ + Displayable(&myDisplayable{[]string{}}), + Displayable(&myDisplayable{[]string{}}), + }, + "\n", + nil, + }, + { + []Displayable{ + Displayable(&myDisplayable{[]string{"aa", "b"}}), + Displayable(&myDisplayable{[]string{"c", "d"}}), + }, + "aa b\nc d", + nil, + }, + { + []Displayable{ + Displayable(&myDisplayable{[]string{"a"}}), + Displayable(&myDisplayable{[]string{"b", "c"}}), + }, + "", + errors.New("Each item must return the same number of strings to display"), + }, + } + + for _, s := range scenarios { + str, err := renderDisplayableList(s.input) + assert.EqualValues(t, s.expectedString, str) + assert.EqualValues(t, s.expectedError, err) + } +} + +func TestRenderList(t *testing.T) { + type scenario struct { + input interface{} + expectedString string + expectedError error + } + + scenarios := []scenario{ + { + []*myDisplayable{ + {[]string{"aa", "b"}}, + {[]string{"c", "d"}}, + }, + "aa b\nc d", + nil, + }, + { + []*myStruct{ + {}, + {}, + }, + "", + errors.New("item does not implement the Displayable interface"), + }, + { + &myStruct{}, + "", + errors.New("RenderList given a non-slice type"), + }, + } + + for _, s := range scenarios { + str, err := RenderList(s.input) + assert.EqualValues(t, s.expectedString, str) + assert.EqualValues(t, s.expectedError, err) + } +} + +func TestGetPaddedDisplayStrings(t *testing.T) { + type scenario struct { + stringArrays [][]string + padWidths []int + expected []string + } + + scenarios := []scenario{ + { + [][]string{{"a", "b"}, {"c", "d"}}, + []int{1}, + []string{"a b", "c d"}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, getPaddedDisplayStrings(s.stringArrays, s.padWidths)) + } +} + +func TestGetPadWidths(t *testing.T) { + type scenario struct { + stringArrays [][]string + expected []int + } + + scenarios := []scenario{ + { + [][]string{{""}, {""}}, + []int{}, + }, + { + [][]string{{"a"}, {""}}, + []int{}, + }, + { + [][]string{{"aa", "b", "ccc"}, {"c", "d", "e"}}, + []int{2, 1}, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays)) } } From e95b2e5f0b2aef3afcc6fa4e8b1f24d74cd1fb2a Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 18:31:54 +1000 Subject: [PATCH 20/25] update specs --- pkg/commands/git_test.go | 40 ++++++++++++++++++++-------------------- pkg/utils/utils_test.go | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 88ecaea66..d1c27cd30 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -275,7 +275,7 @@ func TestGitCommandGetStashEntries(t *testing.T) { type scenario struct { testName string command func(string, ...string) *exec.Cmd - test func([]StashEntry) + test func([]*StashEntry) } scenarios := []scenario{ @@ -284,7 +284,7 @@ func TestGitCommandGetStashEntries(t *testing.T) { func(string, ...string) *exec.Cmd { return exec.Command("echo") }, - func(entries []StashEntry) { + func(entries []*StashEntry) { assert.Len(t, entries, 0) }, }, @@ -293,8 +293,8 @@ func TestGitCommandGetStashEntries(t *testing.T) { func(string, ...string) *exec.Cmd { return exec.Command("echo", "WIP on add-pkg-commands-test: 55c6af2 increase parallel build\nWIP on master: bb86a3f update github template") }, - func(entries []StashEntry) { - expected := []StashEntry{ + func(entries []*StashEntry) { + expected := []*StashEntry{ { 0, "WIP on add-pkg-commands-test: 55c6af2 increase parallel build", @@ -341,7 +341,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) { type scenario struct { testName string command func(string, ...string) *exec.Cmd - test func([]File) + test func([]*File) } scenarios := []scenario{ @@ -350,7 +350,7 @@ func TestGitCommandGetStatusFiles(t *testing.T) { func(cmd string, args ...string) *exec.Cmd { return exec.Command("echo") }, - func(files []File) { + func(files []*File) { assert.Len(t, files, 0) }, }, @@ -362,10 +362,10 @@ func TestGitCommandGetStatusFiles(t *testing.T) { "MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt", ) }, - func(files []File) { + func(files []*File) { assert.Len(t, files, 4) - expected := []File{ + expected := []*File{ { Name: "file1.txt", HasStagedChanges: true, @@ -463,22 +463,22 @@ func TestGitCommandCommitAmend(t *testing.T) { func TestGitCommandMergeStatusFiles(t *testing.T) { type scenario struct { testName string - oldFiles []File - newFiles []File - test func([]File) + oldFiles []*File + newFiles []*File + test func([]*File) } scenarios := []scenario{ { "Old file and new file are the same", - []File{}, - []File{ + []*File{}, + []*File{ { Name: "new_file.txt", }, }, - func(files []File) { - expected := []File{ + func(files []*File) { + expected := []*File{ { Name: "new_file.txt", }, @@ -490,7 +490,7 @@ func TestGitCommandMergeStatusFiles(t *testing.T) { }, { "Several files to merge, with some identical", - []File{ + []*File{ { Name: "new_file1.txt", }, @@ -501,7 +501,7 @@ func TestGitCommandMergeStatusFiles(t *testing.T) { Name: "new_file3.txt", }, }, - []File{ + []*File{ { Name: "new_file4.txt", }, @@ -512,8 +512,8 @@ func TestGitCommandMergeStatusFiles(t *testing.T) { Name: "new_file1.txt", }, }, - func(files []File) { - expected := []File{ + func(files []*File) { + expected := []*File{ { Name: "new_file1.txt", }, @@ -1223,7 +1223,7 @@ func TestGitCommandDiff(t *testing.T) { gitCommand := newDummyGitCommand() assert.NoError(t, test.GenerateRepo("lots_of_diffs.sh")) - files := []File{ + files := []*File{ { Name: "deleted_staged", HasStagedChanges: false, diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 045bf9c64..76a9f8b95 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -211,7 +211,7 @@ func TestGetDisplayStringArrays(t *testing.T) { { []Displayable{ Displayable(&myDisplayable{[]string{"a", "b"}}), - Displayable(&myDisplayable{[]string{"a", "b"}}), + Displayable(&myDisplayable{[]string{"c", "d"}}), }, [][]string{{"a", "b"}, {"c", "d"}}, }, From e91fb21233f1c725c34e03b43ac763d8b4c61889 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 19:15:29 +1000 Subject: [PATCH 21/25] add recent repos menu option --- pkg/gui/gui.go | 35 ------------------ pkg/gui/keybindings.go | 2 +- pkg/gui/options_menu_panel.go | 6 +-- pkg/gui/recent_repos_panel.go | 69 +++++++++++++++++++++++++++++++++++ pkg/i18n/english.go | 2 +- 5 files changed, 74 insertions(+), 40 deletions(-) create mode 100644 pkg/gui/recent_repos_panel.go diff --git a/pkg/gui/gui.go b/pkg/gui/gui.go index 818655aa6..7e7648cfe 100644 --- a/pkg/gui/gui.go +++ b/pkg/gui/gui.go @@ -317,41 +317,6 @@ func (gui *Gui) layout(g *gocui.Gui) error { return gui.resizeCurrentPopupPanel(g) } -func newRecentReposList(recentRepos []string, currentRepo string) []string { - newRepos := []string{currentRepo} - for _, repo := range recentRepos { - if repo != currentRepo { - newRepos = append(newRepos, repo) - } - } - return newRepos -} - -// updateRecentRepoList registers the fact that we opened lazygit in this repo, -// so that we can open the same repo via a 'recent repos' menu -func (gui *Gui) updateRecentRepoList() error { - recentRepos := gui.Config.GetAppState().RecentRepos - currentRepo, err := os.Getwd() - if err != nil { - return err - } - gui.Config.GetAppState().RecentRepos = newRecentReposList(recentRepos, currentRepo) - return gui.Config.SaveAppState() -} - -func (gui *Gui) handleSwitchRepo(g *gocui.Gui, v *gocui.View) error { - newRepo := gui.Config.GetAppState().RecentRepos[1] - if err := os.Chdir(newRepo); err != nil { - return err - } - newGitCommand, err := commands.NewGitCommand(gui.Log, gui.OSCommand, gui.Tr) - if err != nil { - return err - } - gui.GitCommand = newGitCommand - return gui.Errors.ErrSwitchRepo -} - func (gui *Gui) promptAnonymousReporting() error { return gui.createConfirmationPanel(gui.g, nil, gui.Tr.SLocalize("AnonymousReportingTitle"), gui.Tr.SLocalize("AnonymousReportingPrompt"), func(g *gocui.Gui, v *gocui.View) error { return gui.Config.WriteToUserConfig("reporting", "on") diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index 73f4aabe5..1073da68a 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -116,7 +116,7 @@ func (gui *Gui) GetKeybindings() []*Binding { ViewName: "status", Key: 's', Modifier: gocui.ModNone, - Handler: gui.handleSwitchRepo, + Handler: gui.handleCreateRecentReposMenu, Description: gui.Tr.SLocalize("SwitchRepo"), }, { diff --git a/pkg/gui/options_menu_panel.go b/pkg/gui/options_menu_panel.go index 2da002b43..ac01ad03d 100644 --- a/pkg/gui/options_menu_panel.go +++ b/pkg/gui/options_menu_panel.go @@ -33,11 +33,11 @@ func (gui *Gui) getBindings(v *gocui.View) []*Binding { func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error { bindings := gui.getBindings(v) - handleOptionsMenuPress := func(index int) error { + handleMenuPress := func(index int) error { if bindings[index].Key == nil { return nil } - if index <= len(bindings) { + if index >= len(bindings) { return errors.New("Index is greater than size of bindings") } err := gui.handleMenuClose(g, v) @@ -47,5 +47,5 @@ func (gui *Gui) handleCreateOptionsMenu(g *gocui.Gui, v *gocui.View) error { return bindings[index].Handler(g, v) } - return gui.createMenu(bindings, handleOptionsMenuPress) + return gui.createMenu(bindings, handleMenuPress) } diff --git a/pkg/gui/recent_repos_panel.go b/pkg/gui/recent_repos_panel.go new file mode 100644 index 000000000..add987b32 --- /dev/null +++ b/pkg/gui/recent_repos_panel.go @@ -0,0 +1,69 @@ +package gui + +import ( + "os" + "path/filepath" + + "github.com/fatih/color" + "github.com/jesseduffield/gocui" + "github.com/jesseduffield/lazygit/pkg/commands" + "github.com/jesseduffield/lazygit/pkg/utils" +) + +type recentRepo struct { + path string +} + +func (r *recentRepo) GetDisplayStrings() []string { + yellow := color.New(color.FgMagenta) + base := filepath.Base(r.path) + path := yellow.Sprint(r.path) + return []string{base, path} +} + +func (gui *Gui) handleCreateRecentReposMenu(g *gocui.Gui, v *gocui.View) error { + recentRepoPaths := gui.Config.GetAppState().RecentRepos + reposCount := utils.Min(len(recentRepoPaths), 20) + // we won't show the current repo hence the -1 + recentRepos := make([]*recentRepo, reposCount-1) + for i, path := range recentRepoPaths[1:reposCount] { + recentRepos[i] = &recentRepo{path: path} + } + + handleMenuPress := func(index int) error { + repo := recentRepos[index] + if err := os.Chdir(repo.path); err != nil { + return err + } + newGitCommand, err := commands.NewGitCommand(gui.Log, gui.OSCommand, gui.Tr) + if err != nil { + return err + } + gui.GitCommand = newGitCommand + return gui.Errors.ErrSwitchRepo + } + + return gui.createMenu(recentRepos, handleMenuPress) +} + +// updateRecentRepoList registers the fact that we opened lazygit in this repo, +// so that we can open the same repo via the 'recent repos' menu +func (gui *Gui) updateRecentRepoList() error { + recentRepos := gui.Config.GetAppState().RecentRepos + currentRepo, err := os.Getwd() + if err != nil { + return err + } + gui.Config.GetAppState().RecentRepos = newRecentReposList(recentRepos, currentRepo) + return gui.Config.SaveAppState() +} + +func newRecentReposList(recentRepos []string, currentRepo string) []string { + newRepos := []string{currentRepo} + for _, repo := range recentRepos { + if repo != currentRepo { + newRepos = append(newRepos, repo) + } + } + return newRepos +} diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 5843b5760..b8be4cfc6 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -392,7 +392,7 @@ func addEnglish(i18nObject *i18n.Bundle) error { Other: `Are you sure you want to quit?`, }, &i18n.Message{ ID: "SwitchRepo", - Other: `Switch to a recent repo`, + Other: `switch to a recent repo`, }, ) } From fcaf4e339c4e76aafb9e89114a7eabe1bcbe0362 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 19:16:55 +1000 Subject: [PATCH 22/25] fix specs --- pkg/commands/git.go | 4 ++-- pkg/commands/git_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 2a127d407..c37ca9b47 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -231,9 +231,9 @@ func (c *GitCommand) UpstreamDifferenceCount() (string, string) { return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount) } -// getCommitsToPush Returns the sha's of the commits that have not yet been pushed +// GetCommitsToPush Returns the sha's of the commits that have not yet been pushed // to the remote branch of the current branch, a map is returned to ease look up -func (c *GitCommand) getCommitsToPush() map[string]bool { +func (c *GitCommand) GetCommitsToPush() map[string]bool { pushables := map[string]bool{} o, err := c.OSCommand.RunCommandWithOutput("git rev-list @{u}..head --abbrev-commit") if err != nil { diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 6d19a1629..6e3b81bf5 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -631,7 +631,7 @@ func TestGitCommandGetCommitsToPush(t *testing.T) { t.Run(s.testName, func(t *testing.T) { gitCmd := newDummyGitCommand() gitCmd.OSCommand.command = s.command - s.test(gitCmd.getCommitsToPush()) + s.test(gitCmd.GetCommitsToPush()) }) } } From 64f0eeb42e8c19251f0ca823e43c62468066474c Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 19:23:31 +1000 Subject: [PATCH 23/25] fix specs --- pkg/commands/git_test.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/commands/git_test.go b/pkg/commands/git_test.go index 6e3b81bf5..790a34690 100644 --- a/pkg/commands/git_test.go +++ b/pkg/commands/git_test.go @@ -1225,7 +1225,7 @@ func TestGitCommandRemoveFile(t *testing.T) { testName string command func() (func(string, ...string) *exec.Cmd, *[][]string) test func(*[][]string, error) - file File + file *File removeFile func(string) error } @@ -1247,7 +1247,7 @@ func TestGitCommandRemoveFile(t *testing.T) { {"reset", "--", "test"}, }) }, - File{ + &File{ Name: "test", HasStagedChanges: true, }, @@ -1270,7 +1270,7 @@ func TestGitCommandRemoveFile(t *testing.T) { assert.EqualError(t, err, "an error occurred when removing file") assert.Len(t, *cmdsCalled, 0) }, - File{ + &File{ Name: "test", Tracked: false, }, @@ -1295,7 +1295,7 @@ func TestGitCommandRemoveFile(t *testing.T) { {"checkout", "--", "test"}, }) }, - File{ + &File{ Name: "test", Tracked: true, HasStagedChanges: false, @@ -1321,7 +1321,7 @@ func TestGitCommandRemoveFile(t *testing.T) { {"checkout", "--", "test"}, }) }, - File{ + &File{ Name: "test", Tracked: true, HasStagedChanges: false, @@ -1348,7 +1348,7 @@ func TestGitCommandRemoveFile(t *testing.T) { {"checkout", "--", "test"}, }) }, - File{ + &File{ Name: "test", Tracked: true, HasStagedChanges: true, @@ -1374,7 +1374,7 @@ func TestGitCommandRemoveFile(t *testing.T) { {"reset", "--", "test"}, }) }, - File{ + &File{ Name: "test", Tracked: false, HasStagedChanges: true, @@ -1398,7 +1398,7 @@ func TestGitCommandRemoveFile(t *testing.T) { assert.NoError(t, err) assert.Len(t, *cmdsCalled, 0) }, - File{ + &File{ Name: "test", Tracked: false, HasStagedChanges: false, @@ -1484,7 +1484,7 @@ func TestGitCommandGetCommits(t *testing.T) { type scenario struct { testName string command func(string, ...string) *exec.Cmd - test func([]Commit) + test func([]*Commit) } scenarios := []scenario{ @@ -1504,7 +1504,7 @@ func TestGitCommandGetCommits(t *testing.T) { return nil }, - func(commits []Commit) { + func(commits []*Commit) { assert.Len(t, commits, 0) }, }, @@ -1524,9 +1524,9 @@ func TestGitCommandGetCommits(t *testing.T) { return nil }, - func(commits []Commit) { + func(commits []*Commit) { assert.Len(t, commits, 2) - assert.EqualValues(t, []Commit{ + assert.EqualValues(t, []*Commit{ { Sha: "8a2bb0e", Name: "commit 1", From 5a76b579528bb90b5aff388f8aa68594c22a03c3 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 19:31:29 +1000 Subject: [PATCH 24/25] one more spec to increase coverage --- pkg/utils/utils_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 76a9f8b95..918031512 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -346,3 +346,33 @@ func TestGetPadWidths(t *testing.T) { assert.EqualValues(t, s.expected, getPadWidths(s.stringArrays)) } } + +func TestMin(t *testing.T) { + type scenario struct { + a int + b int + expected int + } + + scenarios := []scenario{ + { + 1, + 1, + 1, + }, + { + 1, + 2, + 1, + }, + { + 2, + 1, + 1, + }, + } + + for _, s := range scenarios { + assert.EqualValues(t, s.expected, Min(s.a, s.b)) + } +} From 0367399cf3368c6e05b9187ac8a9c994eaf2fc9c Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Wed, 19 Sep 2018 19:54:58 +1000 Subject: [PATCH 25/25] bump deps to use forked termbox which doesn't crash as easily --- Gopkg.lock | 20 +++---- go.mod | 4 +- go.sum | 54 ++----------------- .../jesseduffield/gocui/attribute.go | 2 +- vendor/github.com/jesseduffield/gocui/gui.go | 2 +- .../jesseduffield/gocui/keybinding.go | 2 +- vendor/github.com/jesseduffield/gocui/view.go | 6 ++- .../{nsf => jesseduffield}/termbox-go/AUTHORS | 0 .../{nsf => jesseduffield}/termbox-go/LICENSE | 0 .../{nsf => jesseduffield}/termbox-go/api.go | 9 ++++ .../termbox-go/api_common.go | 0 .../termbox-go/api_windows.go | 0 .../termbox-go/escwait.go | 0 .../termbox-go/escwait_darwin.go | 0 .../termbox-go/syscalls.go | 0 .../termbox-go/syscalls_darwin.go | 0 .../termbox-go/syscalls_darwin_amd64.go | 0 .../termbox-go/syscalls_dragonfly.go | 0 .../termbox-go/syscalls_freebsd.go | 0 .../termbox-go/syscalls_linux.go | 0 .../termbox-go/syscalls_netbsd.go | 0 .../termbox-go/syscalls_openbsd.go | 0 .../termbox-go/syscalls_windows.go | 0 .../termbox-go/termbox.go | 0 .../termbox-go/termbox_common.go | 0 .../termbox-go/termbox_windows.go | 0 .../termbox-go/terminfo.go | 0 .../termbox-go/terminfo_builtin.go | 0 28 files changed, 34 insertions(+), 65 deletions(-) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/AUTHORS (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/LICENSE (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/api.go (98%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/api_common.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/api_windows.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/escwait.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/escwait_darwin.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_darwin.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_darwin_amd64.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_dragonfly.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_freebsd.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_linux.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_netbsd.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_openbsd.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/syscalls_windows.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/termbox.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/termbox_common.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/termbox_windows.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/terminfo.go (100%) rename vendor/github.com/{nsf => jesseduffield}/termbox-go/terminfo_builtin.go (100%) diff --git a/Gopkg.lock b/Gopkg.lock index 2bbaa2118..540b26052 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -189,11 +189,19 @@ [[projects]] branch = "master" - digest = "1:71e6c15797951d3fabaa944d70253e36a6cee96bf54ca0bc43ca3de3b4814bbb" + digest = "1:d6df25dee1274e63c25f84c257bc504359f975dedb791eec7a5b51992146bb76" name = "github.com/jesseduffield/gocui" packages = ["."] pruneopts = "NUT" - revision = "2cb6e95bbbf850bb32cc1799e07d08ff0f144746" + revision = "4fca348422d8b6136e801b222858204a35ee369a" + +[[projects]] + branch = "master" + digest = "1:3ab130f65766f5b7cc944d557df31c6a007ec017151705ec1e1b8719f2689021" + name = "github.com/jesseduffield/termbox-go" + packages = ["."] + pruneopts = "NUT" + revision = "1e272ff78dcb4c448870f464fda1cdcf2bf0b3dd" [[projects]] digest = "1:ac6d01547ec4f7f673311b4663909269bfb8249952de3279799289467837c3cc" @@ -294,14 +302,6 @@ revision = "a16b91a3ba80db3a2301c70d1d302d42251c9079" version = "v2.0.0-beta.5" -[[projects]] - branch = "master" - digest = "1:34d9354c2c5d916c05864327553047df59fc10e86ff1f408e4136eba0a25a5ec" - name = "github.com/nsf/termbox-go" - packages = ["."] - pruneopts = "NUT" - revision = "5c94acc5e6eb520f1bcd183974e01171cc4c23b3" - [[projects]] digest = "1:cf254277d898b713195cc6b4a3fac8bf738b9f1121625df27843b52b267eec6c" name = "github.com/pelletier/go-buffruneio" diff --git a/go.mod b/go.mod index 02db8a03e..61692f46e 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,8 @@ require ( github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 - github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8 + github.com/jesseduffield/gocui v0.0.0-20180919095827-4fca348422d8 + github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 @@ -31,7 +32,6 @@ require ( github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80 - github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb github.com/pelletier/go-buffruneio v0.2.0 github.com/pelletier/go-toml v1.2.0 github.com/pkg/errors v0.8.0 diff --git a/go.sum b/go.sum index e1d3249fa..1506becb9 100644 --- a/go.sum +++ b/go.sum @@ -1,38 +1,22 @@ -github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY= github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/aws/aws-sdk-go v1.15.21 h1:STLvc6RrpycslC1NRtTvt/YSgDkIGCTrB9K9vE5R2oQ= github.com/aws/aws-sdk-go v1.15.21/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do= github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo= github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/gliderlabs/ssh v0.1.1 h1:j3L6gSLQalDETeEg/Jg0mGY0/y/N6zI2xX1978P0Uqw= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ini/ini v1.38.2 h1:6Hl/z3p3iFkA0dlDfzYxuFuUGD+kaweypF6btsR2/Q4= github.com/go-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSfgRJKqIzapE78onvvTbdi1rMkU00lZw= -github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186 h1:URgjUo+bs1KwatoNbwG0uCO4dHN4r1jsp4a5AGgHRjo= github.com/hashicorp/go-cleanhttp v0.0.0-20171218145408-d5fe4b57a186/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-getter v0.0.0-20180809191950-4bda8fa99001 h1:MFPzqpPED05pFyGjNPJEC2sXM6EHTzFyvX+0s0JoZ48= @@ -45,18 +29,16 @@ github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGE github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331 h1:qio0y/sQdhbHRA3cmgczo04MaSV2zw+n46G1owvgWIk= github.com/heroku/rollrus v0.0.0-20180515183152-fc0cef2ff331/go.mod h1:BT+PgT529opmb6mcUY+Fg0IwVRRmwqFyavEMU17GnBg= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63 h1:Nrr/yUxNjXWYK0B3IqcFlYh1ICnesJDB4ogcfOVc5Ns= github.com/jesseduffield/go-getter v0.0.0-20180822080847-906e15686e63/go.mod h1:fNqjRf+4XnTo2PrGN1JRb79b/BeoHwP4lU00f39SQY0= -github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8 h1:YMYnpIu5HUJfx/yfIwnZhFrgWTg51FQWtvZi+PMzQm8= -github.com/jesseduffield/gocui v0.0.0-20180905104005-2cb6e95bbbf8/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= +github.com/jesseduffield/gocui v0.0.0-20180919095827-4fca348422d8 h1:XxX+IqNOFDh1PnU4eZDzUomoKbuKCvwyEm5an/IxLQU= +github.com/jesseduffield/gocui v0.0.0-20180919095827-4fca348422d8/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw= +github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb h1:cFHYEWpQEfzFZVKiKZytCUX4UwQixKSw0kd3WhluPsY= +github.com/jesseduffield/termbox-go v0.0.0-20180919093808-1e272ff78dcb/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1 h1:PJPDf8OUfOK1bb/NeTKd4f1QXZItOX389VN3B6qC8ro= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kevinburke/ssh_config v0.0.0-20180317175531-9fc7bb800b55 h1:S38dC4mEwxdw/U41+97VWdbun8mTcTjwg5Ujfg8QPME= @@ -77,22 +59,16 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1: github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI= github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80 h1:7ory6RlsEkeK89iyV7Imz3sVz8YHeSw29w3PehpCWC0= github.com/nicksnyder/go-i18n v0.0.0-20180803040939-a16b91a3ba80/go.mod h1:e4Di5xjP9oTVrC6y3C7C0HoSYXjSbhh/dU0eUV32nB4= github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 h1:/TjjTS4kg7vC+05gD0LE4+97f/+PRFICnK/7wJPk7kE= github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU= -github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb h1:YahEjAGkJtCrkqgVHhX6n8ZX+CZ3hDRL9fjLYugLfSs= -github.com/nsf/termbox-go v0.0.0-20180613055208-5c94acc5e6eb/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= -github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.1 h1:PZSj/UFNaVp3KxrzHOcS7oyuWA7LoOY/77yCTEFu21U= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -100,10 +76,6 @@ github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0 h1:Xuk8ma/ibJ1 github.com/shibukawa/configdir v0.0.0-20170330084843-e180dbdc8da0/go.mod h1:7AwjWCpdPhkSmNAgUv5C7EJ4AbmjEB3r047r3DXWu3Y= github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf h1:6V1qxN6Usn4jy8unvggSJz/NC790tefw8Zdy6OZS5co= -github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= -github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I= github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= @@ -118,7 +90,6 @@ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJ github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/src-d/gcfg v1.3.0 h1:2BEDr8r0I0b8h/fOqwtxCEiq2HJu8n2JGZJQFGXWLjg= github.com/src-d/gcfg v1.3.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea h1:jysxIKov/4GJ33wI2aRvuIK7yBwB28E5almlgDLPRpM= github.com/stvp/roll v0.0.0-20170522205222-3627a5cbeaea/go.mod h1:Ffmqrj3nXIMIjeA4uW3Qjj0Ud9eDoTG0fu4JxyAr/tE= @@ -132,31 +103,16 @@ golang.org/x/crypto v0.0.0-20180808211826-de0752318171 h1:vYogbvSFj2YXcjQxFHu/rA golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0 h1:8H8QZJ30plJyIVj60H3lr8TZGIq2Fh3Cyrs/ZNg1foU= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/ini.v1 v1.38.2 h1:dGcbywv4RufeGeiMycPT/plKB5FtmLKLnWKwBiLhUA4= -gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/src-d/go-billy.v4 v4.2.0 h1:VGbrP1EsYxtvVPEiHui+4//imr4E5MGEFLx66bQtusg= gopkg.in/src-d/go-billy.v4 v4.2.0/go.mod h1:ZHSF0JP+7oD97194otDUCD7Ofbk63+xFcfWP5bT6h+Q= -gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs= -gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714 h1:+wM2BGgQ1znCKBexOB4OrGVSDw8mtKNUSq3wqxZhi/k= gopkg.in/src-d/go-git.v4 v4.0.0-20180807092216-43d17e14b714/go.mod h1:CzbUWqMn4pvmvndg3gnh5iZFmSsbhyhUWdI0IQ60AQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= diff --git a/vendor/github.com/jesseduffield/gocui/attribute.go b/vendor/github.com/jesseduffield/gocui/attribute.go index bad758a1d..5b46b1427 100644 --- a/vendor/github.com/jesseduffield/gocui/attribute.go +++ b/vendor/github.com/jesseduffield/gocui/attribute.go @@ -4,7 +4,7 @@ package gocui -import "github.com/nsf/termbox-go" +import "github.com/jesseduffield/termbox-go" // Attribute represents a terminal attribute, like color, font style, etc. They // can be combined using bitwise OR (|). Note that it is not possible to diff --git a/vendor/github.com/jesseduffield/gocui/gui.go b/vendor/github.com/jesseduffield/gocui/gui.go index 4393c06c4..cb163e06d 100644 --- a/vendor/github.com/jesseduffield/gocui/gui.go +++ b/vendor/github.com/jesseduffield/gocui/gui.go @@ -7,7 +7,7 @@ package gocui import ( "errors" - "github.com/nsf/termbox-go" + "github.com/jesseduffield/termbox-go" ) var ( diff --git a/vendor/github.com/jesseduffield/gocui/keybinding.go b/vendor/github.com/jesseduffield/gocui/keybinding.go index 82d1acc9f..65d9ec6cf 100644 --- a/vendor/github.com/jesseduffield/gocui/keybinding.go +++ b/vendor/github.com/jesseduffield/gocui/keybinding.go @@ -4,7 +4,7 @@ package gocui -import "github.com/nsf/termbox-go" +import "github.com/jesseduffield/termbox-go" // Keybidings are used to link a given key-press event with a handler. type keybinding struct { diff --git a/vendor/github.com/jesseduffield/gocui/view.go b/vendor/github.com/jesseduffield/gocui/view.go index 53ab06f8c..46b86ec2a 100644 --- a/vendor/github.com/jesseduffield/gocui/view.go +++ b/vendor/github.com/jesseduffield/gocui/view.go @@ -10,8 +10,8 @@ import ( "io" "strings" + "github.com/jesseduffield/termbox-go" "github.com/mattn/go-runewidth" - "github.com/nsf/termbox-go" ) // Constants for overlapping edges @@ -447,6 +447,10 @@ func (v *View) ViewBufferLines() []string { return lines } +func (v *View) ViewLinesHeight() int { + return len(v.viewLines) +} + // ViewBuffer returns a string with the contents of the view's buffer that is // shown to the user. func (v *View) ViewBuffer() string { diff --git a/vendor/github.com/nsf/termbox-go/AUTHORS b/vendor/github.com/jesseduffield/termbox-go/AUTHORS similarity index 100% rename from vendor/github.com/nsf/termbox-go/AUTHORS rename to vendor/github.com/jesseduffield/termbox-go/AUTHORS diff --git a/vendor/github.com/nsf/termbox-go/LICENSE b/vendor/github.com/jesseduffield/termbox-go/LICENSE similarity index 100% rename from vendor/github.com/nsf/termbox-go/LICENSE rename to vendor/github.com/jesseduffield/termbox-go/LICENSE diff --git a/vendor/github.com/nsf/termbox-go/api.go b/vendor/github.com/jesseduffield/termbox-go/api.go similarity index 98% rename from vendor/github.com/nsf/termbox-go/api.go rename to vendor/github.com/jesseduffield/termbox-go/api.go index d530ab5c5..4c828e482 100644 --- a/vendor/github.com/nsf/termbox-go/api.go +++ b/vendor/github.com/jesseduffield/termbox-go/api.go @@ -317,6 +317,9 @@ func PollEvent() Event { event.Type = EventKey status := extract_event(inbuf, &event, true) if event.N != 0 { + if event.N > len(inbuf) { + event.N = len(inbuf) + } copy(inbuf, inbuf[event.N:]) inbuf = inbuf[:len(inbuf)-event.N] } @@ -345,6 +348,9 @@ func PollEvent() Event { input_comm <- ev status := extract_event(inbuf, &event, true) if event.N != 0 { + if event.N > len(inbuf) { + event.N = len(inbuf) + } copy(inbuf, inbuf[event.N:]) inbuf = inbuf[:len(inbuf)-event.N] } @@ -359,6 +365,9 @@ func PollEvent() Event { status := extract_event(inbuf, &event, false) if event.N != 0 { + if event.N > len(inbuf) { + event.N = len(inbuf) + } copy(inbuf, inbuf[event.N:]) inbuf = inbuf[:len(inbuf)-event.N] } diff --git a/vendor/github.com/nsf/termbox-go/api_common.go b/vendor/github.com/jesseduffield/termbox-go/api_common.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/api_common.go rename to vendor/github.com/jesseduffield/termbox-go/api_common.go diff --git a/vendor/github.com/nsf/termbox-go/api_windows.go b/vendor/github.com/jesseduffield/termbox-go/api_windows.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/api_windows.go rename to vendor/github.com/jesseduffield/termbox-go/api_windows.go diff --git a/vendor/github.com/nsf/termbox-go/escwait.go b/vendor/github.com/jesseduffield/termbox-go/escwait.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/escwait.go rename to vendor/github.com/jesseduffield/termbox-go/escwait.go diff --git a/vendor/github.com/nsf/termbox-go/escwait_darwin.go b/vendor/github.com/jesseduffield/termbox-go/escwait_darwin.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/escwait_darwin.go rename to vendor/github.com/jesseduffield/termbox-go/escwait_darwin.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls.go b/vendor/github.com/jesseduffield/termbox-go/syscalls.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_darwin.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_darwin.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_darwin.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_darwin.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_darwin_amd64.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_darwin_amd64.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_darwin_amd64.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_dragonfly.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_dragonfly.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_dragonfly.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_freebsd.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_freebsd.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_freebsd.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_freebsd.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_linux.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_linux.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_linux.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_linux.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_netbsd.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_netbsd.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_netbsd.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_netbsd.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_openbsd.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_openbsd.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_openbsd.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_openbsd.go diff --git a/vendor/github.com/nsf/termbox-go/syscalls_windows.go b/vendor/github.com/jesseduffield/termbox-go/syscalls_windows.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/syscalls_windows.go rename to vendor/github.com/jesseduffield/termbox-go/syscalls_windows.go diff --git a/vendor/github.com/nsf/termbox-go/termbox.go b/vendor/github.com/jesseduffield/termbox-go/termbox.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/termbox.go rename to vendor/github.com/jesseduffield/termbox-go/termbox.go diff --git a/vendor/github.com/nsf/termbox-go/termbox_common.go b/vendor/github.com/jesseduffield/termbox-go/termbox_common.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/termbox_common.go rename to vendor/github.com/jesseduffield/termbox-go/termbox_common.go diff --git a/vendor/github.com/nsf/termbox-go/termbox_windows.go b/vendor/github.com/jesseduffield/termbox-go/termbox_windows.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/termbox_windows.go rename to vendor/github.com/jesseduffield/termbox-go/termbox_windows.go diff --git a/vendor/github.com/nsf/termbox-go/terminfo.go b/vendor/github.com/jesseduffield/termbox-go/terminfo.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/terminfo.go rename to vendor/github.com/jesseduffield/termbox-go/terminfo.go diff --git a/vendor/github.com/nsf/termbox-go/terminfo_builtin.go b/vendor/github.com/jesseduffield/termbox-go/terminfo_builtin.go similarity index 100% rename from vendor/github.com/nsf/termbox-go/terminfo_builtin.go rename to vendor/github.com/jesseduffield/termbox-go/terminfo_builtin.go